It’s become a common practice when launching a new PFP style NFT collection (a la Bored Ape Yacht Club) to use a placeholder image for every NFT minted, and to only reveal the final NFTs once all NFTs have been minted.
This is an important practice because without it snipers can choose which NFTs to mint based on the rarities of the traits exposed through the metadata.
Before starting to build this feature, let’s expand on our business requirements:
- Hide the tokens and metadata until all tokens have been minted
- Send a “pre-reveal” version of the token to users once they mint
- Allow the contract owner to “reveal” all tokens in the collection
Our smart contract is responsible for returning the URL where each token’s
metadata.json file lives.
The way we can solve for the above is to create a “pre-reveal” metadata file and upload it somewhere (e.g.: https://example.org/pre-reveal.json). This “pre-reveal URI” is what we want to return for every token until the reveal takes place.
After the reveal, we want to be able to update the smart contract with a new URL that can be used to generate the correct token URIs. For example, if we’ve uploaded all our tokens to a web-server that responds to
https://exmaple.org/tokens/:tokenId, then we’ll want to update the smart contract with our baseURI (
https://example.org/tokens/) so that it can easily generate the correct tokenURI by simply appending the token ID to the baseURI.
With this knowledge, we can redefine the problem to more concrete asks of our smart contract:
- It should return a generic metadata when the collection is not yet revealed
- It should allow the contract owner to update the baseURI
- It should return the correct metadata when the token is revealed
How do we implement it?
Let’s assume that we are developing a smart contract for an NFT collection that extends off the usual OpenZeppelin
If you inspect the
ERC721.sol contract, you’ll find that a
tokenURI function is implemented there.
Here it is, for your reference:
Their implementation (above) will automatically concatenate the base URI (returned from
_baseURI() – which we’ll need to remember for later) with the token ID being retrieved. This is great for AFTER the reveal – but for the pre-reveal we’ll need to override this function to return our pre-reveal URI:
You’ll notice that we have a couple of global variables referenced:
_preRevealURI. You can implement these however you like, but at it’s simplest you can simply define them at the top of your contract:
Next we’ll need to create a function to “reveal” the token.
The above reveal function will save the passed baseURI to a global variable called
_postRevealBaseURI and will set the
_isRevealed boolean to
We’re almost done, with the
_isRevealed boolean set to
true, then the
tokenURI function that we wrote earlier will defer to the parent class’ implementation. If you remember, this implementation calls a
_baseURI function to retrieve the base URI.
If we inspect that implementation, we can see that it’s actually there to be overridden:
Let’s oblige OpenZeppelin and override the
_baseURI function to return the correct base URI!
You’ve learnt a simple, secure and effective method to implement an NFT pre-reveal mechanism.
This article is a specially re-written extract from my upcoming book launch: “A developer’s guide to launching an NFT collection”.
Follow me on twitter for more blockchain-related tips and tricks, and to keep in the loop about the book!