How to use a Merkle Tree for large whitelist mints


// Code for demonstration purposes only
// DO NOT USE for a real project.
contract NFT is ERC721 {
mapping(address => bool) public whitelist;

function setWhitelist(address[] calldata _addresses) {
for (uint i = 0; i < _addresses.length; i++) {
whitelist[_addresses[i]] = true;
function mintWhitelist() {
require(whitelist[msg.sender], "not whitelisted");

// mint an nft

Merkle Trees

Merkle tree layout. Wikipedia

Usage in practice

  1. Once the list of whitelist addresses and token IDs are finalised, generate the Merkle root and store this in the contract.
  2. Create an API endpoint that returns the Merkle proof for a specific wallet address. When a user connects their wallet and presses mint, their browser requests the Merkle proof from the API.
  3. The smart contract is called with the wallet address and Merkle proof.
  4. The contract checks the root of the supplied proof matches the root stored by the administrator in step one.
  5. If these agree, mint to the wallet.

Generating the Merkle tree ahead of time

import { ethers } from "ethers";
import keccak256 from "keccak256";
import MerkleTree from "merkletreejs";
// Map tokenID to wallets
// e.g.
const tokens = {
2: "0xabcde..."
export function hashToken(tokenId, account) {
return Buffer.from(ethers.utils.solidityKeccak256(["uint256", "address"], [tokenId, account]).slice(2), "hex");
export function generateMerkleTree() {
const merkleTree = new MerkleTree(
Object.entries(tokens).map((token) => hashToken(...token)),
{ sortPairs: true }
return merkleTree;

Smart Contract Code

// Generate the leaf node (just the hash of tokenID concatenated with the account address)
function _leaf(address account, uint256 tokenId) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(tokenId, account));
// Verify that a given leaf is in the tree.
function _verify(bytes32 _leafNode, bytes32[] memory proof) internal view returns (bool) {
return MerkleProofUpgradeable.verify(proof, merkleRoot, _leafNode);
function mintWhitelist(
address account,
uint256 tokenId,
bytes32[] calldata proof
) public payable {
require(_verify(_leaf(account, tokenId), proof), "Invalid
// Require that this token or wallet hasn't already minted // Store that either the wallet or tokenID has been minted, then mint the token

Serving a proof for a specific wallet

import whitelist from "../../../utils/whitelist/whitelist.json";
import { generateMerkleTree, hashToken } from "previous section"
// Generate the Merkle tree (use supplied code in previous section)
const merkleTree = generateMerkleTree();
export default function handler(req, res) {
const { wallet } = req.query;
// Lookup the token ID for this wallet, use your own implementation
const id = addressToTokenId[wallet];
if (!id) return res.json({ error: "Wallet not in whitelist" });
// Return the Merkle proof
const proof = merkleTree.getHexProof(hashToken(id, wallet));
return res.json({ id, proof });





EvoSnails are a fully on-chain open source NFT project that allows users to stake their snails and upgrade snail traits.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium


Introducing DeFiPie: all the advantages of DeFi rolled into one dApp

[ENG] Week 42 update

Repaving the way…

Crypto-what? The imaginary future of wealth..

Option Robot

Kryptview Roadmap Update

FortFC revolutionizes cryptocurrency

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


EvoSnails are a fully on-chain open source NFT project that allows users to stake their snails and upgrade snail traits.

More from Medium

ETHDenver 2022 Wrap-up: Major Themes

Top NFT Drop Left in 2021- Seussibles Evergreen Set

Дым и зеркала: заблуждения Запада о действиях России в Украине

AfroDroids Transmission: First week of 2022 & Updates to the Roadmap.