How to use a Merkle Tree for large whitelist mints

Motivation

// 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

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)),
keccak256,
{ sortPairs: true }
);
console.log(merkleTree.getHexRoot())
return merkleTree;
}
// 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
proof");
// Require that this token or wallet hasn't already minted // Store that either the wallet or tokenID has been minted, then mint the token
}
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 });
}

Conclusion

--

--

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