NFT whitelist with Merkle Tree — turnkey development

Manual allowlists for private drops leak, become cluttered with edits, and open the door to bots. We build Merkle Tree whitelists turnkey: eligibility for the drop is verified automatically rather than by trusting a spreadsheet. Drop preparation is cut dramatically, and manual edits disappear.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1375
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1349
  • B2B Advance company logo design
    B2B Advance company logo design
    794
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1087
  • AIDER company logo development
    AIDER company logo development
    1032
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1129

Merkle Tree for NFT whitelist: where the drop breaks

A gated drop breaks not in the mint code, but in the eligibility check for the drop.

The allowlist lives in at least three places — a spreadsheet, a registration form, and correspondence with the team — and by launch they diverge: someone was removed from one source but left in another, someone was included twice.

As a result, some participants are rejected despite valid eligibility, while others get through by mistake.

Next, the verification server fails. While eligibility is confirmed by a database query, every attempt in the first minutes after launch is a read and a write, and the same load takes down both verification and slot distribution.

The server becomes both a bottleneck for slot distribution and a single point of failure: if the database is unavailable, the gated distribution stops entirely, even though the contract itself is fine.

The third failure is mint eligibility forgery. If eligibility is confirmed by a server response or a signature that can be reproduced, a leaked credential opens the mint to anyone, not just the allowlist.

Then disputes begin over the right to a slot, where neither side has verifiable proof, and they have to be resolved manually.

Merkle Tree verification for NFTs removes all three causes at once: eligibility is confirmed mathematically, without querying a database and without trusting a server.

Below we break down how this is handled in practice — from tree construction to distributing the list to participants.

What drop eligibility verification provides instead of manual lists

Drop eligibility is confirmed by a Merkle proof: the tree root is fixed in the contract, and the participant receives a set of hashes along the path to the root.

The contract verifies the proof in a number of steps that grows logarithmically with the list size: ten thousand addresses — about fourteen hashes instead of an array on-chain.

A ready-made verification is provided by the OpenZeppelin library; we wrap it with allocated quota logic and an already-claimed flag.

Load moves off the server because the decision is made on-chain: it serves a static file with proofs that is cached at the edge, instead of a stream of “is my address on the list” requests in the first minutes. The spike in requests at launch stops being a bottleneck.

No manual operations remain at launch: the root is updated with a single transaction rather than by rewriting an array, and adding a late address does not require stopping the drop.

Along with the procedure, a whole class of failures goes away — desynchronization between the working database and contract state, an accidentally skipped address, repeated claiming from another wallet, and dumping the full list via a server method for bots. Repeated claiming is blocked by an on-chain flag, and the list itself is not publicly available.

Public verifiability is a separate effect: a participant rebuilds the tree from the published list and compares the root with the contract root, confirming that their address and quota are included. A dispute of “I'm not on the list” is resolved by hashes, without correspondence.

The drop opening time is predictable: verification does not depend on a queue or on how many people clicked the button at the same time.

Approaches to whitelist for an NFT drop: comparing modes

The slot distribution scheme determines what exactly the contract checks before minting and where the right to a slot is stored. It affects the cost of writing on-chain, the amount of preparation, and how expensive it is to change the list after the sale starts.

Scheme When it fits Limitations
Manual list in the contract up to a few dozen addresses, a closed circle every change is a new on-chain write, and the list size hits the limit on data volume per write
Server signature dynamic access, slots are issued on an event a signing service is required, and its key becomes a single point of failure
Proof tree (Merkle Tree) hundreds and thousands of addresses, a static list the root is fixed in advance, adding an address requires rebuilding the tree and a new root
Registrar contract multiple drops, roles, slot reuse more expensive to develop and verify, and dependencies between contracts appear

In practice, schemes are combined: a proof tree covers the main list, a server signature covers additional slots during the sale window, and a registrar is used when there are multiple slots and they are reused across releases.

Any of the solutions fails the same way — at eligibility verification: if the contract does not re-check that a slot has not already been used, circumventing it will cost more than the distribution itself.

Implementation step by step: from list audit to handover

Implementing a whitelist with Merkle Tree is not one contract but a sequence of work where an error at an early step nullifies all the following ones. We start with what the team already has: slot distribution spreadsheets, marketplace exports, manual lists. Then come the scheme, tree construction, contract setup, and testnet verification.

  1. Slot distribution audit. We analyze address sources, limit calculation rules, duplicates, and discrepancies between spreadsheets — these become the future mint conditions.
  2. Scheme design. We fix the leaf composition in the tree (address, limit, participation flag), the sorting order, and the list update method.
  3. Tree and root construction. A script deterministically builds the tree from the prepared list, outputs the root for the contract, and inclusion proofs for each participant.
  4. Contract setup. The root is written to the contract, proof verification is one operation instead of iterating over the list, plus protection against repeat minting and controlled root rotation.
  5. Testnet verification. We run a valid mint, a rejection for an address outside the list, limit enforcement, and a repeated call with the same data.
  6. Handover to maintenance. We provide build scripts, documentation, and the procedure for issuing a new root — updating the list does not break already distributed slots.

What remains in the repository and documentation after delivery

We deliver not a set of files “for review,” but a state from which another team can issue a new root and grant access without corresponding with us.

