ERC20 metadata and ERC20Detailed

Greetings @abcoathup Andrew,

Hope you are doing well. I´m back at deploying these contracts on Mainnet, so I looked at the documentation you shared regarding ERC20Detailed and I noticed:

a) I do not “import” OpenZelpelin Contracts, as the documentation suggests but rather, I create all functions/contracts within my Solidity script;

b) I do not know much about OpenZeppelin 2.x nor 3.x nor I have ever used them;
– Question: Could you please shed some light on the difference between these two versions and my Solidity script available at https://github.com/estudios-amazonia/FilmVault

c) I could not find the ERC20Detailed function on https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20.
– Question: Am I looking at the wrong place?

d) I looked at https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#ERC20Detailed but it was not easy for me how to reapply this function.
– Question: Do you have a working example somewhere where I to see it working?

I look forward to any help you may provide.

g.a.

1 Like

Hi @jaureguino,

To keep your system secure, you should always use the installed code as-is, and neither copy-paste it from online sources, nor modify it yourself.
https://docs.openzeppelin.com/contracts/3.x/#usage

I recommend importing OpenZeppelin Contracts rather than copying.

OpenZeppelin Contracts 2.x supports Solidity 0.5
OpenZeppelin Contracts 3.x supports Solidity 0.6 and 0.7
OpenZeppelin Contracts 4.0 (currently release candidate) supports Solidity 0.8

For creating a token today I would look at using OpenZeppelin Contracts 4.0 when it is released (depending on your timeline).

There isn't a lot of complexity in your token, other than minting at certain stages and being pausable.

Depending on your use case, you could instead mint tokens into TokenTimelock (https://docs.openzeppelin.com/contracts/3.x/api/token/erc20#TokenTimelock) contracts if locking for a specific period of time. Though you should consider addressing: Bypassing Smart Contract Timelocks

Metadata was merged into ERC20 in OpenZeppelin Contracts 3.x

For 2.5.1 we can use the release tag:

Unless you need Solidity 0.5 you could be using a later version of OpenZeppelin Contracts and Solidity.

Greetings @abcoathup Andrew,

Thanks for your kind and prompt answers.

I reviewed ERC20, extensions, presets and security and I still have a few concerns that I would like to fully address before I decide importing OpenZeppelin Contracts instead of typing it myself; could you please help me understand the following topics:

  1. Since I need to deploy my contracts ASAP, I would most likely be using OpenZeppelin 3.x. Do I install it with
$ npm i @openzeppelin/contracts@3.0.1

or is there a later 3.x version?

  1. The reason I have a constructor with minting stages is because I need to ensure that certain supplies exists at different times. Can I replace my logic by simply calling the 3.x _mint function when I require it with the amount I require it?

  2. I need to set a maximun supply of tokens that will ever be minted. Would the ERC20Capped extension be used for this purpose and how? I would like to set up a 1.000.000 Tokens as Maximun Supply. Could you please review my New Contract Proposal below. :point_down:t2:

  3. I must ensure that the contract creator is the ONLY address able to mint, pause and possibly burn. I do not quite recall if I modified the Ownable function or created the PauseRole on my existing contact, but this security concern is the UPMOST CRITICAL for me. Could you please clarify if with 3.x my security concern is addressed; that is, the contract creator is the ONLY address with such priviledges. Is my concern addressed with ERC20PresetMinterPauser. Could you please review my New Contract Proposal below. :point_down:t2:

  4. Is the _burn function automatically available when deplying ERC20 or is it only available with ERC20Burnable? Are they different things? What is the difference?

  5. TokenTimeLock is not a required concern for me since we are not granting tokens to our core team (which is rather quite small and trustworthy) ahead of time. However, the ERCPausable is a requirement not for enforcing vesting periods but rather, for two main reasons: a) as a security measure in case there is a hack on a CEX or a DEX (as seen recently); and b) to pause all trading during voting periods. Could you please review my New Contract Proposal below. :point_down:t2:

  6. I would like to mint an initial supply during deployment. In this case, I would like to mint 9.000 tokens. Could you please review my New Contract Proposal below. :point_down:t2:

  7. Since ERC20Detailed has been integrated into 3.x, I would like to set up my name, symbol and decimals as follows: FilmVault, FilmVault, 9. Could you please review my New Contract Proposal below. :point_down:t2:

  8. The reason I typed my own contract instead of importing was due to a flattenning issue in order to verify the contract. I just reviewed Verifying a contract inheriting from OpenZeppelin Contracts and Deployed Preset ERC20 using Remix and can't verify - #2 by abcoathup Could you please confirm the latest and greatest the Flattening Process at the end. :point_down:t2:

