Assignment - Openzeppelin Templates

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
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 MySecondToken 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) {
        require(cap_ > 0, "MySecondToken: cap is 0");
        _cap = cap_;

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

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

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

    function capAdd(uint256 amount) public virtual returns (uint256) {
        require(hasRole(CAPPER_ROLE, _msgSender()), "MySecondToken: must have minter role to mint");
        _cap +=  amount;
        return _cap;
    }

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

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


    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "MySecondToken: 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 "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
import "@openzeppelin/contracts/utils/Context.sol";

contract TokenMinterPauser 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");
    uint private _cap;
    
    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(CAPPER_ROLE, _msgSender());
        
        require(cap > 0);
        _cap = cap;
    }

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

    function setCap(uint cap) public {
        require(hasRole(CAPPER_ROLE, _msgSender()), "Must have CAPPER ROLE");
        require(cap > 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");
        super._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);
    }
}

I removed the comments, because it’s too much code. I feel a bit lost. I don’t think we covered this virtual or super in the course…

Question.

For the setupRole(bytes32 role, address account) function, the second argument(account) should be any address. But for some reason, it only works with the _msg.sender( ) function as argument for account.

If I give an actual address as an argument to that function it grants access to anyone that calls my modifyCap function, without an error. Why is that?

Doesn’t work

setupRole(CAP_MODIFIER, 0xce30923002102aB92baB7277B0B539FBa275f398);

Works

setupRole(CAP_MODIFIER, _msgSender( ));

Btw, this was a great assignment, thank you!

Here is my code:

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (token/ERC20/presets/ERC20PresetMinterPauser.sol)





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 ERC20Modified 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_MODIFIER = keccak256("CAP_MODIFIER");


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

   

   
    function cap() public view virtual returns (uint256) {
        return _cap;
    }
   
   // my added function that modifies the cap
    function modifyCap(uint newCap)public{
        require(hasRole(CAP_MODIFIER, _msgSender()), "ERC20CapModifier: must have capModifier role to modify the cap");
        _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 <= 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

Here is my final code. After watching the final solution video and comparing it to my code below I noticed that the ERC20Capped version has changed a bit from what is shown in the video. Mainly I do not see a before transfer function in my ERC20Capped.sol. I utilized the supply check require statement in my _mint function instead. Hopefully that was OK.

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.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/access/Ownable.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 MODCAP_ROLE = keccak256("MODCAP_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, "cap must be > 0");

        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(MODCAP_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(), "cap exceeded");
        _mint(to, amount);
    }

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

    /**
     * @dev Modifies the cap on the token's total supply.
     */
    function ModCap(uint256 cap_) public virtual returns (uint256) {
        require(hasRole(MODCAP_ROLE, _msgSender()), "Only the contract creator can modify the cap");
        require(ERC20.totalSupply() <= cap_, "Total supply already exceeds cap");
        _cap = cap_;
        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);
    }
}

Here is my code for the assignment. I coded the _setcap function outside of the constructor for better visibility of the code and set the SETCAP_ROLE to accounts[5] in truffle.

I googled to find how to pass a function in truffle develop from a specific account as i cant remember this being explained in the videos.

truffle(develop)> instance.changeCap(25000, {from: accounts[5]})
// 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 Token is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant SETCAP_ROLE = keccak256("SETCAP"); //define new role for setting token cap

    uint256 private _cap; // define token 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) ERC20("Token", "TKN") {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setupRole(SETCAP_ROLE, 0x06b9fBe0daAd76a80BB531F2f14383E6b16e8f11);

        //set token cap in constructor
        _setcap(1000);
        _mint(msg.sender, 1000);

        
    }

    function _setcap(uint256 cap_) private{  //function for setting token cap used in constructor
        require(cap_ > 0, "ERC20Capped: cap is 0");
require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }

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

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

    function changeCap(uint _newcap) public returns(bool _success){
        require(hasRole(SETCAP_ROLE, _msgSender()), "Must have CHANGECAP ROLE");
        _cap = _newcap;
        return true; 




    }

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

Hey @sbelka1703, hope you are well.

I think the error comes from the checksum verification of the address type, https://docs.soliditylang.org/en/develop/types.html#address-literals

Now if you think about it for a sec, you are creating the roles for the first time, so its the deployer who will do that (msg.sender()), then you could grantRole() to the address you desired through the unit test.

Carlos Z

Here is my assignment code.

Question: is there any reason that the base ERC20 contract doesn’t use SafeMath anymore?

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

    //ASSIGNMENT

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

    /**
     * @dev Changes cap on token supply.
     *
     * Requirements:
     *
     * - the caller must have the `CAP_ROLE`.
     */
    function capChange(uint256 cap_) public virtual {
        require(hasRole(CAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap role to change token supply");
        require(ERC20.totalSupply() <= cap_, "ERC20PresetMinterPauser: existing tokens exceed new cap");
        _cap = cap_;
    }
        
}
1 Like

Hey @MightyYak, hope you are well.

Since solidity version 0.8, is optional to use SafeMath:

https://docs.soliditylang.org/en/latest/080-breaking-changes.html

Carlos Z

pragma solidity ^0.8.0;

import "../node_modules/@openzeppelin/contracts/access/AccessControlEnumerable.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/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;

    /**
     * @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 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`.
     * - the created amount can't increase the total supply beyond the 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 setCap(uint256 cap_) public {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have capper role to change cap");
        require(totalSupply() >= cap_, "ERC20Capped: new cap must be greater than total supply");
        _cap = 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);
    }
}
1 Like
pragma solidity >=0.6.0 <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");

    /**

     * @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_;

    }

      uint256 private _cap;

    /**

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

     */

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

        return _cap;

    }

    function setCap(uint256 newcap) public virtual returns (uint256){

        require(hasRole(CAPPER_ROLE, _msgSender()), "Reverted: Must have capper role to set caps.");

        _cap = newcap;

        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()), "Reverted: 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

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 ERC20PresetMinterPauser 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 CAP_ROLE = keccak256("PAUSER_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_ROLE, _msgSender());

        
    }


    /**
     * @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()), "ERC20PresetMinterPauser: must have minter role to mint");
        require(ERC20.totalSupply() + amount <= cap(), "ERC20Capped: cap exceeded");
            super._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 setCap(uint256 cap_)public virtual{
        require(hasRole(CAP_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have cap role to set cap");
        require(cap_ > 0, "ERC20Capped: cap is 0");
        _cap = cap_;
    }


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

    }
}

1 Like