What is the best method to set the metadata for each token in a ERC721 contract? Please take a PRODUCTION ENVIRONMENT in mind while answering the question. How do the industry giants achieve it?
Case 1: Hardcoding the IPFS URLs in the contract itself before minting begins.
This seems to be the easiest way. I can upload the metadata of each token to IPFS (using Pinata or something else) and get the URLs which I can then hardcode as a mapping in the contract itself. Then, I can internally call _setTokenURI()
method to set the metadata of the token being minted in my mintNFT()
function.
mapping(uint => string) public ipfsURLs; // Hardcode URLs here
function mintNFT() public payable
{
_tokenIds.increment(); // Counter
uint256 newItemId = _tokenIds.current();
_mint(msg.sender, newItemId);
string memory tokenURI = ipfsURLs[newItemId];
_setTokenURI(newItemId, tokenURI);
}
Pros: I won't have to pay the gas fees while setting the metadata as it is set in the mintNFT()
function, i.e. the gas is deducted from the wallet of the user who is minting the token.
Cons: The problem with this approach is that all the tokenURIs will be visible to everyone who reads the contract and people might take unfair advantage of that.
Case 2: Setting the metadata as the minting is going on
In the mintNFT()
function, I can emit an event with tokenId
as the parameter. Then, in my JS code, I can watch for the event and call a function that sets the tokenURI of the the minted token, which I'll get from my database.
event MintComplete(uint tokenId, address owner);
function mintNFT() public payable
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(msg.sender, newItemId);
emit MintComplete(newItemId, msg.sender);
}
function setURI(uint256 id, string memory ipfsURI)
public onlyOwner // called from a script when above event emitted
{
_setTokenURI(id, ipfsURI);
}
Pros: The metadata IPFS URLs are not visible in the contract before the token is minted. This will act as a good security measure in maintaining randomness of minting.
Cons: User will pay the gas fees for mintNFT()
method, and the contract owner will have to pay the gas fees for calling setURI()
method. More the number of tokens (~1000s), more the contract owner will have to pay in Gas fees.
Case 3: Setting the metadata after the minting process is complete
After the minting process is over and all the tokens have been minted, I can send the mapping of tokenId to IPFS URLs to a contract function in a single transaction (or in batches of say 100 URLs per transaction).
function setURI(uint256 startIndex, string[] memory listOfIpfsURI)
public onlyOwner // called after batch minting over
{
for (index = startIndex; index < listOfIpfsURI.length; index++) {
_setTokenURI(index, listOfIpfsURI[index-startIndex]);
}
}
Pros: Same as Case 2.
Cons: Same as Case 2, but Gas fees is lower due to lower number of calls to setURI()
. Also, people might have to wait for the reveal till the batch size is complete, which can take a lot of time in some cases.
Case 4: Any other method ...
In case there exists a better method which I haven't listed above, please include it with an example code if possible. Also, if anyone has any idea how the big NFT collections (BoredApes etc.) do it, please shed some light on that as well.