Implementing Contract-Level Metadata for On-Chain NFTs

In the evolving world of NFTs, contract-level metadata plays a pivotal role in enhancing the overall user experience and functionality of NFT platforms. This guide aims to provide a detailed walkthrough on how to properly implement contract-level metadata for on-chain NFTs, specifically for collections on OpenSea.

sequenceDiagram participant User participant OpenSea participant Contract User->>OpenSea: Request to view NFT OpenSea->>Contract: Fetch contractURI Contract-->>OpenSea: Return _contractURI OpenSea-->>User: Display NFT with metadata

Understanding the Challenge

When implementing contract-level metadata for an NFT collection on OpenSea, it's essential to ensure that the metadata updates correctly on the OpenSea collections' page. Additionally, the description of the contract on the tokens' pages should also reflect these updates. The TokenURI, when stored on-chain, should update accordingly when provided with the correct data.

The Code Walkthrough

Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

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

contract Test is ERC721Enumerable, ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    using Strings for uint256;

    string private _contractURI;

    constructor() ERC721("Test", "T") {}

    function svgToImageURI(string memory _source) public pure returns (string memory) {
        string memory baseURL = "data:image/svg+xml;base64,";
        string memory svgBase64Encoded = Base64.encode(bytes(string(abi.encodePacked(_source))));
        return string(abi.encodePacked(baseURL, svgBase64Encoded));
    }

    function formatTokenURI(string memory _imageURI, string memory _name, string memory _description, string memory _properties) public pure returns (string memory) {
        return string(
            abi.encodePacked(
                "data:application/json;base64,",
                Base64.encode(
                    bytes(
                        abi.encodePacked(
                            '{"name":"', _name,
                            '", "description": "', _description, '"',
                            ', "attributes": ', _properties,
                            ', "image":"', _imageURI, '"}'
                        )
                    )
                )
            )
        );
    }

    function setTokenURI(uint256 _tokenId, string memory _tokenURI) public onlyOwner() {
        _setTokenURI(_tokenId, _tokenURI);
        emit tokenChanged(_tokenId);
    }

    function setContractURI(string memory contractURI_) public onlyOwner() {
        _contractURI = string(abi.encodePacked(
            "data:application/json;base64,",
            Base64.encode(
                bytes(
                    abi.encodePacked(
                        contractURI_
                    )
                )
            )
        ));
    }

    function contractURI() public view returns (string memory) {
        return _contractURI;
    }
}

Common Pitfalls and Solutions

When setting the contract URI, ensure that the input provided to the setContractURI function is a valid JSON snippet that complies with the platform's requirements. For instance, using a simple JSON snippet from the OpenSea website can help ensure compliance.

FAQs

  • Q: What is the role of contract-level metadata in NFTs?
    • A: Contract-level metadata provides detailed information about the NFT collection, enhancing the user experience and functionality on platforms like OpenSea.
  • Q: Why is my contract's metadata not updating on OpenSea?
    • A: Ensure that the input provided to the setContractURI function is a valid JSON snippet that complies with OpenSea's requirements.
  • Q: How can I ensure that the TokenURI updates correctly when stored on-chain?
    • A: Make sure to provide the correct data to the TokenURI, and it should reflect the updates accordingly.

Author