Learning Openzeppelin

@Javier_Flores hey man good proposal. This actually doesnt work. If you go into the ERC20Capped contratc you will see that the _mint( ) function requires the cap is not exceeded and then it just calls the ERC20 _mint( ) function. So by calling ERC20._mint( ) you are excluding the effect of the cap requirment.

SOLUTION FOR THE ERC20Capped IMMUTABILITY ERROR****

For anyone experiencing the cap issue i have came up with a decent solution. Note that using ERC20._mint will not work. It will of course mint tokens but the supply cap will not be required since this is the extra functionality that the ERC20Capped brings so we will be ignoring it otherwise. So since the cap variable in the ERC20Capped contratc is immutable for good reasons aswell we cannot mint tokens in our constructor. The only reason the immutability error is getting thrown is because we are minting tokens on creation of our MyToken contract via the constructor. This is whats throwing the error.

The solution is to mint tokens after the contratc has been created via a function. So we simply just need to create a Mint( ) function that takes in an amount. The we just inherit from the ERC20Capped contract by calling the _mint( ) function. Here is my change to the code for anyone who is curious

mint

This prevents tokens from being minted on creation but that doesnt really matter anyway.

If you really wanted tokens to be minted on creation you could remove the immutable keyword on the supplyCap variable declaration in the ERC20Capped contratc. I originally thought that this may be unsafe but in Filips video (which is probably an older version) the supplyCap declaration does not hav ethis immutable Functionality. But to be safe maybe the latter solution is better

Shoutout to this post for informing me of the mint in constructor issue https://forum.openzeppelin.com/t/erc20capped-immutable-variables-cannot-be-read-during-contract-creation-time/6174/5 Hope this helps

8 Likes

It worked for me. In accordance to the exercise it solved my issue. I agree though, token shouldn’t be minted in constructor. Thanks for the insight.

:nerd_face:

3 Likes

@Javier_Flores - is there a solution for this error? It looks like a solution is in-progress and waiting for a solidity solution? https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2580

Nevermind just found the solution :slight_smile: https://forum.openzeppelin.com/t/erc20capped-immutable-variables-cannot-be-read-during-contract-creation-time/6174/5

2 Likes
  
// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

import "../node_modules/@openzeppelin/contracts/access/AccessControl.sol";
import "../node_modules/@openzeppelin/contracts/utils/Context.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";

/**
 * @dev {ERC20} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 */
contract MyToken is Context, AccessControl, ERC20Burnable, ERC20Pausable {
    using SafeMath for uint256;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAP_ROLE = keccak256("CAP_ROLE");

    uint256 private _cap;

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
    constructor(string memory name, string memory symbol, uint256 cap_) public ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAP_ROLE, _msgSender());

        //Set Cap
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

    /**
     * @dev Returns the cap on the token's total supply.
     */
    function cap() public view virtual returns (uint256) {
        return _cap;
    }

    /**
     * @dev Changes the cap on the token's total supply.
     */
    function setCap(uint256 cap_) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap role to set cap");
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

    /**
     * @dev Creates `amount` new tokens for `to`.
     *
     * See {ERC20-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        _mint(to, amount);
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20, ERC20Pausable) {
        super._beforeTokenTransfer(from, to, amount);

        if (from == address(0)) { // When minting tokens
            require(totalSupply().add(amount) <= cap(), "ERC20Capped: cap exceeded");
        }
    }
}

What is my error saying here? i don't understand..

I have a question: The lesson instructions say to start with the source from ERC20PresetMinterPauser, but it makes more sense to inherit from it and add the capped functionality to MyToken. Is my solution acceptable?

pragma solidity 0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
//import "../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";
import "../node_modules/@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20PresetMinterPauser, Ownable {
    uint256 private _cap;
    bytes32 public constant MODIFYCAP_ROLE = keccak256("MODIFYCAP_ROLE");

    constructor() ERC20PresetMinterPauser("MyToken", "MTKN") {
        _cap = 100000;
        _setupRole(MODIFYCAP_ROLE, _msgSender());
        mint(msg.sender, 1000);
    }

    function setCap(uint256 newCap) public returns (uint256) {
        require(hasRole(MODIFYCAP_ROLE, _msgSender()), "Sender missing MODIFYCAP_ROLE");
        require(newCap > 0, "Zero cap not allowed");
        _cap = newCap;
        return _cap;
    }

    // ERC20Capped functionality - copied from ../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol

    /**
     * @dev Returns the cap on the token's total supply.
     */
    function cap() public view virtual returns (uint256) {
        return _cap;
    }

    /**
     * @dev See {ERC20-_mint}.
     */
    function mint(address account, uint256 amount) public virtual override {
        require(ERC20.totalSupply() + amount <= cap(), "MyToken: cap exceeded");
        super.mint(account, amount);
    }
}

