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.
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
// 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.
- A: Ensure that the input provided to the
- 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.