Verify a contract extending OpenZeppelin Contracts with a constructor deployed from a factory?

I’m using Etherscan multi-file verification but I keep on getting error “File import callback not supported” even though I made sure that all openzeppelin files were uploaded.

Here’s the smart contract:

pragma solidity >=0.4.25 <0.7.0;

// import './VerificationList.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/utils/Pausable.sol';
import '@openzeppelin/contracts/access/Ownable.sol';

contract CreateToken is ERC20, Ownable, Pausable {
    uint256 _totalSupply;
    uint256 public blackListCount;
    uint256 public whiteListCount;
 
    // Mapping of token addresses to vesting deadline
    // mapping(address => uint256) public vestingDeadline;
 
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        address _owner,
        uint256 _supply
    ) public ERC20(_name, _symbol) {
        // _totalSupply = _supply.mul(10**_decimals);
        _setupDecimals(_decimals);
        _mint(_owner, _supply);
        transferOwnership(_owner);
        blackListCount = 0;
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     * @param recipient The wallet address of the investor receiving the tokens
     * @param amount The amount of tokens being sent
     */
    function transfer(address recipient, uint256 amount) public virtual override whenNotPaused() returns (bool) {
        _beforeTokenTransfer(msg.sender, recipient, amount);
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused and must be called by the owner.
     */
    function pause() public onlyOwner() {
        _pause();
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused and must be called by the owner.
     */
    function unpause() public onlyOwner() {
        _unpause();
    }

    /**
     * @dev Burns a given number of tokens
     * @param amount The number of tokens to burn
     */
    function burn(uint256 amount) public {
        _burn(msg.sender, amount);
    }

    /**
     * @dev Mints a given number of tokens
     * @param amount The number of tokens to burn
     */
    function mint(uint256 amount) public onlyOwner() {
        _mint(msg.sender, amount);
    }

    /**
    @dev List of checks before token transfers are allowed
    @param from The wallet address sending the tokens
    @param to The wallet address recieving the tokens
    @param amount The amount of tokens being transfered
   */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        require(!paused(), 'ERC20Pausable: token transfer while paused');
        require(!isBlackListed[to], 'Recipient not allowed');
        require(amount > 0, 'Amount must not be 0');
        // if (msg.sender != owner()) {
        //     require(vestingDeadline[from] >= block.timestamp, 'Lock in period has not passed');
        // }
    }

    ///mapping of the addresses if it is blacklisted or not
    mapping(address => bool) public isBlackListed;

    ///mapping of the addresses if it is whitelisted or not
    mapping(address => bool) public isWhiteListed;

    /***
     * @notice Check of an address is blacklisted
     * @param _address Address to be checked if blacklisted
     * @return Whether the address passed is blacklisted or not
     */
    function getBlackListStatus(address _address) public view onlyOwner returns (bool) {
        return isBlackListed[_address];
    }

    /***
     * @notice Check of an address is whitelisted
     * @param _address Address to be checked if whitelisted
     * @return Whether the address passed is whitelisted or not
     */
    function getWhiteListStatus(address _address) public view onlyOwner returns (bool) {
        return isWhiteListed[_address];
    }

    /***
     * @notice Add an address to the blacklist
     * @param _address The address to be added to the blacklist
     */
    function addBlackList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            require(_address[i] != address(0), 'The address is address 0');
            require(_address[i] != owner(), 'The address is the owner');
            if (!isBlackListed[_address[i]]) {
                isBlackListed[_address[i]] = true;
                blackListCount++;
                emit AddedBlackList(_address[i]);
            }
            if (isWhiteListed[_address[i]]) {
                isWhiteListed[_address[i]] = false;
                emit RemovedWhiteList(_address[i]);
            }
        }
    }

    /***
     * @notice Remove an address from the blacklist
     * @param _address The address to be removed from the blacklist
     */
    function removeBlackList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            if (isBlackListed[_address[i]]) {
                isBlackListed[_address[i]] = false;
                blackListCount--;
                emit RemovedBlackList(_address[i]);
            }
        }
    }

    /***
     * @notice Add an address to the whitelist
     * @param _address The address to be added to the whitelist
     */
    function addWhiteList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            if (!isBlackListed[_address[i]]) {
                isWhiteListed[_address[i]] = true;
                whiteListCount++;
                emit AddedWhiteList(_address[i]);
            }
        }
    }

    /***
      * @notice Remove an address from the whitelist
      * @param _address The address to be removed from the whitelist
      */
     function removeWhiteList(address[] memory _address) public onlyOwner {
         for (uint256 i = 0; i < _address.length; i++) {
             if (isWhiteListed[_address[i]]) {
                 isWhiteListed[_address[i]] = false;
                 whiteListCount--;
                 emit RemovedWhiteList(_address[i]);
             }
         }
     }
 
     // event DestroyedBlackFunds(address _blackListedUser, uint256 _balance);
 
     event AddedBlackList(address _address);
 
     event RemovedBlackList(address _address);
 
     event AddedWhiteList(address _address);
 
     event RemovedWhiteList(address _address);
 }