2 Likes

Hey @walter_w. You dont have to start with the ERC20PreserMinterPauser contract at all if you dont want to. You could code the whole thing yourself if you really wanted. The purpose of these contratcs is to save you the hassle. You can also gaurantee security because the openzepellin library is constant being worked on by pioneers in the space.

So i guess this wont serve as an exact answer to your question but you can approach it how you like. I think when i actually did this part i too just used ERC20 and Capped and did not use the minter pauser. I remember doing thus because i wanted to learn more how the functions worked like the pause function and pauser authority etc. you should maybe try this if your curious, but by inheiriting from ERC20PresetMinerPauser, you automaticlly get things like control over the minter /pauser authroity.

However your solution here looks fine to me.

Hi, I’m having a little trouble in the lesson Extending ERC20

It’s not compiling well, then I found that my file ERC20Capped is different, it doest’t have function _beforeTokenTransfer

image

pragma solidity ^0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";

contract MyToken is ERC20Capped {
    constructor () ERC20("MyToken", "MTKN") ERC20Capped(100000){
        _mint(msg.sender, 1000);
        
    }
    
}

Hi @jad

The path to ERC20Capped.sol shown in the console is not correct.
Make sure to fix the path in your contract and save it, you should be able to migrate your project correctly.

Cheers,
Dani

Please have a look at this code, this is for the Assignment to add Capped functionality to contract. I’m getting an error please see below,

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "../node_modules/@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "../node_modules/@openzeppelin/contracts/utils/Context.sol";

/**
 * @dev {ERC20} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 */
contract KamToken is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAP_ROLE = keccak256("CAP_ROLE");
    
    uint256 immutable private _cap;

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
    constructor(string memory name, string memory symbol, uint256 cap_) ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAP_ROLE, _msgSender());

        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

    

    /**
     * @dev Returns the cap on the token's total supply.
     */
    function cap() public view virtual returns (uint256) {
        return _cap;
    }

    function setCap(uint256 cap_) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap role to set cap");
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

    /**
     * @dev See {ERC20-_mint}.
     */
    function _mint(address account, uint256 amount) internal virtual override {
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        super._mint(account, amount);
    }

    /**
     * @dev Creates `amount` new tokens for `to`.
     *
     * See {ERC20-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        _mint(to, amount);
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20, ERC20Pausable) {
        super._beforeTokenTransfer(from, to, amount);
    }
}

and that’s the error

C:\Users\kamil\Documents\Ethereum-201\ERC20PresetMinterPauser>truffle compile

Compiling your contracts...
===========================
> Compiling .\contracts\ERC20PresetMinterPauser.sol

TypeError: Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor.
  --> /C/Users/kamil/Documents/Ethereum-201/ERC20PresetMinterPauser/contracts/ERC20PresetMinterPauser.sol:61:9:     
   |
61 |         _cap = cap_;
   |         ^^^^

Compilation failed. See above.
Truffle v5.3.7 (core: 5.3.7)
Node v16.2.0

C:\Users\kamil\Documents\Ethereum-201\ERC20PresetMinterPauser>

also this is the new capped file that is different to what Filip has and haven’t got _beforeTokenTransfer function please see if what I did with the above would work

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../ERC20.sol";

/**

 * @dev Extension of {ERC20} that adds a cap to the supply of tokens.

 */

