A collection of 10,000 NFTs with a whitelist of 3,000 addresses. Storing the whitelist in an on-chain mapping means 3,000 SSTORE operations during setup — around 0.5–0.8 ETH ($1,500) in gas. A Merkle Tree whitelist solves this: one transaction for the root, 3–5k gas per mint verification. Real savings of 10x or more. Our audited smart contracts guarantee protection against double-leaf attacks and multi-tier support — a challenge we've been solving for over 5 years across 30+ projects. Order turnkey development — you get the secure smart contract, proof generator, and frontend.
How to Build a Merkle Tree for a Whitelist
The tree leaves are keccak256 hashes of addresses (sometimes with extra data: keccak256(abi.encodePacked(address, maxMintAmount))). The tree is built bottom-up: adjacent leaves are concatenated and hashed. The root is a single bytes32 stored in the contract.
import { MerkleTree } from 'merkletreejs' import { keccak256, encodePacked } from 'viem' const leaves = whitelist.map(addr => keccak256(encodePacked(['address'], [addr])) ) const tree = new MerkleTree(leaves, keccak256, { sortPairs: true }) const root = tree.getHexRoot() // → bytes32 for contract sortPairs: true is critical. It ensures deterministic tree construction regardless of leaf order. Without it, the same whitelist produces different roots with different address ordering.
Verification in the Contract
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; bytes32 public merkleRoot; function mint(uint256 amount, bytes32[] calldata proof) external payable { bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof"); // ... mint logic } OpenZeppelin MerkleProof library provides a standard implementation with O(log n) complexity. For 3,000 addresses — a proof of 12 hashes (log2(3000) ≈ 12). For 100,000 addresses — 17 hashes. Gas cost grows slowly.
How to Defend Against Double-Leaf and Double-Mint Attacks
A classic Merkle Tree issue in smart contracts: if a leaf is not unique, one proof can verify multiple leaves. OpenZeppelin's MerkleProof library handles this since version 4.7, checking that leaf != internal_node. An extra safeguard: double-hash the leaf — keccak256(keccak256(abi.encodePacked(addr))). This makes leaf collisions with internal nodes practically impossible.
Extended Whitelist: Tiers and Allocations
A simple whitelist only checks if an address is present. For tiered whitelists (tier 1: 2 NFTs, tier 2: 1 NFT), include the data in the leaf:
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, maxAmount)); require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid proof"); require(amount <= maxAmount, "Exceeds allocation"); Now the proof verifies both the address and its maximum allocation. One tree, one root, different allocations.
Double-Mint Protection
A Merkle proof only validates the right to mint — it doesn't prevent repeated mints. Use a separate mapping for used allocations:
mapping(address => uint256) public mintedAmount; function mint(uint256 amount, uint256 maxAmount, bytes32[] calldata proof) external { require(mintedAmount[msg.sender] + amount <= maxAmount, "Exceeds allocation"); mintedAmount[msg.sender] += amount; // ... } mintedAmount only does an SSTORE on a user's first mint — that's O(unique_minters) storage, not O(whitelist_size).
Off-Chain Proof Distribution
Three proven approaches for users to get their proof. Static JSON on IPFS/CDN — simple, no backend: { "0xABC...": ["0x...", "0x..."] } generated once, published on CDN. A backend API offers flexibility for adding addresses but requires a server. On-chain events via The Graph provide decentralization but add complexity. For most projects, static JSON on CDN works best.
| Distribution Method | Complexity | Flexibility | Cost |
|---|---|---|---|
| Static JSON (IPFS/CDN) | Low | Low | Minimal |
| Backend API | Medium | High | Medium |
| The Graph (subgraph) | High | Medium | Medium |
Updating the Whitelist After Deployment
If the contract allows changing merkleRoot (via onlyOwner), the whitelist can be updated without redeploying. Scenario: main whitelist + last-minute additions. Build a new tree with the additions, update the root via setMerkleRoot(). Important: after changing the root, old proofs stop working. Users with cached proofs will get a revert. Communicate the update.
Why Merkle Tree Is Better Than a Mapping
Gas comparison for a typical project with 3,000 addresses:
| Stage | Mapping (3,000 addresses) | Merkle Tree (3,000 addresses) |
|---|---|---|
| Setup | 3,000 SSTORE (≈0.5–0.8 ETH, ~$1,500) | 1 SSTORE (≈0.0001 ETH, ~$0.20) |
| Mint (1 verification) | 1 SLOAD + 1 SSTORE (≈5,000 gas) | 12 hashes + 1 SSTORE (≈3,000 gas) |
| Whitelist update | Rewrite all addresses (expensive) | 1 transaction (≈0.0001 ETH, ~$0.20) |
Merkle Tree is 10x+ more efficient on setup and 1.5–2x on each mint. For a collection of 10,000 tokens with a 3,000-address whitelist, the gas saved during setup is 0.5–0.8 ETH (~$1,500).
What's Included in Turnkey Whitelist Development
Our proven process includes these stages:
- Requirements analysis: tiers, limits, ERC-721 standard, need for updates.
- Smart contract development with Merkle proof verification (Solidity, OpenZeppelin).
- Merkle Tree and proof generation using a Node.js script (merkletreejs).
- API or static JSON for proof distribution.
- Frontend integration (wagmi, RainbowKit) with client-side verification.
- Thorough testing (Foundry: unit tests, correct/incorrect proofs, double-mint protection).
- Deployment and interaction documentation.
Estimated Timeline and Cost
Basic Merkle whitelist implementation — 2–3 days. With tiers, API for proofs, and frontend integration — up to 5 days. Cost is calculated individually after analyzing your project. We guarantee accurate estimates — contact us for a consultation and we'll prepare a commercial proposal.
Why Entrust Development to Us?
Our blockchain development experience (Ethereum, Polygon, Arbitrum, Solana) spans over 5 years with a proven track record of 30+ successful projects. Each audited smart contract undergoes internal code review and formal verification of critical functions. We guarantee secure, gas-efficient solutions. Get a consultation — our certified engineers are ready to analyze your project and propose the optimal solution.