This contract is being called by another smart contract with a function of CreateToken then passing the parameters for the constructor.

:computer: Environment
Truffle v5.1.52 (core: 5.1.52)
Solidity - 0.6.2 (solc-js)
Node v12.18.4
Web3.js v1.2.9

:1234: Code to reproduce
The error occurs when verifying the Smart Contract using Multi-part option of Etherscan.

Thanks and Advance

1 Like

Hi @Benjamin_P,

Welcome to the community forum :wave:

Have you tried the following using Hardhat (As using Truffle requires the contract to be deployed with Truffle)?
Verify smart contract inheriting from OpenZeppelin Contracts

I tried using Hardhat but got this instead

[Error: ENOENT: no such file or directory, open ‘/UserDir/artifacts/build-info/dfcbbd42275ba6340580f1a1297d200a.json’] {
errno: -2,
code: ‘ENOENT’,
syscall: ‘open’,
path: ‘/UserDir/artifacts/build-info/dfcbbd42275ba6340580f1a1297d200a.json’
}

May I know what is causing this?

1 Like

HI @Benjamin_P,

I haven’t seen that error before. I generally create a new project for Hardhat and copy my contract over to verify and follow the process in: Verify smart contract inheriting from OpenZeppelin Contracts.

Is there a way for it to be verified successfully via Etherscan?

1 Like

Hi @Benjamin_P,

Yes, it should be.

As long as you know:

  • address
  • Solidity compiler version (you can also get this from the bytecode)
  • if optimization was used
  • version of OpenZeppelin Contracts
  • constructor parameters

If you get stuck, feel free to post the above and I can try verifying for you.

If my Constructor is like this below:

constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        address _owner,
        uint256 _supply
    ) public ERC20(_name, _symbol) {
        _setupDecimals(_decimals);
        _mint(_owner, _supply);
        transferOwnership(_owner);
    }

How do I enter that in the Ethercan verification page? It says it requires ABI-encoded but I’m not sure how to get it.

Thanks

1 Like

Hi @Benjamin_P,

You can encode your constructor parameters using the following:
https://abi.hashex.org/

As an aside, this is how to Format code in the forum

1 Like

Thanks. I’ll let you know if I’ll have more issues.

1 Like

Hi,

I’m getting this error instead.

