Estimated time: 4 minutes read

A scalable solution is presented for managing whitelists in NFT contracts using Merkle trees, reducing gas costs and improving privacy by avoiding the direct storage of addresses in the smart contract.

What is a whitelist in an NFT contract?

A whitelist in an NFT contract is a data set that allows the blockchain to verify whether a transaction is authorized to mint a new NFT from the smart contract.

A typical solution involves adding the list of authorized user wallet addresses directly to the contract. This is a very simple option, but it has two main drawbacks:

  • It requires storing a large amount of data in the smart contract if the list of authorized users is extensive, which leads to higher gas costs.
  • The list of authorized addresses must be known in advance or updated on the fly (via blockchain transactions), which also results in gas fees.

As an alternative solution, this article presents the use of a Merkle tree to create a list of pre-authorized codes for minting a new NFT from the contract.

What is a Merkle tree?

A Merkle tree is a binary tree data structure in which each node is linked to the hashes of its child nodes. The hashes of the initial data are represented in the leaf nodes of the tree. From there, the structure builds upward into parent nodes, forming the branches of the tree. The tree concludes with the hash of the root node, known as the Merkle root.

The structure, adapted from Wikipedia, would be as follows:

Merkle Tree Diagram

Among the uses of a Merkle tree, one of the most important is the ability to verify whether a given piece of data belongs to one of the leaves, using the root hash and a list of hashes from the tree called a Merkle proof.

These verifications are computationally efficient processes, making them suitable for execution on the blockchain.

Merkle trees are widely used in many blockchain networks: each block contains a tree of validated transactions. To verify a transaction, only the Merkle root and the proof are needed.

How to create a whitelist in an NFT contract using a Merkle tree?

The idea is simple: the whitelist consists of N random alphanumeric codes, for example in UUID format.

A Merkle tree is built using the whitelist codes as the initial data. The tree will be composed of the leaves (hashes of the initial data), the branches, the Merkle root, and the Merkle proofs.

Each user authorized to mint an NFT is given a valid code and its corresponding Merkle proof.

From the DApp that interacts with the NFT contract, it will be verified on-chain whether the submitted code belongs to the tree, using the corresponding proof and the root hash (stored in the smart contract). If the verification is successful, minting a new NFT will be allowed.

To implement this scheme, you can use OpenZeppelin’s Merkle Tree JavaScript library.

import { StandardMerkleTree } from "@openzeppelin/merkle-tree";
import crypto  from 'crypto';

// Creates the whitelisted codes
export const whitelistCodes = Array.from({ length: 300 }, () => [crypto.randomUUID()]);
// Creates Merkle tree from the whitelisted codes
const tree = StandardMerkleTree.of(whitelistCodes, ["string"]);  

// Merkle root should be stored in the smart contract
const merkleRoot = tree.root;

// Codes and proofs should be delivered to the authorized users
for (const [i, v] of tree.entries()) {
    let proof = tree.getProof(i);
    console.log('Code:', v);
    console.log('Proof:', proof);   
}

The on-chain verification of the code and the proof can be performed in Solidity using OpenZeppelin’s MerkleProof utility contract:

function mint(string calldata code, bytes32[] memory proof, address to) external {
            
        if (!MerkleProof.verify(proof, _merkleRoot, 
                                keccak256(bytes.concat(keccak256(abi.encode(code)))))) 
            revert NonexistentCode();
        _safeMint(to, uint256(keccak256(bytes.concat(keccak256(abi.encodePacked(code))))));
}

Conclusion: Creating Scalable Whitelists

This article has explored a simple way to create a whitelist to authorize users of an NFT DApp to mint a new token.

As an alternative to more straightforward approaches, such as directly storing the list of allowed addresses in the smart contract, this method proposes the use of a Merkle tree.

Although slightly more complex, this solution is scalable, as it only requires storing the tree’s root hash in the contract, and anonymous, since it does not require knowing users’ addresses in advance.


Have you ever needed to authorize a list of users to access a token? What kind of whitelists have you implemented?

If using a Merkle tree seems like an interesting solution to you, I’d be happy to help you develop it further for your specific use case.
Feel free to reach out and  let’s talk. Thank you so much!