Leaving this here for my future self and for others as this question pops up on the OpenSea discord quite a bit.
tl;dr – Don’t return an IPFS URI (ipfs://{cid})
or IPFS HTTP gateway URL (https://ipfs.io/ipfs/{cid}
or https://gateway.pinata.cloud/ipfs/{cid}
) via the contractURI()
function. Use a traditional HTTP web API or base64 encode the metadata JSON and return it from the function if you want OpenSea to be able to read the contract-level metadata and display it on your collection’s page.
OpenSea allows smart contract developers to include storefront-level metadata by implementing a function named contractURI()
and returning a URI pointing to the metadata. OpenSea expects metadata formatted as JSON that consists of a few specific attributes they recognize. This information is pre-populated into the NFT’s collections page associated with the smart contract on OpenSea.
For individual non-fungible tokens (NFT) like ERC721’s and ERC1155’s, calling the tokenURI()
function with a tokenId value as a parameter returns a URI that points at the token’s metadata. The tokenURI()
function typically returns URI that point to traditional endpoints like web servers or cloud storage using conventional URL protocols (https://
) or, more commonly in web3, to files stored on IPFS via the IPFS URL protocol (ipfs://
). URIs returned from this function can point directly to IPFS or via IPFS HTTP gateways without problems.
It is unclear from the docs, but the responses from the token URI function can vary more than the contract URI function. I have experimented with valid URI variations, all pointing to the same JSON, and had no success with having the contract URI function return either IPFS or IPFS public gateway URIs. Providing the same types of URI from the token URI functions has worked as expected. What I have found to work are the following:
-
Return the metadata JSON as a base64 encoded string. Do not forget to prepend the string with
data:application/json;base64,{yourBase64EncodedStringHere}
. -
Return a URL pointing to a conventional REST API endpoint that returns the metadata JSON.
I cannot explain why the API requests to a REST API work as expected or why the requests to IPFS or an IPFS public gateway do not. I plan to file this as a ticket with OpenSea, and hopefully, if they’re unable to address the issue outright, they can at least update the docs to be more explicit about what can and cannot be returned from the contract URI function.