ERC20.sol:5:1: ParserError: Source "GSN/Context.sol" not found: File import callback not supported
import "../../GSN/Context.sol"
^-----------------------------^
ERC20.sol:7:1: ParserError: Source "math/SafeMath.sol" not found: File import callback not supported
import "../../math/SafeMath.sol"
^-------------------------------^
Ownable.sol:5:1: ParserError: Source "GSN/Context.sol" not found: File import callback not supported
import "../GSN/Context.sol"
^--------------------------^
Pausable.sol:5:1: ParserError: Source "GSN/Context.sol" not found: File import callback not supported
import "../GSN/Context.sol"
^--------------------------^
Tokenize.sol:4:1: ParserError: Source "@openzeppelin/contracts/token/ERC20/ERC20.sol" not found: File import callback not supported
import '@openzeppelin/contracts/token/ERC20/ERC20.sol'
^-----------------------------------------------------^
Tokenize.sol:5:1: ParserError: Source "@openzeppelin/contracts/utils/Pausable.sol" not found: File import callback not supported
import '@openzeppelin/contracts/utils/Pausable.sol'
^--------------------------------------------------^
Tokenize.sol:6:1: ParserError: Source "@openzeppelin/contracts/access/Ownable.sol" not found: File import callback not supported
import '@openzeppelin/contracts/access/Ownable.sol'
^--------------------------------------------------^

I uploaded the files as shown in the screenshot below.

Is there something wrong with the uploaded files?

1 Like

Hi @Benjamin_P,

If you are uploading each file then you would need to change the imports to all have the contracts in the same directory.

If you tried to compile the contracts as they currently are it would fail with the same error.

@PaulRBerg created https://github.com/paulrberg/multisol which does this (though I haven’t tried it).

I recommend the following process using Hardhat: Verify smart contract inheriting from OpenZeppelin Contracts

Your alternative is to flatten the contracts (removing imports and license information, you need to ensure that you use the appropriate license).

1 Like

Ok, I tried to change the import to make them all in 1 folder via import ‘./ERC20.sol’ but got an error like this

We tried looking for a match from the list of compiled contract bytecode outputs (as listed below), but was unable to find an exact match.

  1. ERC20.sol : ERC20
    60806040523480156200001157600080fd5b50604051620013bb380380620…

  2. SafeMath.sol : SafeMath
    60566023600b8282823980516…

I tried the hardhat process but I’m getting the error
[Error: ENOENT: no such file or directory, open ‘/userfolder/artifacts/build-info/dfcbbd42275ba6340580f1a1297d200a.json’] {
errno: -2,
code: ‘ENOENT’,
syscall: ‘open’,
path: ‘/userfolder/artifacts/build-info/dfcbbd42275ba6340580f1a1297d200a.json’
}

For the License information, where can I see this? I only chose in Etherscan the No License option.

Thanks for the shill, Andrew!

@Benjamin_P, Multisol is the solution to the problem you're facing here. It's a CLI tool that you can install via Homebrew (if on Mac), Cargo (needs Rust installed on your OS) or by downloading the binary directly.

After you install it, you run this:

multisol contracts/CreateToken.sol

And a multisol-createtoken folder will be generated for you. You will then be able to upload all the contracts in there in the Etherscan UI (after selecting "Multi-part" in the compiler type dropdown).

1 Like

Thanks for this. I’ll give it a try.

1 Like

Unfortunately I’m still getting the bytecode error:

We tried looking for a match from the list of compiled contract bytecode outputs (as listed below), but was unable to find an exact match.

  1. ERC20.sol : ERC20

  2. SafeMath.sol : SafeMath

This is despite the files are being uploaded from the Multisol folder.

1 Like

Hi @Benjamin_P,

Do you want to post what version of OpenZeppelin Contracts you are using, whether you used optimization, the address on the network and what constructor parameters were used and I can have a try?

I checked the package.json in the openzeppelin folder in the node_modules and it says it uses 3.3.0

Contract:

Code:

pragma solidity >=0.4.25 <0.7.0;

import './ERC20.sol';
import './Pausable.sol';
import './Ownable.sol';

