Skip to content

Add Crowdsale Contracts

Here we do two things:

  1. We adapt the old Crowdsale Smart Contract from Open-Zeppelin to be Solidity 0.6 compliant

  2. We write out own Crowdsale on Top of it

Note

With OpenZeppelin approaching Solidity 0.6 the Crowdsale contracts were removed. Some people are inclined to add a "mintToken" functionality or something like that to the Token Smart Contract itself, but that would be bad design. We should add a separate Crowdsale Contract that handles token distribution.

Let's modify the Crowdsale Contract from Open-Zeppelin 2.5 to be available for Solidity 0.6:

Copy the contents from this file https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.5.0/contracts/crowdsale/Crowdsale.sol to /contracts/Crowdsale.sol and change a few lines to make it project compliant.

  1. The pragma line and the import statements

  2. The fallback function

  3. The virtual Keyword

The Pragma line and the Import Statements

If we copy the smart contract out of another repository instead of just using it, then we have to adjust the import statements. Replace the existing ones with this:

pragma solidity ^0.6.0;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/GSN/Context.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";

The Fallback Function for Solidity 0.6

The unnamed fallback function is gone. We need to replace the function() with a receive function, since it will be possible to send Ether directly to our smart contract without really interacting with it.

Replace the "fallback () " function with this one:

 receive () external payable {
        buyTokens(_msgSender());
    }

The Virtual Keyword in Solidity 0.6

If you want to override functions in Solidity 0.6 then the base smart-contract must define all functions as virtual to be overwritten. In the Crowdsale we must add the virtual keyword to functions that are potentially overwritten:

    function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view virtual {
        require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address");
        require(weiAmount != 0, "Crowdsale: weiAmount is 0");
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    }

    function _postValidatePurchase(address beneficiary, uint256 weiAmount) internal view virtual {
        // solhint-disable-previous-line no-empty-blocks
    }

    function _deliverTokens(address beneficiary, uint256 tokenAmount) internal virtual {
        _token.safeTransfer(beneficiary, tokenAmount);
    }

    function _processPurchase(address beneficiary, uint256 tokenAmount) internal virtual {
        _deliverTokens(beneficiary, tokenAmount);
    }

    function _updatePurchasingState(address beneficiary, uint256 weiAmount) internal virtual {
        // solhint-disable-previous-line no-empty-blocks
    }

    function _getTokenAmount(uint256 weiAmount) internal view virtual returns (uint256) {
        return weiAmount.mul(_rate);
    }

Create your own Crowdsale Contract

Add in a contracts/MyTokenSale.sol file with the following content:

pragma solidity ^0.6.0;

import "./Crowdsale.sol";

contract MyTokenSale is Crowdsale {

    KycContract kyc;
    constructor(
        uint256 rate,    // rate in TKNbits
        address payable wallet,
        IERC20 token
    )
        Crowdsale(rate, wallet, token)
        public
    {

    }

}

Adopt the Migration for the Crowdsale Contract

In order for our crowdsale smart contract to work, we must send all the money to the contract. This is done on the migrations stage in our truffle installation:

The problem is now that the Test is failing. Let's change the standard Truffle-Test-Suite to the openzeppelin test suite:

var MyToken = artifacts.require("./MyToken.sol");
var MyTokenSales = artifacts.require("./MyTokenSale.sol");

module.exports = async function(deployer) {
let addr = await web3.eth.getAccounts();
await deployer.deploy(MyToken, 1000000000);
await deployer.deploy(MyTokenSales, 1, addr[0], MyToken.address);
let tokenInstance = await MyToken.deployed();
await tokenInstance.transfer(MyTokenSales.address, 1000000000);

};

Perfect, now that we have that covered, let's have a look at the unit tests again.


Last update: April 29, 2022