Gas estimation error when minting ERC721

@abcoathup I have actually the same problem with code (Gas estimation failed for mint in ERC721 on Remix) which is more or less a copy of ERC721PresetMinterPauserAutoId.sol

deployed on kovan : 0x09f951AC8e7414965c243E5B9817718813A2F1ce

MINTER_ROLE = bytes32: 0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6

checked that hasRole is true for my account 0xa31F3d5d0d8A412084a3f83D253340524F7f8897

and still I get the gas estimation error when I try to mint(0x123…, 1, 5)

pragma solidity >=0.6.0 <0.8.0;


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/token/ERC721/ERC721Burnable.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/utils/Counters.sol";

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



/**
 * @dev {ERC721} 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
 *  - token ID and URI autogeneration
 *
 * 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 DTOParticipationNFT is Context, AccessControl, ERC721Burnable, ERC721Pausable {

    using Counters for Counters.Counter;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    Counters.Counter private _tokenIdTracker;

    struct TicketData {
        uint256 ticketNumberFrom;
        uint256 ticketAmount;
    }

    TicketData[] public ticketData;

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
     * See {ERC721-tokenURI}.
     */
    constructor(string memory name, string memory symbol, string memory baseURI) public ERC721(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
        _setBaseURI(baseURI);
    }

    /**
     * @dev Creates a new token for `to`. Its token ID will be automatically
     * assigned (and available on the emitted {IERC721-Transfer} event), and the token
     * URI autogenerated based on the base URI passed at construction.
     *
     * See {ERC721-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 _ticketNumberFrom, uint256 _ticketAmount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "DTOParticipationNFT: must have minter role to mint");

        // We cannot just use balanceOf to create the new tokenId because tokens
        // can be burned (destroyed), so we need a separate counter.
        ticketData[_tokenIdTracker.current()] =
            TicketData({
                ticketNumberFrom: _ticketNumberFrom,
                ticketAmount:     _ticketAmount
            });

        _mint(to, _tokenIdTracker.current());
        _tokenIdTracker.increment();
    }


    function getNumberOfMintedNFT() public view returns (uint256) {
        return _tokenIdTracker.current();
    }


    function getTicketData(uint256 tokenId) public view returns (uint256, uint256)
    {
        return (
            ticketData[tokenId].ticketNumberFrom,
            ticketData[tokenId].ticketAmount
        );
    }


    function getTokenURI(uint256 tokenId) public view returns (string memory) {
        return tokenURI(tokenId);
    }


    // ERC721Pausable -----------------------------------------------------------------------------------------------------------

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC721Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC721Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC721PresetMinterPauserAutoId: must have pauser role to unpause");
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721, ERC721Pausable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }
}
1 Like

I even have the same error with a stripped down version with no access control :frowning:

pragma solidity >=0.6.0 <0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/token/ERC721/ERC721.sol";

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/utils/Counters.sol";

contract DTOParticipationNFT is ERC721 { // Context, AccessControl, ERC721Burnable, ERC721Pausable {

    using Counters for Counters.Counter;

    // bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    // bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    Counters.Counter private _tokenIdTracker;

    struct TicketData {
        uint256 ticketNumberFrom;
        uint256 ticketAmount;
    }

    TicketData[] public ticketData;

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * Token URIs will be autogenerated based on `baseURI` and their token IDs.
     * See {ERC721-tokenURI}.
     */
    constructor(string memory name, string memory symbol, string memory baseURI) public ERC721(name, symbol) {
        // _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        // _setupRole(MINTER_ROLE, _msgSender());
        // _setupRole(PAUSER_ROLE, _msgSender());
        _setBaseURI(baseURI);
    }

    /**
     * @dev Creates a new token for `to`. Its token ID will be automatically
     * assigned (and available on the emitted {IERC721-Transfer} event), and the token
     * URI autogenerated based on the base URI passed at construction.
     *
     * See {ERC721-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 _ticketNumberFrom, uint256 _ticketAmount) public virtual {
        // require(hasRole(MINTER_ROLE, _msgSender()), "DTOParticipationNFT: must have minter role to mint");

        // We cannot just use balanceOf to create the new tokenId because tokens
        // can be burned (destroyed), so we need a separate counter.
        ticketData[_tokenIdTracker.current()] =
            TicketData({
                ticketNumberFrom: _ticketNumberFrom,
                ticketAmount:     _ticketAmount
            });

        _mint(to, _tokenIdTracker.current());
        _tokenIdTracker.increment();
    }


    function getNumberOfMintedNFT() public view returns (uint256) {
        return _tokenIdTracker.current();
    }


    function getTicketData(uint256 tokenId) public view returns (uint256, uint256)
    {
        return (
            ticketData[tokenId].ticketNumberFrom,
            ticketData[tokenId].ticketAmount
        );
    }


    function getTokenURI(uint256 tokenId) public view returns (string memory) {
        return tokenURI(tokenId);
    }

}

I found the problem, totally unrelated to the ERC721 contract :grimacing:

This was the error message (which did not really help)

transact to DTOParticipationNFT.mint pending ...
transact to DTOParticipationNFT.mint errored: Error: The execution failed due to an exception. Bad instruction fe

My error was, that I tried to add elemets to an array by just accessing new (unpopulated) entries (which works with mappings, but apparently not with arrays

        ticketData[_tokenIdTracker.current()] =
            TicketData({
                ticketNumberFrom: _ticketNumberFrom,
                ticketAmount:     _ticketAmount
            });

now replaced by

       ticketData.push(
            TicketData({
                ticketNumberFrom: _ticketNumberFrom,
                ticketAmount:     _ticketAmount
            }));

works just fine now !

1 Like

Hi @sven.meyer ,

Welcome to the community :wave:

Glad you were able to resolve. If you get a chance, it would be great to know What are you working on?