contract CreateToken is ERC20, Ownable, Pausable {
    uint256 _totalSupply;
    uint256 public blackListCount;
    uint256 public whiteListCount;

    // Mapping of token addresses to vesting deadline
    // mapping(address => uint256) public vestingDeadline;

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        address _owner,
        uint256 _supply
    ) public ERC20(_name, _symbol) {
        // _totalSupply = _supply.mul(10**_decimals);
        _setupDecimals(_decimals);
        _mint(_owner, _supply);
        transferOwnership(_owner);
        blackListCount = 0;
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     * @param recipient The wallet address of the investor receiving the tokens
     * @param amount The amount of tokens being sent
     */
    function transfer(address recipient, uint256 amount) public virtual override whenNotPaused() returns (bool) {
        _beforeTokenTransfer(msg.sender, recipient, amount);
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused and must be called by the owner.
     */
    function pause() public onlyOwner() {
        _pause();
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused and must be called by the owner.
     */
    function unpause() public onlyOwner() {
        _unpause();
    }

    /**
     * @dev Burns a given number of tokens
     * @param amount The number of tokens to burn
     */
    function burn(uint256 amount) public {
        _burn(msg.sender, amount);
    }

    /**
     * @dev Mints a given number of tokens
     * @param amount The number of tokens to burn
     */
    function mint(uint256 amount) public onlyOwner() {
        _mint(msg.sender, amount);
    }

    /**
    @dev List of checks before token transfers are allowed
    @param from The wallet address sending the tokens
    @param to The wallet address recieving the tokens
    @param amount The amount of tokens being transfered
   */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        require(!paused(), 'ERC20Pausable: token transfer while paused');
        require(!isBlackListed[to], 'Recipient not allowed');
        require(amount > 0, 'Amount must not be 0');
        // if (msg.sender != owner()) {
        //     require(vestingDeadline[from] >= block.timestamp, 'Lock in period has not passed');
        // }
    }

    ///mapping of the addresses if it is blacklisted or not
    mapping(address => bool) public isBlackListed;

    ///mapping of the addresses if it is whitelisted or not
    mapping(address => bool) public isWhiteListed;

    /***
     * @notice Check of an address is blacklisted
     * @param _address Address to be checked if blacklisted
     * @return Whether the address passed is blacklisted or not
     */
    function getBlackListStatus(address _address) public view onlyOwner returns (bool) {
        return isBlackListed[_address];
    }

    /***
     * @notice Check of an address is whitelisted
     * @param _address Address to be checked if whitelisted
     * @return Whether the address passed is whitelisted or not
     */
    function getWhiteListStatus(address _address) public view onlyOwner returns (bool) {
        return isWhiteListed[_address];
    }

    /***
     * @notice Add an address to the blacklist
     * @param _address The address to be added to the blacklist
     */
    function addBlackList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            require(_address[i] != address(0), 'The address is address 0');
            require(_address[i] != owner(), 'The address is the owner');
            if (!isBlackListed[_address[i]]) {
                isBlackListed[_address[i]] = true;
                blackListCount++;
                emit AddedBlackList(_address[i]);
            }
            if (isWhiteListed[_address[i]]) {
                isWhiteListed[_address[i]] = false;
                emit RemovedWhiteList(_address[i]);
            }
        }
    }

    /***
     * @notice Remove an address from the blacklist
     * @param _address The address to be removed from the blacklist
     */
    function removeBlackList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            if (isBlackListed[_address[i]]) {
                isBlackListed[_address[i]] = false;
                blackListCount--;
                emit RemovedBlackList(_address[i]);
            }
        }
    }

    /***
     * @notice Add an address to the whitelist
     * @param _address The address to be added to the whitelist
     */
    function addWhiteList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            if (!isBlackListed[_address[i]]) {
                isWhiteListed[_address[i]] = true;
                whiteListCount++;
                emit AddedWhiteList(_address[i]);
            }
        }
    }

    /***
     * @notice Remove an address from the whitelist
     * @param _address The address to be removed from the whitelist
     */
    function removeWhiteList(address[] memory _address) public onlyOwner {
        for (uint256 i = 0; i < _address.length; i++) {
            if (isWhiteListed[_address[i]]) {
                isWhiteListed[_address[i]] = false;
                whiteListCount--;
                emit RemovedWhiteList(_address[i]);
            }
        }
    }

    // event DestroyedBlackFunds(address _blackListedUser, uint256 _balance);

    event AddedBlackList(address _address);

    event RemovedBlackList(address _address);

    event AddedWhiteList(address _address);

    event RemovedWhiteList(address _address);
}

I used Multisol to generate a folder containing all the contracts being called.

Here’s the ABI-encoded from abi-hashex for the constructor:
00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000700000000000000000000000016ede61a09835d35e60d92ae0f11cf148ce262bf00000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000442656e6a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000342454e0000000000000000000000000000000000000000000000000000000000

Hope it works for you.

Thanks

1 Like

Hi @Benjamin_P,

I wasn’t able to verify your contract. Was it deployed with optimization? You can check the compiler settings in truffle-config.js.

I got the following error when trying to verify:

$ npx hardhat verify --network kovan --constructor-args arguments.js 0xc6
02812189Bd13a1B4847753FFd33BcE6e09A136
Nothing to compile
Error in plugin @nomiclabs/hardhat-etherscan: The address provided as argument contains a contract, but its bytecode doesn't match any of your local contracts.

Possible causes are:
  - Contract code changed after the deployment was executed. This includes code for seemingly unrelated contracts.
  - A solidity file was added, moved, deleted or renamed after the deployment was executed. This includes files for seemingly unrelated contracts.
  - Solidity compiler settings were modified after the deployment was executed (like the optimizer, target EVM, etc.).
  - The given address is wrong.
  - The selected network (kovan) is wrong.

For more info run Hardhat with --show-stack-traces

I tried deploying your token as a standalone contract.

$ npx hardhat run --network kovan scripts/deploy.js
Deploying Token...
CreateToken deployed to: 0xcF94c72f615D54E97C6dCCB6262C5Bf69F3F4FF3

I was then able to verify:

$ npx hardhat verify --network kovan --constructor-args arguments.js 0xcF94c72f615D54E97C6dCCB6262C5Bf69F3F4FF3
Nothing to compile
Compiling 1 file with 0.6.2
contracts/CreateToken.sol:91:9: Warning: Unused function parameter. Remove or comment out the variable name to silence this warning.
        address from,
        ^----------^

Successfully submitted source code for contract
contracts/CreateToken.sol:CreateToken at 0xcF94c72f615D54E97C6dCCB6262C5Bf69F3F4FF3
for verification on Etherscan. Waiting for verification result...
Successfully verified contract CreateToken on Etherscan
// arguments.js
module.exports = [
    "Benj",
    "BEN",
    "7",
    "0x16EDE61a09835D35e60D92AE0F11CF148cE262bF",
    "1000000000000"
  ];

Here’s the truffle config

compilers: {
    solc: {
      version: "0.6.2",    // Fetch exact version from solc-bin (default: truffle's version)
      //  docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)
      settings: {          // See the solidity docs for advice about optimization and evmVersion
        optimizer: {
          enabled: true,
          runs: 200
        },
        evmVersion: "byzantium"
      }
    }
  },

I just copied it, but I also tried the optimization true in etherscan options as well as Byzantium but it didn’t work. I also tried removing those options in truffle config to make things as default but to no avail. I tried in remix manually inputting the data for the constructor then used a plugin for etherscan verification and it worked. What I was doing is creating a parent contract first then this parent contract calls this createtoken.sol function to create the token using the data inputed to that parent contract. I guess that’s causing the problem because when I called the createtoken.sol without the parent contract, the verification works just like what you did when you called it independently. However I tried deploying it in truffle and still wasnt able to get verified so I’ll give hardhat a try in deployment.

1 Like

Hi @Benjamin_P,

Unless there is a specific need, I would recommend using the default EVM version in your compiler settings, e.g. don’t specify an evmversion.

I wasn’t aware that there was anything special to do for verifying a factory created contract.