1

I'm trying to build an ERC721 contract using the default OpenZeppelin 4.x ERC721URIStorage implementation. In the documentation at this URL (https://docs.openzeppelin.com/contracts/4.x/erc721) you can find the following text:

The ERC721URIStorage contract is an implementation of ERC721 that includes all standard extensions (IERC721Metadata and IERC721Enumerable).

Now, if you check the IERC721Enumerable interface documentation here (https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Enumerable) you can see that there is a tokenOfOwnerByIndex function but unfortunately it is not available at the OpenZeppelin ERC712URIStorage implementation as it was supposed to according to documentation.

Am I missing something or is OpenZeppelin documentation wrong?

Here is the code:

// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract GameItem is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("GameItem", "ITM") {}

    function awardItem(address player, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

3 Answers 3

2

The ERC721URIStorage contract is an implementation of ERC721 that includes all standard extensions (IERC721Metadata and IERC721Enumerable).

nope, the ERC721 oz implementation only includes the IERC721Metadata extension and not the IERC721Enumerable

the tokenOfOwner is available as part of ERC721Enumerable extension

And you're only inheriting from ERC721URIStorage, which intern only inherits from the ERC721 implementation, which only includes the Metadata extension

see underneath the description for the ERC721 implementation which is used by ERC721URIStorage and your example contract:

Implementation of ERC721 Non-Fungible Token Standard, including the Metadata extension, but not including the Enumerable extension, which is available separately as ERC721Enumerable.


A complete implementation for your example would be as follows:

// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract GameItem is ERC721URIStorage, ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("GameItem", "ITM") {}

    function awardItem(address player, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
    
    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function safeMint(address to, uint256 tokenId) public onlyOwner {
        _safeMint(to, tokenId);
    }

}
1

You need to override the _burn and tokenURI function, checkout the wizard here and add URI storage as one of your features: https://docs.openzeppelin.com/contracts/4.x/wizard

0

IDK how I feel about my answer, but what I did is i went into my node_modules folder, and copied all the code from ERC721Enumerable into ERC721URIStorage and that's how I was able to combine both modules. Not sure if this is a correct workaround but it's managed to work in my code so far

1
  • Although this works, Its bad practice to modify Open Zeppelin's contracts
    – 0xD1x0n
    Aug 30 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.