ERC721 - NFT - all the tokens sharing the same metadata tokenURI?

TLDR: I would like all the NFTs to use the same token URI - an IPFS hash that contains JSON file with all the metadata.

Looking at the source:

     * @dev See {IERC721Metadata-tokenURI}.
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0
            ? string(abi.encodePacked(baseURI, tokenId.toString()))
            : '';

Looks like tokenURI is individual for each token.

Does it make sense for many NFTs to share the same tokenURI?

In my opinion yes.

I could be wrong? :woman_shrugging:

Limited series of 100 artworks or passports. All of them can share the same token URI?


(still working and editing, the JSON metadata on IPFS and tokenURI is the current problem I’m solving)


I don’t want to deploy a directory of JSON files, I just prefer a sigle JSON file that is applicable for the NFTs.


/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See
///  Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface ERC721Metadata /* is ERC721 */ {
    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external view returns (string _name);

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external view returns (string _symbol);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the "ERC721
    ///  Metadata JSON Schema".
    function tokenURI(uint256 _tokenId) external view returns (string);
  • This is the “ERC721 Metadata JSON Schema” referenced above.
    "title": "Asset Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this NFT represents"
        "description": {
            "type": "string",
            "description": "Describes the asset to which this NFT represents"
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
1 Like

I think I’ll just override the function.

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        return "ipfs://Qmck3GpzP8uhXgpkUUKnHMH3DZKUpwf1U9BgyvLKFNK6r6"; // 1 ?
        return "Qmck3GpzP8uhXgpkUUKnHMH3DZKUpwf1U9BgyvLKFNK6r6"; // 2 ?
        return ""; // 3 ?

I think option 1 is the most preferable.

That IPFS hash will be a JSON containing all the metadata.

I think that is a sensible workaround?

Kindly welcoming a reality check.

This is a relatively new area (ground zero), standards are still being developed…

1 Like

Hi @marsxr,

You could either override tokenURI (option 1 is supported by OpenSea, rather than option 3 as it is centralized due to the gateway) or you could pre-create your metadata for each token ID and upload the directory to IPFS and have the base URI as ipfs://[Hash]/ and concatenate with the tokenID in tokenURI

That is up to infinity… Not keen. I prefer a static / fixed tokenURI and I will use option 1 as it appears natural.



    receive() external payable { // Fallback function

    function purchase() payable public {
        require(msg.value >= currentPrice, "Not enough ETH. Check the current price.");
        uint256 refund = msg.value - currentPrice;
        if (refund > 0) {
            (bool sent1, bytes memory data1) = payable(msg.sender).call{value: refund}("");
            require(sent1, "Failed to send ETH refund to the sender");

        // Sending to Gnosis Safe takes more than 21k gas limit on `transfer`
        // Need to use something else, see:
        (bool sent2, bytes memory data2) ={value: currentPrice}("");
        require(sent2, "Failed to send ETH to the multisig");

        _mint(msg.sender, currentSerialNumber);
        emit Purchase(msg.sender, currentSerialNumber, currentPrice, false);

        if (block.timestamp > cutoffTimestamp) {
            currentPrice = currentPrice * multiplier / divisor; // * 1001 / 1000 === increase by 0.1% (no longer SafeMath, compiler by default)

There is a price increase but as we recycle ETH (spend and release back in circulation) there is no upper limit…


    // This is inspired by Hackers Congress Paralelní Polis: final ticket available for 1 BTC
    // Network State Genesis offers *UNLIMITED* number of NFTs for 1 BTC
    // How is that even possible?
    // As we establish multiplanetary civilisation, some of the accrued money will be put back into the circulation (Bitcoin recycling)
    function purchaseWithWBTC() public {
        WBTC.transferFrom(msg.sender, multisig, 10 ** 18);
        _mint(msg.sender, currentSerialNumber);
        Purchase(msg.sender, currentSerialNumber, 0, true);


1 Like