Gas estimation failed for mint in ERC721 on Remix

:computer: Environment

:memo:Details
< Throwing gas estimation error while calling mint function from erc721 >

:1234: Code to reproduce

function mint(
        address to,
        uint256 weaponType,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    ) public returns (uint256 tokenId) {
        require(hasRole(MINT_ROLE, _msgSender()), 'Must have mint role');
        tokenId = nextTokenId;
        _mint(to, tokenId);
        nextTokenId++;
        uint256 level = getLevel(stakingPower);
        weaponInfoMap[tokenId] = WeaponInfo({
            weaponType: weaponType,
            stakingPower: stakingPower,
            damagePower: damagePower,
            level: level,
            cifiToGet: cifiToGet,
            ethToGet: ethToGet
        });
        _sync(cifiToGet, ethToGet, true);
        emit Mint(to, tokenId, weaponType, stakingPower, damagePower, level, cifiToGet, ethToGet);
1 Like

@abcoathup @Skyge could you please help with above?

Hi @senavi,

Does the account that you are calling mint from have the MINTER_ROLE?
How are you testing this?

Are you able to share the contract?

Hi @abcoathup yes the account from i am calling is having the MINT_ROLE

1 Like

Hi @abcoathup here is the contract

pragma solidity =0.6.6;
pragma experimental ABIEncoderV2;
contract GunNFT is ERC721Pausable, AccessControl, Ownable {
    using SafeERC20 for IERC20;

    struct WeaponInfo {
        uint256 weaponType;
        uint256 stakingPower;
        uint256 damagePower;
        uint256 level;
        uint256 cifiToGet;
        uint256 ethToGet;
    }

    bytes32 public constant MINT_ROLE = keccak256('MINT_ROLE');

    uint256 public nextTokenId = 1;
    uint256 public cifiReserve;
    uint256 public ethReserve;

    address public immutable CIFI;
    address public immutable ETH;

    mapping(uint256 => WeaponInfo) public weaponInfoMap;

    event Mint(
        address indexed user,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Burn(
        address indexed user,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Update(
        address indexed sender,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Sync(uint256 cifiReserve, uint256 ethReserve);

    constructor(address _CIFI, address _ETH) public ERC721('Citizen.Fi', 'GUNNFT') {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        CIFI = _CIFI;
        ETH = _ETH;
    }

    function getLevel(uint256 stakingPower) public pure returns (uint256 level) {
        level = 1; // < 1k
        if (stakingPower >= 1E3 && stakingPower < 2E3) {
            level = 2;
        } else if (stakingPower >= 2E3 && stakingPower < 5E3) {
            level = 3;
        } else if (stakingPower >= 5E3 && stakingPower < 1E4) {
            level = 4;
        } else if (stakingPower >= 1E4 && stakingPower < 2E4) {
            level = 5;
        } else if (stakingPower >= 2E4 && stakingPower < 5E4) {
            level = 6;
        } else if (stakingPower >= 5E4 && stakingPower < 1E5) {
            level = 7;
        } else if (stakingPower >= 1E5 && stakingPower < 5E5) {
            level = 8;
        } else if (stakingPower >= 5E5 && stakingPower < 1E6) {
            level = 9;
        } else if (stakingPower >= 1E6) {
            level = 10;
        }
        
    
    }

    function _sync(
        uint256 pendingCifiToGet,
        uint256 pendingEthToGet,
        bool isAdd
    ) private {
        uint256 balanceOfCifi = IERC20(CIFI).balanceOf(address(this));
        uint256 balanceOfEth = IERC20(ETH).balanceOf(address(this));
        require(
            pendingCifiToGet == 0 ||
                (
                    isAdd
                        ? balanceOfCifi.sub(cifiReserve) == pendingCifiToGet
                        : balanceOfCifi.add(pendingCifiToGet) == cifiReserve
                ),
            'Error cifi amount'
        );
        require(
            pendingEthToGet == 0 ||
                (
                    isAdd
                        ? balanceOfEth.sub(ethReserve) == pendingEthToGet
                        : balanceOfEth.add(pendingEthToGet) == ethReserve
                ),
            'Error eth amount'
        );
        if (cifiReserve != balanceOfCifi) {
            cifiReserve = balanceOfCifi;
        }
        if (ethReserve != balanceOfEth) {
            ethReserve = balanceOfEth;
        }
        emit Sync(cifiReserve, ethReserve);
    }

    function mint(
        address to,
        uint256 weaponType,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    ) public returns (uint256 tokenId) {
        require(hasRole(MINT_ROLE, _msgSender()), 'Must have mint role');
        tokenId = nextTokenId;
        _mint(to, tokenId);
        nextTokenId++;
        uint256 level = getLevel(stakingPower);
        weaponInfoMap[tokenId] = WeaponInfo({
            weaponType: weaponType,
            stakingPower: stakingPower,
            damagePower: damagePower,
            level: level,
            cifiToGet: cifiToGet,
            ethToGet: ethToGet
        });
      //  _sync(cifiToGet, ethToGet, true);
        emit Mint(to, tokenId, weaponType, stakingPower, damagePower, level, cifiToGet, ethToGet);
    }

   
}
1 Like

Hi @senavi,

You are currently setting the deployer of the contract to be the default admin but haven’t set any account to have the MINT_ROLE.

So you would need to call grantRole from the account that deployed the contract to grant the MINT_ROLE (if calling externally you need to use the getter MINT_ROLE()) to an account who can then mint.

See the documentation for details: https://docs.openzeppelin.com/contracts/3.x/access-control#granting-and-revoking

Hi @abcoathup yes i called the grantRole function to set the MINT_ROLE by passing the same value in form of bytes32 and then i called the mint function. but still got same gas estimation error

1 Like

@abcoathup could you please try at your end once… i am not sure if sync function is causing any issue here

1 Like

Hi @senavi,

I was able to mint, see my manual check in Truffle Develop.

You need to grant an account the MINT_ROLE and then you should be able to mint.

$ npx truffle develop
...
truffle(develop)> compile

Compiling your contracts...
===========================
> Compiling ./contracts/GunNFT.sol
> Artifacts written to /home/abcoathup/projects/forum/senavi/build/contracts
> Compiled successfully using:
   - solc: 0.6.6+commit.6c089d02.Emscripten.clang

truffle(develop)> token = await GunNFT.new(accounts[8], accounts[9])
undefined
truffle(develop)> await token.grantRole(await token.MINT_ROLE(), accounts[1])
{ tx:
...
truffle(develop)> await token.hasRole(await token.MINT_ROLE(), accounts[1])
true
truffle(develop)> await token.mint(accounts[2], 1, 2, 3, 4, 5, {from:accounts[1]})
{ tx:
...
truffle(develop)> await token.ownerOf(1)
'0xfF8DdFD4aE8ED56c4b94738fEF931b732F3AaEb5'
truffle(develop)> accounts[2]
'0xfF8DdFD4aE8ED56c4b94738fEF931b732F3AaEb5'

I added the imports from OpenZeppelin Contracts

pragma solidity =0.6.6;
//pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC721/ERC721Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";

contract GunNFT is ERC721Pausable, AccessControl, Ownable {
    using SafeERC20 for IERC20;

    struct WeaponInfo {
        uint256 weaponType;
        uint256 stakingPower;
        uint256 damagePower;
        uint256 level;
        uint256 cifiToGet;
        uint256 ethToGet;
    }

    bytes32 public constant MINT_ROLE = keccak256('MINT_ROLE');

    uint256 public nextTokenId = 1;
    uint256 public cifiReserve;
    uint256 public ethReserve;

    address public immutable CIFI;
    address public immutable ETH;

    mapping(uint256 => WeaponInfo) public weaponInfoMap;

    event Mint(
        address indexed user,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Burn(
        address indexed user,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Update(
        address indexed sender,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Sync(uint256 cifiReserve, uint256 ethReserve);

    constructor(address _CIFI, address _ETH) public ERC721('Citizen.Fi', 'GUNNFT') {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        CIFI = _CIFI;
        ETH = _ETH;
    }

    function getLevel(uint256 stakingPower) public pure returns (uint256 level) {
        level = 1; // < 1k
        if (stakingPower >= 1E3 && stakingPower < 2E3) {
            level = 2;
        } else if (stakingPower >= 2E3 && stakingPower < 5E3) {
            level = 3;
        } else if (stakingPower >= 5E3 && stakingPower < 1E4) {
            level = 4;
        } else if (stakingPower >= 1E4 && stakingPower < 2E4) {
            level = 5;
        } else if (stakingPower >= 2E4 && stakingPower < 5E4) {
            level = 6;
        } else if (stakingPower >= 5E4 && stakingPower < 1E5) {
            level = 7;
        } else if (stakingPower >= 1E5 && stakingPower < 5E5) {
            level = 8;
        } else if (stakingPower >= 5E5 && stakingPower < 1E6) {
            level = 9;
        } else if (stakingPower >= 1E6) {
            level = 10;
        }
        
    
    }

    function _sync(
        uint256 pendingCifiToGet,
        uint256 pendingEthToGet,
        bool isAdd
    ) private {
        uint256 balanceOfCifi = IERC20(CIFI).balanceOf(address(this));
        uint256 balanceOfEth = IERC20(ETH).balanceOf(address(this));
        require(
            pendingCifiToGet == 0 ||
                (
                    isAdd
                        ? balanceOfCifi.sub(cifiReserve) == pendingCifiToGet
                        : balanceOfCifi.add(pendingCifiToGet) == cifiReserve
                ),
            'Error cifi amount'
        );
        require(
            pendingEthToGet == 0 ||
                (
                    isAdd
                        ? balanceOfEth.sub(ethReserve) == pendingEthToGet
                        : balanceOfEth.add(pendingEthToGet) == ethReserve
                ),
            'Error eth amount'
        );
        if (cifiReserve != balanceOfCifi) {
            cifiReserve = balanceOfCifi;
        }
        if (ethReserve != balanceOfEth) {
            ethReserve = balanceOfEth;
        }
        emit Sync(cifiReserve, ethReserve);
    }

    function mint(
        address to,
        uint256 weaponType,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    ) public returns (uint256 tokenId) {
        require(hasRole(MINT_ROLE, _msgSender()), 'Must have mint role');
        tokenId = nextTokenId;
        _mint(to, tokenId);
        nextTokenId++;
        uint256 level = getLevel(stakingPower);
        weaponInfoMap[tokenId] = WeaponInfo({
            weaponType: weaponType,
            stakingPower: stakingPower,
            damagePower: damagePower,
            level: level,
            cifiToGet: cifiToGet,
            ethToGet: ethToGet
        });
      //  _sync(cifiToGet, ethToGet, true);
        emit Mint(to, tokenId, weaponType, stakingPower, damagePower, level, cifiToGet, ethToGet);
    }
 
}

Thanks @abcoathup , i was able to mint after commenting the sync function … i am using remix … but with sync function still i am getting gas estimation error … even after granting MINT_ROLE to account.

Could you please check your same contract in remix once …just to confirm?

1 Like

Hi @senavi,

I changed the imports from npm imports to GitHub imports and was able to deploy the contract with similar dummy data, grant the MINT_ROLE using grantRole and then mint with _sync commented out.

Given that the issue isn’t the minting but rather the _sync function, I recommend just trying to get that working on its own. You may want to move to Truffle or Hardhat so that you can write unit tests to test this functionality.

pragma solidity =0.6.6;
//pragma experimental ABIEncoderV2;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/token/ERC721/ERC721Pausable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/access/AccessControl.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/token/ERC20/SafeERC20.sol";

contract GunNFT is ERC721Pausable, AccessControl, Ownable {
    using SafeERC20 for IERC20;

    struct WeaponInfo {
        uint256 weaponType;
        uint256 stakingPower;
        uint256 damagePower;
        uint256 level;
        uint256 cifiToGet;
        uint256 ethToGet;
    }

    bytes32 public constant MINT_ROLE = keccak256('MINT_ROLE');

    uint256 public nextTokenId = 1;
    uint256 public cifiReserve;
    uint256 public ethReserve;

    address public immutable CIFI;
    address public immutable ETH;

    mapping(uint256 => WeaponInfo) public weaponInfoMap;

    event Mint(
        address indexed user,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Burn(
        address indexed user,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Update(
        address indexed sender,
        uint256 indexed tokenId,
        uint256 weaponType,
        uint256 level,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    );
    event Sync(uint256 cifiReserve, uint256 ethReserve);

    constructor(address _CIFI, address _ETH) public ERC721('Citizen.Fi', 'GUNNFT') {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        CIFI = _CIFI;
        ETH = _ETH;
    }

    function getLevel(uint256 stakingPower) public pure returns (uint256 level) {
        level = 1; // < 1k
        if (stakingPower >= 1E3 && stakingPower < 2E3) {
            level = 2;
        } else if (stakingPower >= 2E3 && stakingPower < 5E3) {
            level = 3;
        } else if (stakingPower >= 5E3 && stakingPower < 1E4) {
            level = 4;
        } else if (stakingPower >= 1E4 && stakingPower < 2E4) {
            level = 5;
        } else if (stakingPower >= 2E4 && stakingPower < 5E4) {
            level = 6;
        } else if (stakingPower >= 5E4 && stakingPower < 1E5) {
            level = 7;
        } else if (stakingPower >= 1E5 && stakingPower < 5E5) {
            level = 8;
        } else if (stakingPower >= 5E5 && stakingPower < 1E6) {
            level = 9;
        } else if (stakingPower >= 1E6) {
            level = 10;
        }
        
    
    }

    function _sync(
        uint256 pendingCifiToGet,
        uint256 pendingEthToGet,
        bool isAdd
    ) private {
        uint256 balanceOfCifi = IERC20(CIFI).balanceOf(address(this));
        uint256 balanceOfEth = IERC20(ETH).balanceOf(address(this));
        require(
            pendingCifiToGet == 0 ||
                (
                    isAdd
                        ? balanceOfCifi.sub(cifiReserve) == pendingCifiToGet
                        : balanceOfCifi.add(pendingCifiToGet) == cifiReserve
                ),
            'Error cifi amount'
        );
        require(
            pendingEthToGet == 0 ||
                (
                    isAdd
                        ? balanceOfEth.sub(ethReserve) == pendingEthToGet
                        : balanceOfEth.add(pendingEthToGet) == ethReserve
                ),
            'Error eth amount'
        );
        if (cifiReserve != balanceOfCifi) {
            cifiReserve = balanceOfCifi;
        }
        if (ethReserve != balanceOfEth) {
            ethReserve = balanceOfEth;
        }
        emit Sync(cifiReserve, ethReserve);
    }

    function mint(
        address to,
        uint256 weaponType,
        uint256 stakingPower,
        uint256 damagePower,
        uint256 cifiToGet,
        uint256 ethToGet
    ) public returns (uint256 tokenId) {
        require(hasRole(MINT_ROLE, _msgSender()), 'Must have mint role');
        tokenId = nextTokenId;
        _mint(to, tokenId);
        nextTokenId++;
        uint256 level = getLevel(stakingPower);
        weaponInfoMap[tokenId] = WeaponInfo({
            weaponType: weaponType,
            stakingPower: stakingPower,
            damagePower: damagePower,
            level: level,
            cifiToGet: cifiToGet,
            ethToGet: ethToGet
        });
      //  _sync(cifiToGet, ethToGet, true);
        emit Mint(to, tokenId, weaponType, stakingPower, damagePower, level, cifiToGet, ethToGet);
    }
 
}

3 posts were split to a new topic: Gas estimation error when minting ERC721