Assignment - Openzeppelin Templates

Oh im staring to understand a little, i am testing stuff with the base ERC20 contract and i put name and symbol in front of the contracts name in the migration file and it worked, im gonna keep messing around witht these stuff cause im so lost o

module.exports = function (deployer) {
  deployer.deploy(ERC20, "MyToken", "MTKN");
};
1 Like

Here is my solution:

I imported the ERC20PresetMinterPauser.sol directly and rather built the cap functions.
CAPPER_ROLE is setup in ERC20PresetMinterPauser.sol(hope that works). And I brought in the _mint() function from ERC20Capped.sol and the _beforeTokenTransfer() function from ERC20Pausable.sol as the function body of the _beforeTokenTransfer() in the video was split into these two so I thought it would maybe work(please let me know if it doesnā€™t).

// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.0;

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

contract MyToken is ERC20PresetMinterPauser, Ownable {

    uint256 private _cap;

    constructor() ERC20PresetMinterPauser("RoryToken", "RORY") {
       _cap = 300000000000000000000; 
       _setupRole(CAPPER_ROLE, _msgSender());

       _mint(msg.sender, 300000000000000000000);
    }

    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 _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

        require(!paused(), "ERC20Pausable: token transfer while paused");
    }

    function newCap(uint256 _newCap) public {
        require(hasRole(CAPPER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capper role to change cap");
        require(_newCap > 0, "ERC20Capped: cap is 0");
        _cap = _newCap;
    }
}
1 Like

Hey @Carlosvh96

Thatā€™s the way! Good job

Hello. I bring you a solution

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/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
 *  - a cap editor role that allows to set token cap
 *
 * 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 SamuraiToken is Context, AccessControl, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAP_EDITOR_ROLE = keccak256("CAP_EDITOR_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_EDITOR_ROLE, _msgSender());

        require(cap_ > 0, "SamuraiToken: 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()), "SamuraiToken: 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()), "SamuraiToken: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Set token cap.
     *
     * Requirements:
     *
     * - the caller must have the `CAP_EDITOR_ROLE`.
     */
    function setCap(uint256 cap_) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "SamuraiToken: must have cap editor role to change cap");
        require(cap_ >= totalSupply(), "SamuraiToken: cap must be greater than total supply");
        _cap = cap_;

    }

    /**
     * @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()), "SamuraiToken: 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;
    }

    /**
     * @dev See {ERC20-_beforeTokenTransfer}.
     *
     * Requirements:
     *
     * - minted tokens must not cause the total supply to go over the cap.
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

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

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";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.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 MyTokenNew is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAPCHANGER_ROLE = keccak256("CAPCHANGER_ROLE");

    uint256 private _cap; // mutable
    using SafeMath for uint256;

    /**
     * @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(CAPCHANGER_ROLE, _msgSender()); // should be the owner

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

    function changeCap(uint256 newAmount) public virtual {
        require(hasRole(CAPCHANGER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        require(newAmount > 0, "ERC20Capped: cap is 0");

         _cap = newAmount;
    }



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


    }
}

1 Like
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";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol";


contract MyToken is Context, AccessControlEnumerable, 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 MODIFY_CAP_ROLE = keccak256("MODIFY_CAP_ROLE");
    uint256 private _cap;

    constructor(string memory name, string memory symbol, uint256 cap_) ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MODIFY_CAP_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());

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

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

    function modifyCap(uint _newCap) public virtual returns(uint256){
        require(hasRole(MODIFY_CAP_ROLE, _msgSender()), "Must have the MODIFY_CAP_ROLE to modify cap" );
        _cap = _newCap;
        return _newCap;
    }
    
    
    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        _mint(to, amount);
    }

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

1 Like

This is my solution
In my case I really have problems with this variable uint256 immutable private _cap (even for using only for ERC20Capped)
I used instead uint256 private _cap;

I figured out that there is already an address(msg.sender) for Minter in ERC20PresetMinterPauser.sol

 constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

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

This is my contract.

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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


contract MyToken is ERC20PresetMinterPauser {
    uint256  private _cap; // I cannot do this with immutable 


    constructor() ERC20PresetMinterPauser("CARLOS","CR"){ 
        uint cap_ = 1000000;
        _cap = cap_;
        _mint(msg.sender,10000);
    }

    function cap() public view virtual returns (uint256) {// with immutable _cap does not work 
        return _cap;
    }
    
    function _mint(address account, uint256 amount) internal virtual override { // with immutable _cap does not work 
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        require(hasRole(MINTER_ROLE, account), "ERC20PresetMinterPauser: must have minter role to mint");
        super._mint(account, amount);
    }
    
    function hasRole(address x) public view returns(bool) {// only to check the role from AccessControl 
        return hasRole(MINTER_ROLE, x);

    }
}

image

image

Capped
image

1 Like

Updated from Saturday (forgot check if from ==address(0) in beforeTokenTransfer

// 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";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.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 {
    using SafeMath for uint256;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAPCHANGER_ROLE = keccak256("CAPCHANGER_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, uint cap_) ERC20(name, symbol) {
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

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

        _cap=cap_;
        _mint(msg.sender, 1000);
        
    }

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

        
    }
    function cap() public view virtual returns (uint256) {
        return _cap;
    }    
    function modifyCap(uint updatedCap) public virtual{
        require(hasRole(CAPCHANGER_ROLE,msg.sender),"Dont Have Access");
        require(updatedCap > 0, "ERC20Capped: cap is 0");
        _cap = updatedCap;
    }
}
1 Like
pragma solidity 0.8.3;

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

contract MyToken is ERC20PresetMinterPauser {
    using SafeMath for uint256;
    uint256 private _cap;
    bytes32 public constant CAP_ROLE = keccak256("CAP_ROLE");


    constructor (uint256 cap_) ERC20PresetMinterPauser("MyToken", "MTK") {
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
        _setupRole(CAP_ROLE, _msgSender());
        super.mint(msg.sender, 1000);
    }

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

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

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

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauser) {
        super._beforeTokenTransfer(from, to, amount);
        if (from == address(0)) { // When minting tokens
            require(totalSupply().add(amount) <= getCap(), "ERC20Capped: cap exceeded");
        }
    }

}

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 {
using SafeMath for uint256;
    uint private _cap;
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAP_CHANGER_ROLE = keccak256("CAP_CHANGER_ROLE");

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, `PAUSER_ROLE` and `CAP_CHANGER_ROLE`  to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
    constructor(string memory name, string memory symbol, uint 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(CAP_CHANGER_ROLE, _msgSender());
        _mint(msg.sender, 1000);
    }

    /**
     * @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");
        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()), "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 and changes cap.
     *
     * Requirements:
     *
     * - the caller must have the `CAP_CHANGER_ROLE`.
     */
    function cap() public view virtual returns (uint256) {
        return _cap;
    }

    function changeCap(uint256 newCap) public {
        require(hasRole(CAP_CHANGER_ROLE, _msgSender()), "ERC20Capped: must have cap changer role to change cap");
        require(newCap>0, "ERC20Capped: Cap is 0");
        _cap = newCap;

    }


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

    }
     
