Assignment - Openzeppelin Templates

Hey @William

Is not a bad idea to mint tokens in the constructor, it really depends on what your project is about :slight_smile:

On top of this, I can’t access any function that allows me to mint more after contract creation…

instance.mint("0x02daf6ce83fd3803704f63daeb66d930b5121dc5", 250000)
         ^
Uncaught TypeError: instance.mint is not a function

In order to check this one I need to see how you’ve create instance (let instance = await …deployed()), which contract have you assigned to instance? Does that contract have a function mint?

Keep me posted,
Dani

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

    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_ROLE, _msgSender());
    }
    
    function currentCap() public view virtual returns (uint256) {
        return cap;
    }

    function changeCap(uint256 newCap) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have admin role to change cap");
        require(currentCap() <= newCap, "ERC20Capped: cap should be larger");
        cap = newCap;
    }

    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        require(ERC20.totalSupply() + amount <= currentCap(), "ERC20Capped: cap exceeded");
        _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) {
        require(ERC20.totalSupply() + amount <= currentCap(), "ERC20Capped: cap exceeded");
        super._beforeTokenTransfer(from, to, amount);
    }
}

I also have a problem when declaring a cap function.

image

It doesn’t allow me to name function cap() but it allows to name it currentCap() and other names. Can someone tell me what’s wrong with my code?
Thanks.

Hi @benlive

Based on the error returned by the compiler, there is already a function cap somewhere in your code.
It seems like you have a variable called ‘cap’ indeed, therefore you cannot declare a function with the same name.
You can easily replicate the issue with this code:

contract Test {
   uint public cap;
   
   function cap  () public {}
   
}

Happy coding,
Dani

1 Like

Is openzeppelin like moralis??
sorry if its a stupid question…

Hi @AX07

Openzeppelin and Moralis are two different things.
Openzeppelin is a smart contract library, Moralis is a pre-built backend system that allows you to build your dapp easily.

Cheers,
Dani

1 Like

My Capped version of the Openzepplin assignment. This is based on Contracts version 4.X which has a different directory layout so imports paths were modified. I have used hardcoded cap value and Token name and symbol in this code example.

Any comments?

type o// 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/token/ERC20/extensions/ERC20Capped.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 modify role to change  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, pauser and cap
 * 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");  // Add CAP_ROLE 
    
/**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE`, ' CAP_ROLE'  to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
  
     uint256  private _cap;
    
    constructor () ERC20 ("MyToken","MTKN")   {
           

    
	_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAP_ROLE, _msgSender());          // Add CAP_ROLE
	
	// set initial cap
	_cap=10000; 
}

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

  /**
     * @dev Modify token CAP value
     *
     * See {ERC20Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `CAP_ROLE`.
     */
    function modifyCap(uint256 newCap) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "MyToken: must have cap role to change CAPPED");
       require(newCap> 0, "MyToken: newCap is 0");
        _cap = newCap;
	
    }

  /**
     * @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(), "MyToken: cap exceeded");
		}
    }
}
r paste code here
1 Like

Below is my code:

// SPDX-License-Identifier: UNLICENSED

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 myPresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant CHANGECAP_ROLE = keccak256("CHANGECAP_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(CHANGECAP_ROLE, _msgSender());
    }

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

    function changeCap(uint256 amount) public virtual {
        require(hasRole(CHANGECAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have changecap role to change cap");
        _cap = 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");
        require(ERC20.totalSupply() + amount <= cap(), "ERC20PresetMinterPauser: 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);
    }
}

2 Likes

Here’s my version of the contract. I’ve edited out all the comments for ease of reading. It differs to Filip’s version in two details.

One: I hard-coded the cap, because I don’t know how to add arguments of the constructor when deploying (I kept on getting an error). If anyone can help me there I’d much appreciate it.

And two: Instead of modifying the _beforeTokenTransfer() function with this code

if (from == address(0)) {
require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
}

I modified the mint() function instead by just adding the second line of code:

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

As far as I can tell by testing the code it works. Did I miss something, or is my version also acceptable?

// 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 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() ERC20("MyToken", "MTKN") {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAPPER_ROLE, _msgSender());
        
        _cap = 100000;
    }

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

    function changeCap(uint256 cap_) public {
        require(hasRole(CAPPER_ROLE, _msgSender()), "Must have capper role to change cap");
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }
   
    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);
    }
  
    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

My solution

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

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

contract CoolToken is ERC20PresetMinterPauser {
    bytes32 public constant CAP_EDITOR_ROLE = keccak256("CAP_EDITOR_ROLE");

    uint _cap;

    constructor(uint cap_) ERC20PresetMinterPauser('CoolCoin', 'COOL') {
        require(cap_ > 0, "CoolToken: cap is 0");
        _setupRole(CAP_EDITOR_ROLE, _msgSender());
        _cap = cap_;
    }

    function setCap(uint _newCap) public {
        require(hasRole(CAP_EDITOR_ROLE, _msgSender()), 'CoolToken: must have cap editor role to change cap');
        require(cap_ > 0, "CoolToken: cap is 0");
        _cap = _newCap;
    }

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

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

Quiestion:
Why do we have to implement the cap functionality by ourselves? We can just inherit from ERC20Capped.sol, can’t we? We are then left by implementing only the role and setCap() function beacuse by inheriting from ERC20Capped we will get cap() and the right _mint() override

To migrate a Smart Contract with arguments, eg:

    constructor(uint cap_) ERC20PresetMinterPauser('CoolCoin', 'COOL') {
        require(cap_ > 0, "CoolToken: cap is 0");
        _setupRole(CAP_EDITOR_ROLE, _msgSender());
        _cap = cap_;
    }

Add the arguments behind the Smart Contract name in the migration file like this:

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

module.exports = function (deployer) {
  deployer.deploy(CoolToken, 1000000);
};
1 Like

Ahhh, cool, thank you :slight_smile:

1 Like

My solution:

...that no one is gonna expand
// 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
 *  - a capper role that allows to change the 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, pauser and capper
 * roles, as well as the default admin role, which will let it grant all minter
 * pauser and capper roles to other accounts.
 */
