Assignment - Openzeppelin Templates

Only the Cap’tn can change the cap!

pragma solidity 0.8.0;

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

contract Mytoken is ERC20PresetMinterPauser{

    bytes32 public constant CAPTAIN_ROLE = keccak256("CAP'TAIN_ROLE");

    uint256 private  _cap;

    /**

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

     * changed by the cap'tain after construction.

     */

    constructor(uint256 cap_) ERC20PresetMinterPauser("TygerToken", "TYGR") {

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

        _setupRole(CAPTAIN_ROLE, _msgSender());

        _cap = cap_;

    }

    

    /**

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

     */

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

        return _cap;

    }

    /**

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

     */

    function cap(uint _newcap) public virtual returns (uint256) {

        require(hasRole(CAPTAIN_ROLE, _msgSender()), "Mytoken: must have cap'tain role to change the cap");

        require(_newcap >= ERC20.totalSupply(),"We really shouldn't decrease the cap below the current supply, Cap'tn!");

        _cap=_newcap;

        return _cap;

    }

    /**

     * @dev See {ERC20-_mint}.

     */

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

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

        super._mint(account, amount);

    }

}
1 Like

token.sol

// 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 MyToken is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAPPER_ROLE = keccak256("CAPPER_ROLE");

    uint256 private _cap;

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, `PAUSER_ROLE` AND *"CAPPER_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(CAPPER_ROLE, _msgSender());

        
        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);
    }


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

    function setCap(uint256 cap_) public returns (uint256){
        require(hasRole(CAPPER_ROLE, _msgSender()), "CAPPER_ROLE: must have CAPPER_ROLE to change Cap");
        require((cap_ > 0) && (ERC20.totalSupply() <= cap_), "Cap must be >0 && < Current Token Supply");
        _cap = cap_;
        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);
    }
}

2_token_migration.js

const MyToken = artifacts.require("MyToken");

module.exports = function (deployer) {
  const name = "MyTokenX";
  const symbol = "MTKX";
  const cap_ = 1000000;
  
  deployer.deploy(MyToken,name,symbol,cap_);
};

1 Like

Great solution @Sieg, you have added additional functionalities like access control and pausable capabilities!

You could also write some unit tests for this contract to be sure that everything is working properly :nerd_face:

Carlos Z

1 Like

Many thanks thecil
My main prb was to figure out I had to add constructor parameters to the deployer =)
Can you please tell me more about unit tests ?
Thanks again thecil, have a great day !

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";


contract MyToken is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAPPER_ROLE = keccak256("CAPPER_ROLE");
 
    uint256 private _cap;
   
    constructor(string memory name, string memory symbol, uint256 cap_) ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(CAPPER_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());

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

    function setCap (uint newCap) public virtual returns (uint256){
      require(hasRole(MINTER_ROLE, _msgSender()), "Must have capper role to set cap");
      require(newCap > 0, "ERC20Capped: cap is 0");
      require(newCap >= totalSupply());
      _cap = newCap;
      return _cap;
    }


    function cap() public view virtual returns (uint256) {
        return _cap;
    }

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

    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        _mint(to, amount);
    }

    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
        _pause();
    }

    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)) {
          require(totalSupply().add(amount) <= cap());
    }
}
1 Like

Further in this course you will learn how to write unit test for this.

But the basic structure is this one for example:

Carlos Z

My solution…
Didn’t need to copy code from preset. I could extend it normally

pragma solidity 0.8.9;

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

contract Token is ERC20PresetMinterPauser {

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

    uint256 private _cap;

    constructor() ERC20PresetMinterPauser("Token", "TKN") {
        _setupRole(CAP_ROLE, _msgSender());
        setCap(10000000);
        require(cap() > 0, "Token: cap is 0");
        _mint(msg.sender, 1000);
    }

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

    function setCap(uint256 cap_) public {
        require(totalSupply() <= cap_, "Token: Cap should be greater or equal to current total supply");
        require(hasRole(CAP_ROLE, _msgSender()), "Token: must have cap role to change cap");
        _cap = cap_;
    }

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

}
1 Like

I get this error when I try to compile and I haven’t found a solution for it:

Error
> Compiling ./contracts/token.sol

