How to Modify Crowdsale & MintedCrowdsale Solidity Contracts to Work With OpenZeppelin 4.0

By Bobby Gill on February 10, 2022

Like many other Ethereum developers, the developers at Blue Label Labs‘ fledgling Ethereum development division have come to rely on OpenZeppelin extensively in most of our ERC-20  and ERC-721 development.

OpenZeppelin provides a suite of extensible Solidity contracts that implement all of the ERC-20/721 contract specifications, allowing developers to focus specifically on the parts of their contracts which make them unique.

OpenZeppelin continually releases new versions of their templates, with each new version bringing new functionality with it.

Source: Ethereum in Depth, Part 2

However, sometimes these new releases of OpenZeppelin also take things way.

In particular, in earlier versions of OpenZeppelin (before 3.0), they provided contract templates that enabled many different ICO scenarios, one being the minted crowdsale.

But, in the 3.0 release for OpenZeppelin, the team decided to remove all of these crowdsale focused contracts and focus instead on the pure implementation of the ERC standards themselves.

For developers looking to run an ICO, the removal of these ICO contracts means that developers have to choose between using the newest versions of OpenZeppelin and having to write their own crowdsale contracts from scratch versus being relegated to an old version of OpenZeppelin where those contracts are available at the expense of not being able to use the newest versions of OpenZeppelin.

In this post I will show you how to modify the MintedCrowdsale.sol and Crowdsale.sol contracts from OpenZeppelin 2.x such that they can be compiled against and used with the latest set of OpenZeppelin contracts.

Let’s go!

Pre-requisites

  • Start a new Solidity project in your local development environment and install the latest version of the OpenZeppelin/Contracts NPM package (at the time of writing that is OpenZeppelin 4.4.2).
  • Download from Github an earlier release of the OpenZeppelin contracts, for our example we will use OpenZeppelin 2.5 which you can download here. Once downloaded, unzip the release and identify and copy to your project location the following files:
    • contracts/crowdsale/Crowdsale.sol
    • contract/emission/MintedCrowdsale.sol

Modifications to make to Crowdsale.sol

1.) Update Solidity compiler version and import statements

  • Change the pragma declaration at the top to use a newer version of the Solidity compiler that is compatible with OpenZeppelin 4.4.2, which for our example, is 0.8.4.
  • Modify the import statements at the top of Crowdsale.sol to reference the installed version of the OpenZeppelin package. The changes will look like this:
pragma solidity ^0.8.4;

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

2.) Modify the Crowdsale contract to be ‘abstract’

  • Add the “abstract” declaration to the contract declaration statement for Crowdsale.sol
abstract contract Crowdsale is Context, ReentrancyGuard
  • At the same time, remove the “public” keyword from the constructor of the Crowdsale contract. At this point, you will start to see errors that the “this declaration shadows an existing declaration”, simply rename the rate,wallet,token parameters of the constructor to something else. The constructor then will look like this:
constructor (uint256 r, address payable w, IERC20 t) {
    require(r > 0, "Crowdsale: rate is 0");
    require(w != address(0), "Crowdsale: wallet is the zero address");
    require(address(t) != address(0), "Crowdsale: token is the zero address");

    _rate = r;
    _wallet = w;
    _token = t;
}

3.) Replace the fallback function

  • Find the fallback function with the definition “function () external payable” and delete it.
  • In its place add this:
receive() external payable {
    buyTokens(_msgSender());
}

4.) Add “virtual” keyword to methods

  • Add the keyword “virtual” to the following methods in the contract:
    • buyTokens
    • _preValidatePurchase
    • _postValidatePurchase
    • _deliverTokens
    • _processPurchase
    • _updatePurchasingState
    • _getTokenAmount

That’s it, now that we have the base Crowdsale contract modified, let’s move onto the MintedCrowdsale contract which will inherit from this.

Modifications to make to MintedCrowdsale.sol

1.) Update Solidity compiler version and import statements

  • Like the Crowdsale.sol Contract, change the compiler version to ^0.8.4.
  • Modify the import statements to reference our modified version of the Crowdsale.sol Contract and the installed OpenZeppelin NPM package.
pragma solidity ^0.8.4;

import "./Crowdsale.sol";
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";

2.) Add a constructor to the MintedCrowdsale Contract

  • Add a new constructor which will simply call the Crowdsale.sol constructor with the same parameters.
 constructor(
    uint256 r,
    address payable w,
    IERC20 t
) Crowdsale(r,w,t) {
    
}

3.) Modify the _deliverTokens method to use ERC20PresentMinterPauser

  • The _deliveryTokens method on MintedCrowdsale.sol uses the ERC20Mintable Contract which is also no longer present in the newest versions of OpenZeppelin. Instead, modify it to use the new ERC20PresetMinterPauser Contract. Your method should then look like this:
function _deliverTokens(address beneficiary, uint256 tokenAmount) internal override {
        ERC20PresetMinterPauser(address(token())).mint(beneficiary, tokenAmount);

}

That’s it. At this point you now have the ability to leverage the MintedCrowdsale.sol and Crowdsale.sol alongside Contracts built against OpenZeppelin 4.0. While the changes I’ve made above apply to MintedCrowdsale.sol, you can use the same concepts to brings forward any of the other older Crowdsale contracts that were removed from OpenZeppelin.

I’ve created a Gist with a complete version of both files with the changes embedded below.

Blue Label Labs Is Hiring

We are looking for exceptional people to join our exceptional team and build great things together.

See Our Current Open Positions