New Contact Proposal

pragma solidity ^0.7.0;

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

contract FilmVault_Token is ERC20, ERC20Capped, ERCPausable, ERC20PresetMinterPauser  {
    constructor (uint256 initialSupply, uint256 _cap) public ERC20 ("FilmVault", "FilmVault", 9) {
        _mint(msg.sender, 9000 * (10 ** uint256(decimals()))), cap (1000000 * (10 ** uint256(decimals()))) ;
    }
}

as supposed to

 **** ALL THE PREVIOUS TYPED CODE PLUS *****

contract FilmVault_Token is ERC20Pausable {

  using SafeERC20 for ERC20;
  address public creator;
  string public name;
  string public symbol;
  uint8 public decimals;
  uint256 public stage;
  uint256[13] private tokensToMint;

  constructor() public {

  creator = 0xbC57B9bb80DD02c882fcE8cf5700f8A2a003838E;
  name = "FilmVault";
  symbol = "FilmVault";
  decimals = 9;
  stage = 0;
  tokensToMint[0] = 9000000000000;
  tokensToMint[1] = 36000000000000;
  tokensToMint[2] = 100000000000000;
  tokensToMint[3] = 6000000000000;
  tokensToMint[4] = 49000000000000;
  tokensToMint[5] = 100000000000000;
  tokensToMint[6] = 100000000000000;
  tokensToMint[7] = 100000000000000;
  tokensToMint[8] = 100000000000000;
  tokensToMint[9] = 100000000000000;
  tokensToMint[10] = 100000000000000;
  tokensToMint[11] = 100000000000000;
  tokensToMint[12] = 100000000000000;
  }

  function mintFilmVault() public onlyOwner returns (bool) {
    require (stage <=12);
    _mint(creator, tokensToMint[stage]);
    stage = stage.add(1);
    return true;
  }
}

Flattening Process

$ npm init -y
$ npm i truffle
$ npx truffle init
$ npx truffle-flattener ./contracts/FilmVault_Token.sol > ./contracts/FlatFilmVault_Token.sol

You can install the latest release with

npm install @openzeppelin/contracts@solc-0.7

This will install OpenZeppelin Contracts 3.4.1 for Solidity 0.7

It really depends on your use case. Can you mint all of the tokens initially and then lock them, or do you need to mint at the predefined stages.

ERC20Capped does this by checking the cap before minting.

You can do this with Access Control, either Ownable or Roles: https://docs.openzeppelin.com/contracts/3.x/access-control

Using ERC20PresetMinterPauser the deployer is given the minter and pauser roles.

You also need to appropriately secure these admin accounts:

It is available if you extend from ERC20Burnable or add functionality to call _burn

I recommend multifile verification rather than flattening: How to verify with Hardhat or Truffle a smart contract using OpenZeppelin Contracts

I would use flattening as a last resort.


:warning: I am a Community Manager and not a Security Researcher.
Any contracts with value should be appropriately tested and audited.


You only need to extend from ERC20PresetMinterPauser and ERC20Capped
For custom (non-18 decimals) you can use _setupDecimals

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

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

contract MyToken is ERC20PresetMinterPauser, ERC20Capped {
    constructor (uint256 cap) ERC20PresetMinterPauser("MyToken", "TKN") ERC20Capped(cap) {
        _setupDecimals(9);
        _mint(msg.sender, 9000 * (10 ** uint256(decimals())));
    }
    
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauser, ERC20Capped) {
        super._beforeTokenTransfer(from, to, amount);
    }
}

As an aside, you can Format code in the forum.

Dear Andrew (@abcoathup),

Thanks so much for taking your time to answer my questions and for correcting my New Contract Proposal ; I am now conviced that it is the right time to call/import OpenZeppelin Contracts for my projects.

