Create a pre-reveal mechanism for your NFT collection

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.

NFTs with a pre-reveal placeholder image

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 ERC721implementation.

If you inspect the ERC721.sol contract, you’ll find that a tokenURI function is implemented there.

Here it is, for your reference:

OpenZeppelin’s implementation of tokenURI

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:

The tokenURI function

You’ll notice that we have a couple of global variables referenced: _isRevealed and _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 true.

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!

Conclusion

You’ve learnt a simple, secure and effective method to implement an NFT pre-reveal mechanism.