1 Like

Here is my solution :slight_smile:

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


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

    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(CHANGER_ROLE, _msgSender());
    }

    function cap() view virtual{

        return _cap;

    }

    function changeCap(uint256 newCap) public virtual {
        require(
            hasRole(CHANGER_ROLE, msg.sender),
            "must have Changer role to change cap"
        );
        require((newCap > 0), "cap has to be a positive number");

        newCap = _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().add(amount) <= cap(), "ERC20Capped: cap exceeded");

    }

    
    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);
    }
}
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/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 ERC20PresetMinterPauser 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_CHANGER_ROLE = keccak256("CAP_CHANGER_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(CAP_CHANGER_ROLE, _msgSender());
        _mint(msg.sender, 1000);
    }

    /**
     * @dev Modify the cap on the token's total supply.
     */
    function setCap(uint256 cap_) public virtual returns (uint256) {
        require(
            hasRole(CAP_CHANGER_ROLE, _msgSender()),
            "ERC20PresetMinterPauser: must have cap changer role to pause"
        );
        _cap = cap_;
        return _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);
    }

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

1 Like

After hours of making and handling errors, this is my final product. I can compile this contract but seem to have trouble migrating it, problem is on the Migrations file I think. But, I cannot find the solution as of now. Iā€™m still learning the inner workings of the migration files :sweat_smile:

Anyway, Hereā€™s my code!

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

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

contract MPCToken is ERC20PresetMinterPauser {
//added a cap setter role
    bytes32 public constant CAPSETTER_ROLE = keccak256("CAPSETTER_ROLE");
    uint256 private _cap;

    constructor(uint256 cap_) ERC20PresetMinterPauser("Virtuosa", "VIA"){
        _setupRole(CAPSETTER_ROLE, _msgSender());
        
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = 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);
    }
//modified this function below
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauser) {
        super._beforeTokenTransfer(from, to, amount);
        require(from == address(0), "Only Admin can mint Tokens");
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");      
    }