After reading Mr. Hadrien (@Amxx )´s comments within ERC20Capped: Immutable Variables cannot be read during contract creation time - #4 by abcoathup I learned that using when using ERC20capped it is better to do the minting after deployment, so I removed the mint command.

Therefore, the New Contract now looks like this :point_down:t2::

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

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

contract FilmVaultToken is ERC20PresetMinterPauser, ERC20Capped {
    constructor (uint256 cap) ERC20PresetMinterPauser("FilmVault", "FilmVault") ERC20Capped(cap) {
        _setupDecimals(9);
    }

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

However, while it compiled without errors, it did give me errors during deployment :point_down:t2:

I believe the issue has to do with setting the cap value. Would you or Mr. Hadrien @Amxx be so kind by helping me fix the script by setting the cap at 1.000.000 appropiately?

Thanks a lot for your support!

g.a.

1 Like

Gents @abcoathup and @Amxx ,

After reviewing Creating Capped ERC20 gives error in contract constructor I adjusted my New Contract Proposal as follows:

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
 
import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Capped.sol";

contract FilmVaultToken is ERC20PresetMinterPauser, ERC20Capped {
    constructor () ERC20PresetMinterPauser("FilmVault", "FilmVault") ERC20Capped(1000000 * (10 ** uint256(9))) public {
        _setupDecimals(9);
    }
 
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauser, ERC20Capped) {
        super._beforeTokenTransfer(from, to, amount);
    }
}

Which has been successfully deployed and verified as well as fully tested on Ropsten :point_down:t2:

Thank you so much guys for all your great work! We will soon be deploying our v2.0 projects on Mainnet by fully leveraging (importing) OpenZeppelin contracts.

g.a.

1 Like

A post was split to a new topic: Mintable ERC20 but not pausable

Greetings dear @abcoathup!

Would you or anyone be able to provide a guess estimate of how much gas in ETH would it cost to deploy my New Contract Proposal on Mainnet?

I have been trying to deploy it via Trufle and after several attemps, the prompt just sits after compiling :point_down:t2:

imagen_2021-03-18_040154

After waiting for an hour or so, I tried to deploy it via Remix, but I could not even get it compiled :roll_eyes: :point_down:t2:

I would get an invalid input source specified error, which I tend to believe that it is not reading the source file import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; when trying to import the OpenZeppelin contract.

Any thoughts or help with any of these two questions?

1 Like

Hi @jaureguino,

If you deploy to a public testnet and then you will see how much the gas is. Use a service like https://ethgasstation.info to see the current gas price to see the cost.

To deploy using Remix, convert the npm imports to GitHub imports, making sure that you specify a release tag.

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
 
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0-solc-0.7/contracts/presets/ERC20PresetMinterPauser.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0-solc-0.7/contracts/token/ERC20/ERC20Capped.sol";

contract FilmVaultToken is ERC20PresetMinterPauser, ERC20Capped {
    constructor () ERC20PresetMinterPauser("FilmVault", "FilmVault") ERC20Capped(1000000 * (10 ** uint256(9))) public {
        _setupDecimals(9);
    }
 
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauser, ERC20Capped) {
        super._beforeTokenTransfer(from, to, amount);
    }
}

I just deployed to Goerli and Gas Used by Transaction: 2,558,588.
You can try calculating the gas fee in Ether: https://www.ethgasstation.info/calculatorTxV.php

I am not sure why it is freezing on Truffle. I would try removing the build directory and try again.

1 Like

Dear @abcoathup,

Thank you so much. You have always been so kind and precise with your responses. I feel we have a much better idea on what is required in terms of gas and cost.

By the way, we have always been getting a Warning: Visibility for constructor is oignored. If you want the contract to be non-deployable, making it "abstract" is sufficient during compilation, :point_down:t2:

imagen_2021-03-19_100845

And we are also getting exactly the same error on ReMix

Would this be an issue or is there a way to resolve it?

Would it be safe to ignore this warning?

Once you let us know your recommendation on this issue, we will deploy our projects on Mainnet.

Cheers and all the best for you and OpenZeppelin. Keep you the great work!

g.a.

1 Like

Hi @jaureguino,

The warning says that visibility for the constructor is ignored, so you can just remove public from your constructor. It shouldn’t be an issue if you keep this in.