This is verified simply: an engineer who has not previously touched the project builds the tree from a fresh address export and generates a proof for one participant. Below is exactly what is kept in the private repository and documentation for this.

  • Contract source code — Merkle proof verification, prevention of repeated slot claiming by one address, tests for leaf forgery and for an address outside the tree.
  • Root build scripts — address export processing: record normalization, root calculation, publication to the contract; they produce the same root for the same input data.
  • Flow diagrams — where the list comes from, when the tree is rebuilt, and what is sent to a participant to sign the transaction.
  • Access policy — who is allowed to update the root, through what procedure, and how owner and admin roles are separated.
  • Distribution procedure — the order for generating a proof for a specific address, actions for an export error, and for an urgent root reissue.
  • Support guide — common requests, diagnostics for the “proof does not pass” case, and rollback to the previous root version.
  • Root version log — which root was published and when, and which export it is based on: it is used to reconstruct who the previous set verified.

Case study: a gated drop without leaks, bots, or manual edits

We are assembling a gated drop for a collection: several thousand participants, the allowlist lives in several exports, and before launch it is consolidated manually.

By the sale opening, the list diverged from reality — outside addresses passed the filter: some came from bot mailings, some were included when copying ranges from a spreadsheet, and some remained from canceled applications.

We removed manual reconciliation and moved allowlisting to a Merkle tree: the list is hashed into a tree, only the root hash goes to the contract, and a participant presents a short proof of their address.

The list itself is not published — even if someone posts their own dataset, the full allowlist cannot be reconstructed from it. Verification on Ethereum costs a participant one transaction and does not require the contract to know about them in advance.

Metrics before and after

Metric Before After
Allowlist assembly manual reconciliation of exports, 2–3 hours before launch tree rebuild in minutes
Outside addresses in the drop isolated passes in each wave none during the entire sale period
Eligibility requests dozens of “I'm not on the list” on launch day a few, and those are resolved from logs
List leak the full list circulated through public channels only the root is exposed

Edits are no longer an all-night task: adding or excluding an address does not change the contract; it is enough to rebuild the tree and update the root — a matter of minutes, without deploying a new contract.

Support load on peak days dropped to a few questions instead of a stream, because a participant can see their proof and the verification result in advance.

What to do if the whitelist is already maintained manually

A manual list is almost never clean: addresses arrived in different formats, some slots were issued before the idea of verifying the composition on-chain appeared, and edits were made along the way.

It cannot be reset to zero — a participant who was already promised a slot expects distribution, and reissuing from scratch looks like a scam.

Therefore, migrating a manual list starts not with code but with a state snapshot: we separately record those who have already been issued a slot and separately those who have only been promised one.

Next comes history correction: addresses are brought to a uniform format, duplicates are collapsed, and disputed records are resolved before hashing.

Any edit after tree construction means rebuilding the root, so the manual loop is closed at the input rather than edited along the way. Each participant becomes a leaf — address plus salt, so that the entire composition cannot be brute-forced from the root.

Leaves are sorted deterministically: otherwise the same set will produce different roots and the issued proofs will stop matching.

Then comes rebuilding the Merkle tree root and writing it to the contract.

The entire list does not need to be stored on-chain: inclusion proof verification takes a logarithmic number of steps relative to the tree size, and the data volume in the contract remains constant for any number of participants.

A gradual transition looks like this: while the working list is still live, the contract accepts both paths — by the previous confirmation and by inclusion proof.

Participants verify their proofs, the manual loop fades out, and issued slots are fully preserved — they are already inside the tree and will not go anywhere.

After distribution starts, the root is fixed, and any change to the composition is moved into a separate transaction with a delay and an event: a participant can see that the list was not edited retroactively.

Security concerns: list size and updates

An allowlist of tens of thousands of addresses does not need to be stored on-chain — only the Merkle tree root goes to the contract, and a participant presents a short proof of several values.

Verification takes one call and costs as much gas as checking a single address, so list growth affects neither the cost of entry nor response time.

Publishing the root in advance fixes the conditions: an address that did not make it into the fixed set will not pass verification retroactively, and the list itself remains with you — on-chain only the root and proofs are visible, not the composition of holders.

Edits after launch are made by issuing a new root: old proofs stop being accepted, and new ones are issued to the owners.

What happens if the root is published before the sale starts?

Nothing will be lost: the root fixes the composition at the time of construction, but the right to purchase is checked only on call. This way you confirm the list well before opening and rule out “on-the-fly” edits — participants see the same set.

Can the list be expanded after launch?

Yes, but it is a deliberate action: the new root replaces the previous one, and previously issued proofs become invalid. If replenishment is expected, we add a separate reserve set or a role that adds addresses selectively — this is reflected in the contract logic.

Are participant addresses visible on-chain?

No. Only the root, the fact of successful verification, and the address of the person who used it are public. No outside party can reconstruct the full list — a leak is possible only on the side of your storage.

What happens if proof distribution fails?

Each participant receives a proof in advance and can use it without our service. If the transaction reverts, nothing is deducted — the state remains unchanged, and the attempt can be repeated with the same proof.

Let's discuss the task: what to send for an estimate and plan

The allowlist is a small amount of data, but it determines both the Merkle Tree structure and the timeline.

To come back with an estimate and work plan rather than clarifying questions, we need at minimum four axes: how the list is formed, how many addresses it contains, what remains private, and when launch is needed.

  • Distribution conditions: who gets the right to mint — token holders, members of a private community, addresses after verification.
  • List size: the number of addresses at launch and expected growth — this determines whether the tree is fixed once or the root is rebuilt by rounds.
  • Privacy: publish the full list or send only the tree root to the contract while keeping the composition with you.
  • Current contracts: network, existing mint contract, and ability to make edits. If the code is immutable, we verify the proof with a separate call.
  • Updates: how many rounds are planned and who rebuilds the list, confirming the new root.
  • Launch timeline: the date of the first round and hard deadlines — they determine the order of stages and time for testnet verification.

Describe the task — we will come back with an estimate and work plan: tree structure, set of checks, distribution scheme, and schedule. If the contracts are not ready yet, we will include this in the design so that rework is not needed after launch.