//added a new function which only allows cap setter to change token cap
    function setCap(uint _newCap) public {
        require(hasRole(CAPSETTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap setter role to change cap");
        require(_newCap > ERC20.totalSupply(),"New Cap should be more than Total Supply");
        require(_newCap > 0, "Token Cap is 0");
        _cap = _newCap;
    }
}
1 Like

Hereā€™s my migration file, I think I got it working but whenever I set the cap_ to 21^18, I get an overflow error? Why is that happening? Thanks a lot, guys!

Code here:

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

module.exports = function (deployer) {
  var cap_ = 21000000;
  deployer.deploy(MPCToken, cap_);
};

Error here, if I use 21000000000000000000 as cap_

Error:  *** Deployment Failed ***

"MPCToken" -- overflow (fault="overflow", operation="BigNumber.from", value=21000000000000000000, code=NUMERIC_FAULT, version=bignumber/5.0.8).
1 Like

Hi @CryptoXyz

You should use BigNumber.

const BigNumber = require('bignumber.js');
const x = new BigNumber(21000000000000000000);

module.exports = async function (deployer) {
  deployer.deploy(Test, x);
};
pragma solidity 0.6.0;

contract Test {

  uint public cap;
  constructor(uint _c) public {
    cap = _c;
  }
}
truffle(develop)> const a = await Test.deployed()
undefined
truffle(develop)> await a.cap()
<BN: 1236efcbcbb340000>

<BN: 1236efcbcbb340000> is 21000000000000000000

Reagrds,
Dani

2 Likes

This took a bit of mental gymnastics to understand {AccessControl} and implement the new role, but I think I got it figured. Slammed my face against the Truffle debugger for a few minutes to try and follow the state of the _cap variable, but I stopped when my eyes glazed over. Thatā€™s another tool I gotta get used to!

// 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());

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

    /** 
     * Allows users with `CAP_ROLE`
     * to change `_cap` token supply limit
     */
    function changeCap(uint256 newCap) public {
        require(hasRole(CAP_ROLE, _msgSender()), "changeCap: must have cap role to change cap");
        require(newCap > 0, "changeCap: cap is 0");
        _cap = newCap;
    }

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

2 Likes

here ya go, added another require statement into mint function so you can mint more than the cap.

// 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, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

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

    function changeCap(uint256 newCap) external {
        require(hasRole(CAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap role to change cap");
        require(ERC20.totalSupply() <= newCap, "ERC20Capped: new cap cant be less than current total supply");
        _cap = newCap;
    }

    /**
     * @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");
        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()), "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
pragma solidity ^0.8.0;

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

contract MyNewToken is AccessControlEnumerable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CAP_CHANGE_ROLE = keccak256("CAP_CHANGE_ROLE");

    uint256 private _cap;

    constructor(uint256 cap_) ERC20("NewEthToken", "NET") {
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
        
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAP_CHANGE_ROLE, _msgSender());

        mint(msg.sender, 11000);
    }

    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 {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
        _pause();
    }

    function unpause() public {
        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._beforeTokenTransfer(from, to, amount);
    }
  
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    function burnFrom(address account, uint256 amount) public virtual {
        uint256 currentAllowance = allowance(account, _msgSender());
        require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance");
        _approve(account, _msgSender(), currentAllowance - amount);
        _burn(account, amount);
    }

    function modifyCap(uint256 amount) public virtual {
        require(hasRole(CAP_CHANGE_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap change role to modify Cap");
        _modifyCap(amount);
    }

    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 _modifyCap(uint256 newCap_)public {
        require(newCap_ > 0, "ERC20Capped: cap is 0" );
        _cap = newCap_;
    }
}

1 Like

I had everything going until I got to the function.

function changeCap() public virtual {
      require(hasRole(CHANGECAP_ROLE, _msgSender()), "must have ChangeCap role to change the cap");
    }

I then fixed it after looking at otherā€™s results and Pilipā€™s, like so.

// Sets the new cap for tokens
    function changeCap(uint256 cap_) public virtual {
      require(cap_ >= 0, "ERC20Capped: Cap is 0");
      require(hasRole(CHANGECAP_ROLE, _msgSender()), "must have ChangeCap role to change the cap");

      _cap = cap_;
    }

But as for the last function _beforeTokenTransferā€¦I noticed Filipā€™s addition is not in the current ERC20Capped.sol file.
ā€¦Pilipā€™s

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

currently in ERC20Capped.sol file

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

Also: I tried to deploy the MyToken Contract but I get an Error message, ā€œReferenceError: RenCoin is not definedā€
Here is my Migration fileā€¦

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

module.exports = function (deployer, name, symbol, cap_ ) {
  const userName = name;
  const userSymbol = symbol;
  const userCap = cap_;
  deployer.deploy(MyToken, RenCoin, RENN, 100000);
};

I did some more research and got this ERC20 token contract to deploy

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

module.exports = function (deployer) {
  const name_ = 'Ren Token';
  const symbol_ = 'REN';
  const cap_ = 1000000;
  
  deployer.deploy(MyToken, name_, symbol_, cap_);
};

the only thing now is that when I thought I changed the cap or if I try to get the totalSupply(), I get back 0

truffle(develop)> await instance.changeCap(5000000)
{
  tx: '0x7c9ba3e39f5036422b96b97a2306b3c7b1a875572f9d32388c5795d2687bef08',
  receipt: {
    transactionHash: '0x7c9ba3e39f5036422b96b97a2306b3c7b1a875572f9d32388c5795d2687bef08',
    transactionIndex: 0,
    blockHash: '0x2bfed59b6c1cc3c3a7f729d680bde55c106aac6609f4c37c4c7a53a92fe2b728',
    blockNumber: 22,
    from: '0xdc6bbc41d7ff0accd096969921067c07cd3b0d37',
    to: '0xcf08d681bf9dfafa09bcc124a693fa8f42250239',
    gasUsed: 30327,
    cumulativeGasUsed: 30327,
    contractAddress: null,
    logs: [],
    status: true,
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    rawLogs: []
  },
  logs: []
}
truffle(develop)> instance.totalSupply()
BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }