Creating an NFT whitelist using Merkle Tree Proofs
The easiest and most secure way to implement a whitelist to a Solidity NFT Smart Contract for Ethereum or Polygon.
A lot of NFT projects currently implement some sort of whitelist. It’s a great idea as well. It reduces the number of bots. It gives whales a smaller chance of minting an insane number of tokens, and it can give whitelisted users even more perks if you implement them.
But how do you implement a secure whitelist? You probably want to use a backend server, verify signatures in some way, and protect the smart contract in some way that it can’t be cheated. Merkle Tree Proofs is one of the best ways to do this, so let’s take a look at how to use them for a whitelist.
What are Merkle Trees?
A Merkle Tree is a tree-like structure used in cryptography. A Merkle Tree has any number of nodes that are called leaves. These are the data values that will be hashed, the whitelisted users’ addresses in our case. Each pair of hashes will be hashed again, and then pairs of those hashes will be hashed until only one node is left. The root node.
This makes sure that any changes in the nodes will reflect into the final root node. This makes it fairly easy to check if values are correct.
OpenZeppelin has a smart contract that we can use to easily add the functionality we need to check the proofs easily in our own contract. We only need to create a root node and provide a hash to the function of the smart contract. So let’s create the Merkle Tree and set up the backend we need.
The Backend
As mentioned before, we need a backend. This will be the part where we create the Merkle Tree in such a way that a user cannot alter it to add their own address. This will be done on a server which then calls the smart contract with the right parameters for it to perform the check on-chain.
We need to install a few packages. We will use NPM but you can also use Yarn. We will initialize Node (for testing), install MerkleProofJS, and install Keccak256 since it’s the default hashing function for Solidity.
npm init -y
npm i merkletreejs
npm i keccak256
Once the packages have been installed, we can create a file called index.js
and run it when we want by running node index.js
in the console. I’m going to use console.log
to show each step and give some more info.
The first thing we should do is create a list of whitelisted Ethereum addresses, I’ve added a list of 7 random addresses (including the “0x”) and put them in an array as strings.
Then we need to hash them all using keccak256 to create our leaves. With all the leaves done, we can create the Merkle Tree and get our root hash by using MerkleTreeJS.
With this in place, we can create proofs, send them to the blockchain to be verified, or verify them ourselves before even sending a transaction and possibly wasting the user’s funds.
We retrieve the user’s address from the frontend in some way, hash it the same way as our leaves, and retrieve the proof using the Merkle Tree we already made with correct addresses.
After creating the proof in hex format, we can verify it from the merkleTree
variable as well. This could be useful but isn’t necessary since we only need to send the proof to the blockchain.
The best way to make this secure and easily usable in production is by creating an API that you can call (but others can’t) with the right data that needs to be hashed into a Merkle Proof, fetch the Proof in the frontend, and using web3js send it to the blockchain using a transaction from the user’s wallet.
We can now take a look at how the smart contract will implement the proper functionality to verify the Merkle Proofs we send to it.
The Smart Contract
Verifying the Merkle Proof inside of the smart contract is very easy as it only requires us to call a single function from OpenZeppelin’s MerkleProof contract.
We simply call the verify
function from OpenZeppelin inside of a require
statement before executing the functionality we want to execute, like minting tokens for example.
If you want to be safer, you can create a private variable for the root and set it using a setter function. This is up to you though, it would look better and create some more safety in case something goes wrong with the original root hash.
Conclusion
That’s it. You might need to slightly alter some code for testing using Truffle, or actually implement it inside of an API, but it should be very similar, if not the same.
Thank you so much for reading and have an excellent day.
Consider supporting me by getting a Medium membership. It helps me out a lot, it won’t cost you anything extra, and you can read as many Medium articles as you like!
Follow me on Twitter and gm.xyz to keep up with my projects.
Check out the Kangaroo Mob NFT project.
Check out Pixel Pizzas on Polygon.