DeclarationError: Identifier not found or not unique.
  --> /Users/santiago/Documents/Proyectos/Ivan On Tech Academy/Ethereum Smart Contract Programming 201/token/contracts/token.sol:26:11:
   |
26 |     using SafeMath for uint256;
   |           ^^^^^^^^

Compilation failed. See above.

My code:

token.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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 CAPPER_ROLE = keccak256("CAPPER_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_) ERC20(name, symbol) {
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAPPER_ROLE, _msgSender());
    }

    /**
     * @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)) {
          require(totalSupply().add(amount) <= cap(), "ERC20: cap exceeded");
        }
    }

    function cap() public view virtual returns (uint256) {
        return _cap;
    }

    function setCap(uint256 _newCap) public {
      require(hasRole(CAPPER_ROLE, msg.sender()), "ERC20PresetMinterPauser: capper role required to change cap");

      require(totalSupply() >= _newCap, "ERC20Capped: total supply exceeded");

      _cap = _newCap;
    }
}
// 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 MyToken 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 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, "MyToken: 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()), "MyToken: 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()), "MyToken: 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()), "MyToken: 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)) {
            require(totalSupply() + amount <= cap(), "MyToken: cap exceeded");
        }
    }

    //Add functions for cap

    function cap() public view virtual returns (uint256) {
        return _cap;
    }

    function modifyCap(uint256 cap_) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "MyToken: must have cap role to change cap");
        require(cap_ > 0, "MyToken: cap is 0");
        _cap = cap_;
    }

}
1 Like

Hey @santiago, hope you are well.

I did not see any statement in your contract where you import the safemath contract, you should do it in the same way you have imported the other contract:

Example:

import "../node_modules/@openzeppelin/contracts/access/AccessControl.sol";
... and so on

Carlos Z

1 Like

// 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 MyToken is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAPPER_ROLE = keccak256("CAPPER_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_) ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAPPER_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 changeCap(uint256 _newCap) public virtual returns (uint256 _cap){
        require(hasRole(CAPPER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capper role to change");
        _newCap = _cap;
        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);
    }

    /**
     * @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);
    }
}

1 Like

Thank you @thecil, that worked. I just wonder why that import statement is not written in the assignment solution on Filip’s Github?

Here is my solution.

2_token_migration.js

const ERC20 = artifacts.require("MyToken2");

module.exports = function () {
  deployer.deploy(ERC20, "MyToken2", "MTN2", 10000);
};

token2.sol

contract MyToken2 is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    uint256 private _cap;
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAP_ROLE = keccak256("CAP_ROLE");

    /**
     * @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, 100) {
        _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 Change the token's total supply.
    */
    function changeCap(uint newValue) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "MyToken2: must have cap role to change cap");
        _changeCap(newValue);
    }

    /**
     * @dev Triggers the change of the token's total supply.
    */
    function _changeCap(uint _newValue) internal virtual {
        _cap = _newValue;
    }

    /**
     * @dev Returns the cap on the token's total supply.
    */
    function cap() public view virtual returns (uint256) {
        return _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()), "MyToken2: must have minter role to mint");
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        _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()), "MyToken2: 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()), "MyToken2: 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);
    }
}

Truffle Console