abstract contract ERC20Capped is ERC20 {

    uint256 immutable private _cap;

    /**

     * @dev Sets the value of the `cap`. This value is immutable, it can only be

     * set once during construction.

     */

    constructor (uint256 cap_) {

        require(cap_ > 0, "ERC20Capped: cap is 0");

        _cap = cap_;

    }

    /**

     * @dev Returns the cap on the token's total supply.

     */

    function cap() public view virtual returns (uint256) {

        return _cap;

    }

    /**

     * @dev See {ERC20-_mint}.

     */

    function _mint(address account, uint256 amount) internal virtual override {

        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");

        super._mint(account, amount);

    }

}

Many thanks for you help

Hi @Kamil37

This error has been discussed many times in this thread by other students :slight_smile: It is a know issue with this contract implementation and Openzeppelin has a GitHub issue opened.
You should avoid to mint in the constructor, this is a valid workaround.

Happy coding,
Dani

1 Like

Thanks Dan, I went through all Openzepplin issue forums and kind of understand, but what I don’t understand is where and how my contract above minting in the constructor. I don’t see any function minting in the constructor I don’t know what I should change there.

Edited: It looks like we talking about two different issues. Looks like you talking about the issue that has been resolved and is not present in my contract, the error is about setCap function.

Hi @Kamil37

My bad I did not read the error correctly.
There is a logic flow in your code :slight_smile:

Immutable keyword:

Variables declared as immutable are a bit less restricted than those declared as constant: Immutable variables can be assigned an arbitrary value in the constructor of the contract or at the point of their declaration. They cannot be read during construction time and can only be assigned once.

Therefore this line is not valid (function setCap):

_cap = cap_;

Well, I spend an hour or so figuring it out, but that wasn’t a waste. It all helps me to understand it more, and I’m actually happy that I picked up that there’s something not right about what you are saying. Thanks Dan

1 Like

Hi @dan-i

The below code is throwing an error by saying -
Contract “MyToken” should be marked as abstract.

However, this was not the case with Filip’s code. What should I do?

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import  '../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract MyToken is ERC20{
}

Thanks

1 Like

@tanu_g
[/quote]. try changing your solidity version. not sure if this will fix but try using version 0.8.0 and dont forget to change it in truffle config aswell

EDIT how stupid of me sorry i did no think. The reason this is happening is because you have not included the constructor. Even if you are not changing the contents of the ERC20 contract you must include the constructor or else you will get this “marking contract as abstract” error. Use filips constructor from the video i can not remember what this lesson was about but it will work then or even something like

constructor() ERC720("MyToken", "MTKN") public { }

will work and get ride of the error. So remember you always need to implement the constructor when inhereting from the openzeppelin contratcs to avoid this error

1 Like

Hey @mcgrane5,
Thanks a lot for the input… I’ve been looking for it for a while without watching the video.:innocent:

It works very well, but with a slight modification. As for ERC720, it should be ERC20.

2 Likes

Hey @tanu_g sorry thats my bad that was a typo 9n my behalf. Im delighted that u got it working

2 Likes

Holy smokes, I was getting compile errors up the bum for over an hour.

For anyone else looking, getting the compile error on first compilation of:

ParserError: Source "/C/Users/Jack/Desktop/Truffle Projects/token/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol" not found

Instead of using the import directory as:

import "../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";

Try changing the import directory to:

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

(which is just removing ../node_modules/)
You’ll still get the ‘abstract’ errors after that, but @filip addresses these after in the video.

1 Like