contract AssignmentToken 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, 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(CAPPER_ROLE, _msgSender());

        _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");
        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 Set the cap.
     *
     * Requirements:
     *
     * - the caller must have the `CAPPER_ROLE`.
     * - the new cap must be equal to or higher than the current supply.
     */
    function setCap(uint _newCap) public {
        require(hasRole(CAPPER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capper role to setCap");
        require(ERC20.totalSupply() <= _newCap, "ERC20Capped: newCap lower than current supply");
        _cap = _newCap;
    }

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

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

I used functions to increase and decrease the cap, provided the new cap is always >= the circulating supply. I also combined functions from pausable and burnable so the cap can’t be changed when the contract is paused.

pragma solidity 0.8.0;

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

contract Token is ERC20, AccessControlEnumerable, Pausable {

  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;

  constructor(uint256 cap_, uint256 amount_) ERC20( "Token", "TKN") {

    _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_;
    require(amount_ <= cap_, "Amount exceeds cap");
    _mint(msg.sender, amount_);
  }

//Check functions:
  function increaseCap(uint256 _amount) public whenNotPaused {
      require(hasRole(CAP_ROLE, _msgSender()), "Token: Must have cap role to increase cap");
      _increaseCap(_amount);
  }

  function decreaseCap(uint256 _amount) public whenNotPaused {
      require(hasRole(CAP_ROLE, _msgSender()), "Token: Must have cap role to decrease cap");
      _decreaseCap(_amount);
  }

//Effect functions:
  function _increaseCap(uint256 _amount) internal {
      _cap += _amount;
      assert(cap() >= totalSupply());
  }

  function _decreaseCap(uint256 _amount) internal {
      _cap -= _amount;
      require(cap() >= totalSupply(), "Cap cannot be smaller than total supply");
  }


//Checks the current cap;
    function cap() public view virtual returns (uint256) {
        return _cap;
    }

//Checks the current circulating supply;
    function totalSupply() public view virtual override returns (uint256) {
        return ERC20.totalSupply();
    }

//Mint function:
    function mint(address account, uint256 amount) public whenNotPaused {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        require(totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        _mint(account, amount);
    }

//Transfer function:
    function transfer(address recipient, uint256 amount) public virtual override(ERC20) whenNotPaused returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

//Pre-transfer pause check:
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

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

//Pause/unpause functions role check:
    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();
    }

//Burn functions:
    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);
    }

}

1 Like

As an aside, I want to test out granting/revoking Access Control roles via Truffle, but I can’t figure out what to enter for the bytes32 role argument on these functions, and Google searching is turning up empty.

I’m trying to type it in as such:
let token = await Token.deployed()
let account1 = accounts[1]
token.grantRole(CAP_ROLE, account1)

It throws the error: CAP_ROLE is not defined. It does this for all the roles that were defined in the beginning of the contract, and were used in the _setupRole constructor functions. How do I do this?

Here is my code:

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

        //sets the value of 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 Sets the new value of the cap.
    *
    * Requirements:
    *
    * - the caller must have the `CAPPER_ROLE`.
    * - the cap must be greater than zero
    */
    function setCap(uint256 cap_) public virtual {
        //check that msg.sender has the capper role
        require(hasRole(CAPPER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capper role to cap");
        //check that cap entry is greater than zero
        require(cap_ > 0, "ERC20Capped: cap is 0");
        //check that total supply is not exceeded
        require(totalSupply() >= cap_, "ERC20Capped: cap exceeded");
        _cap = cap_;
    }

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

    /**
     * @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);
        //check that total supply does not exceed cap
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
    }
}

Note: The contract will compile but on migrate will get Error: Deployment Failed.
It fails as the constructor parameters are looking for 3 arguments, but receives 0.
Solution is to add arguments into 2_token_migration.js to deploy with constructor parameters.

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

module.exports = function (deployer) {
  deployer.deploy(Migrations, "MyToken", "MTKN", '100000');
};

1 Like

Hi @CryptoPhoenix

You can check this post: Assignment - Openzeppelin Templates

Can i get an answer for my question please?:slight_smile:

Here is my cap assignment with the migration file

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

module.exports = function (deployer) {
  deployer.deploy(MyToken, "Tolga", "TLG", 200000);
};

// 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/token/ERC20/../../access/AccessControlEnumerable.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/../../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 INCREASER_ROLE = keccak256("INCREASER_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(INCREASER_ROLE, _msgSender());
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

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

    function increaseCap(uint256 newCap) public virtual {
        require(hasRole(INCREASER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have role to increase the cap");
        _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) {
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
        super._beforeTokenTransfer(from, to, amount);
    }
}

cap1 cap2 cap22 caperror

As you can see, the cap can be changed unless you are the constructor of the contract.

Hi @kopino4

Why do we have to implement the cap functionality by ourselves? We can just inherit from ERC20Capped.sol, can’t we?

You can surely inherit from ERC20Capped, this was just an exercise.

Cheers,
Dani

Hello,

here is my solution:

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

    constructor(string memory name, string memory symbol, uint256 cap_) ERC20("Axius", "AXU") {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(CAPCHANGER_ROLE, _msgSender());
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }
    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 ChangeCap(uint newCap) public virtual returns(uint256){
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have Cap Changer role to change cap");
        require(newCap > totalSupply();)
        _cap = newCap;
        return _cap;
    }

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

}

1 Like