truffle(develop)> i = await MyToken2.deployed()
undefined
truffle(develop)> i.cap()
<BN: 2710>
truffle(develop)> i.changeCap(100000)
{ tx:
   '0x9b1c7aa746a30bbfe680c4542e4105023edf05ac29e9264f90265101926b74e9',   
  receipt:
   { transactionHash:
      '0x9b1c7aa746a30bbfe680c4542e4105023edf05ac29e9264f90265101926b74e9',
     transactionIndex: 0,
     blockHash:
      '0x6f2a8747f10970d3a856ff6a60aeb9c312e830ed44f803ffd2ca124eb1d00128',
     blockNumber: 57,
     from: '0x32eaf6dc79f055eb5e0a7760ee34f26fd213f895',
     to: '0x3f9d66e33aa6ab9c509e52e1829d75aaaf501822',
     gasUsed: 27876,
     cumulativeGasUsed: 27876,
     contractAddress: null,
     logs: [],
     status: true,
     logsBloom:
      '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     rawLogs: [] },
  logs: [] }
truffle(develop)> i.cap()
<BN: 186a0>
1 Like

I think filip does not use the safemath for this exercise, or maybe he just forget it :nerd_face:

Carlos Z

1 Like

Hey Guys, this is my code, but somehow i get an error message in the SetCap function… i cant set cap = cap . It says: Expected ‘;’ but got identifier

// 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 ATOKEN 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 private immutable _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, uint 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()), "ERC20Capped: must have CAP role to change the 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)){
            require(totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        }
    }
}

        /**
     * @dev Sets the value of the `cap`. This value is immutable, it can only be
     * set once during construction.
     */
    
    
    /**
     * @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);
    }
    
   
}

const ATOKEN = artifacts.require("ATOKEN");

module.exports = function (deployer) {

  deployer.deploy(ATOKEN, "ATOKEN", "LAG", 100000000000);


}

I found my mistake, but now i have an other problem.
When i try to type:

let instance = await ATOKEN.deployed()

I get the error message:

Error: ATOKEN has not been deployed to detected network (network/artifact mismatch)

PS: I compiled and migrated everything successfully

Here is my solution.
Please feel free to let me know if I need to amend anything. Thanks :+1: :slightly_smiling_face:

Assignment - Openzeppelin Templates

// 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 customToken is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {

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

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

bytes32 public constant CAPPED_ROLE = keccak256("CAPPED_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_) ERC20(name, symbol) {

    _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

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

    _cap = cap_;

    _setupRole(MINTER_ROLE, _msgSender());

    _setupRole(PAUSER_ROLE, _msgSender());

    _setupRole(CAPPED_ROLE, _msgSender()); //cap role to adjust cap limit

}

/**

 * @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();

}

/**

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

 */

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

    return _cap;

}

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() + amount <= cap(), "ERC20Capped: cap exceeded");

    }

}

function setCap(uint256 capLimit) public virtual { //modifying the cap limit

    //making sure caller has cap priviledges

    require(hasRole(CAPPED_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capped role to set cap limit");

    //make sure total supply doesn't exceed the adjusted cap limit

    require(totalSupply() >= capLimit, "ERC20Capped: cap exceeded");

    //set new cap limit

    _cap = capLimit;

}

}

1 Like

[/quote]

hey @Leonard_Gohlke, hope you are well.

[quote=“Leonard_Gohlke, post:190, topic:33838”]

     function SetCap (uint256 _cap) public virtual {
        require(hasRole(CAP_ROLE,_msgSender()), "ERC20Capped: must have CAP role to change the CAP");
        require(cap_> 0, "ERC20Capped: cap is 0")
        _cap = cap_;

    }

i think the error is on your variable name, which is the same than the first declaration:

Now about your migration error:

If you are using the truffle console, you should do migrate inside of it, then you should be able to call

let instance = await ATOKEN.deployed()

Let me know if it works.

Carlos Z

Hey @crypto-djent76, hope you are ok.

Nice solution, but is a little bit of a challenge to read the code properly, would be great if you can share it like this way so i can review it properly :nerd_face:

https://academy.ivanontech.com/lessons/how-to-post-code-in-the-forum

Carlos Z

1 Like

Hi @thecil, I’m good thanks. Hope you are too.

Looking at the link you shared, it’s coming up as a 404 page. So I assume you’d like me to do this instead? :upside_down_face:

Assignment - Openzeppelin Templates
// 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 customToken is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {

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

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

bytes32 public constant CAPPED_ROLE = keccak256("CAPPED_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_) ERC20(name, symbol) {

    _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

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

    _cap = cap_;

    _setupRole(MINTER_ROLE, _msgSender());

    _setupRole(PAUSER_ROLE, _msgSender());

    _setupRole(CAPPED_ROLE, _msgSender()); //cap role to adjust cap limit

}

/**

 * @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();

}

/**

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

 */

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

    return _cap;

}

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() + amount <= cap(), "ERC20Capped: cap exceeded");

    }

}

function setCap(uint256 capLimit) public virtual { //modifying the cap limit

    //making sure caller has cap priviledges

    require(hasRole(CAPPED_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capped role to set cap limit");

    //make sure total supply doesn't exceed the adjusted cap limit

    require(totalSupply() >= capLimit, "ERC20Capped: cap exceeded");

    //set new cap limit

    _cap = capLimit;

}
}

Let me know if any issues.
Thanks :+1:

1 Like