Dear @abcoathup ,

I hope you are doing well. We finally got ready to deploy our contracts on mainnet, but since OpenZeppelin Contracts were upgraded to 4.0. I decided to do a deployment test on rinkeby before proceeding and I came up with the following issues:

  1. I was able to deploy the original v3.4.0 contract on Rinkeby via ReMix .

See https://rinkeby.etherscan.io/address/0x4f39efa104a1aed071717e893c3b77faa25a1efa

  1. I attempted to verify it using guideline posted on How to verify with Hardhat or Truffle a smart contract using OpenZeppelin Contracts and I received the following error:

verifyerrors

  1. Since the compiled files were obviously missing from my directory structure I decided to compile the code via truffle

newerror

Therefore I have the following specific questions:

Q1. Would these issues have anything to do with OpenZeppelin recent upgrate to Contracts 4.0 (https://blog.openzeppelin.com/openzeppelin-contracts-4-0/) ?

Q2. Could you please provide the new and correct urls in order to compile and deploy these v3.4.0 contracts?

Q3. Do I have to change/deploy my current contract with the new 4.0 rationale and configure my deployment files to use the Solidity 0.8.0 compiler version?

1 Like

Hi @jaureguino,

I suggest you look at using OpenZeppelin Contracts 4.0 instead to see if this meets your needs.

Remix now supports @ imports so you can use the same imports on Remix and Truffle and Hardhat.

I thought Truffle Verify only verified contracts that were deployed using Truffle. If you want to verify a contract then you can use Hardhat. Verify smart contract inheriting from OpenZeppelin Contracts

Dear @abcoathup ,

Thanks for your prompt response. I will do as you suggests. However, it is not clear to me how to override the decimals function within the constructor

Moreover, I tried to follow the directions noted https://blog.openzeppelin.com/openzeppelin-contracts-4-0/

So I proceeded as follows:

npminstall

Nonetheless, when I view the ERC20.sol file in the directory, it continues to show the older version.

So I proceeded to compile the new contract via ReMix as follows:

and it would not compile due to the following errors:

TypeError: Function needs to specify overridden contract "ERC20". --> contracts/SpecialVaultToken.sol:12:70: | 12 | function _mint(address account, uint256 amount) internal virtual override(ERC20PresetMinterPauser, ERC20Capped) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Note: This contract: --> https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol:33:1: | 33 | contract ERC20 is Context, IERC20, IERC20Metadata { | ^ (Relevant source part starts here and spans across multiple lines).

TypeError: Invalid contract specified in override list: "ERC20PresetMinterPauser". --> contracts/SpecialVaultToken.sol:12:70: | 12 | function _mint(address account, uint256 amount) internal virtual override(ERC20PresetMinterPauser, ERC20Capped) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Note: This contract: --> https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol:25:1: | 25 | contract ERC20PresetMinterPauser is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable { | ^ (Relevant source part starts here and spans across multiple lines).

TypeError: Derived contract must override function "_beforeTokenTransfer". Two or more base classes define function with same name and parameter types. --> contracts/SpecialVaultToken.sol:7:1: | 7 | contract SpecialVaultToken is ERC20PresetMinterPauser, ERC20Capped { | ^ (Relevant source part starts here and spans across multiple lines). Note: Definition in "ERC20": --> https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol:303:5: | 303 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Note: Definition in "ERC20PresetMinterPauser": --> https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol:84:5: | 84 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20, ERC20Pausable) { | ^ (Relevant source part starts here and spans across multiple lines).

TypeError: Wrong argument count for function call: 1 arguments given but expected 0. --> contracts/SpecialVaultToken.sol:9:9: | 9 | decimals(9); | ^^^^^^^^^^^

Would you please provide some guidance on the above issues?

1 Like

Hi @jaureguino,

In OpenZeppelin Contracts 4 the repository organization has changed, so some of the contracts are in new locations.

To set decimals to a value other than 18, we need to override the function (see: https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#ERC20-decimals--):

    function decimals() public view virtual override returns (uint8) {
        return 9;
    }

:warning: The following haven’t been appropriately tested or audited. You should appropriately test and audit before using in production and check that they meet your needs.

You can try these out on Remix as Remix now supports @ imports.

Extending from ERC20PresetMinterPauser:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
 
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";

contract FilmVaultToken is ERC20PresetMinterPauser, ERC20Capped {
    constructor() ERC20PresetMinterPauser("FilmVault", "FilmVault") ERC20Capped(1000000 * (10 ** uint256(decimals()))) {
    }
    
    function decimals() public view virtual override returns (uint8) {
        return 9;
    }

    function _mint(address account, uint256 amount) internal virtual override(ERC20, ERC20Capped) {
        super._mint(account, amount);
    }
 
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20, ERC20PresetMinterPauser) {
        super._beforeTokenTransfer(from, to, amount);
    }
}

Alternatively extending from ERC20:

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract FilmVault is ERC20, ERC20Burnable, ERC20Capped, Pausable, AccessControl {
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() ERC20("FilmVault", "FV") ERC20Capped(1000000 * (10 ** uint256(decimals()))) {
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _setupRole(PAUSER_ROLE, msg.sender);
        _setupRole(MINTER_ROLE, msg.sender);
    }
    
    function decimals() public view virtual override returns (uint8) {
        return 9;
    }

    function _mint(address account, uint256 amount) internal virtual override(ERC20, ERC20Capped) {
        super._mint(account, amount);
    }

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

    function pause() public {
        require(hasRole(PAUSER_ROLE, msg.sender));
        _pause();
    }

    function unpause() public {
        require(hasRole(PAUSER_ROLE, msg.sender));
        _unpause();
    }

    function mint(address to, uint256 amount) public {
        require(hasRole(MINTER_ROLE, msg.sender));
        _mint(to, amount);
    }
}

Dear @abcoathup,

Thanks a lot for your support. I was sucessfull at deploying our NewVaultToken.sol 4.0 contract via Remix on rinkeby as well as I was able to install and configure hardhat.

However, I think the issue I keep encountering is with the deployment or installation of the OpenZeppelin 4.0 node module.

Please see the actions taken:

It apparently installs it, but when trying to import the OpenZeppelin 4.0 Smart Contracts, it does not find the contracts.

Please note that hardhat does not accept imports using https

So when I checked my file system, after “installing 4.0”, I cannot find them either. I only see the previous version.

Please advise.

1 Like

Hi @jaureguino,

For Hardhat we need to use npm imports and we can’t use GitHub imports.

We can now use @ (npm style) imports with Remix. So I recommend using @ imports to deploy and verify.

In Remix we can find the dependencies in a .deps folder. See:

Dear @abcoathup,

Thanks for your prompt response.

However, it seems I have not been clear with the issue at hand. So, allow me to reframe it.

npm install @openzeppelin/contracts IS NOT WORKING, as it does not deploy 4.0 Smart Contracts on the node_modules directory.

After executing three times the npm install @openzeppelin/contracts command , I continue to see the old file structure and the old files.

Because I believe this is a critical issue, given that @OpenZeppelinTeam has officially announced the release of 4.0 Smart Contracts over a week ago, I will also post concern separately.

1 Like

Have you tried deleting node_modules and reinstalling all dependencies with npm i ? Also, can you post your package.json ?

1 Like

Dear @Amxx ,

Thanks for reaching out. The 4.0 Smart Contract install issue has been fixed thanks to @abcoathup who advised me earlier on a separate ticket by performing:

npx uninstall @openzeppeling/contracts
npx install @openzeppeling/contracts

see :point_down:t2:

Then when trying to verify the Previous Contract via hardhat, it was giving me compiling errors with 0.8.3 Solidity, the error literally stated that it did not fully support that version.

Hence I decided to downgrade the solidity versión to 0.8.1 within the code as well as within the hardhat.config.js and it compiled perfectly.

Therefore, I deployed a New Contract via Remix on Rinkeby See :point_down:t2:https://rinkeby.etherscan.io/address/0x94bc0a2916f06b56a330ea57154fe65a6a80ed42

and verified it via hardhat successfully. I have just tested the required functions (mint, pause, renounceRole, transfer, etc) inherited from:

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

and they have successfully worked as expected.

I am ready to go to mainnet. Once deployed, I will update and give closure to this issue.

1 Like