In Part 1 and Part 2 of this series, we have explored what are merkle trees, definition, concept and how merkle trees can be used to check the proof of inclusion. In this part, let’s checkout how we can perform merkle verification in smart contracts and in javascript.
Merkle Verification in Smart contracts
First thing to answer is: Why? There are a number of reasons for computing merkle verifications on-chain. The most popular use case is “Checking if an address is whitelisted”, especially, in case of presales, and asset distributions like ERC20 tokens and NFTs. There are other use cases like token bridges which also rely on merkle proofs to confirm if a particular “token lock” transaction has been mined on a chain correctly.
On chain merkle verification process can be divided in two parts:
- Writing a smart contract to verify the proof given root, leaf and proof data
- Offchain script ( Typescript in our case ) to generate root, leaf and proof data hashes and to interact with our smart contract
Smart contract for merkle verification:
The above code represents a very minimal and basic contract for performing merkle verifications.
- We are using the OpenZepplin library to import the MerkleProof.sol utility contract to perform the verification.
- We pass merkle tree root in the constructor. This merkle root has been computed offchain using the script which we will see in the next section.
- Contract also exposes a public view function “verifyMerkleProof” which accepts two params:
- bytes32[] calldata proof: data required for merkle verification of a leaf
- address elementToProve: Actual leaf ( often this is hashed using algorithms like keccak256 )
- This function calls OZ’s MerkleProof.sol utility and passes proofData, root and leaf to verify if the merkle tree formed using proofData and leaf node matches the root with which the contract was initialized.
Offchain script for merkle tree generation and interaction
- If you check the main function in above typescript file, we are first calling generateMerkleTree function which is responsible for generating the merkle tree using the sampledata which is imported at the start of the file.
- Leaves are first encoded following solidity’s abi.encodePacked format and then are passed to MerkleTree instance.
- We are using this npm pacakge for generating merkle trees and required data.
- In deployContract function, we are deploying the MerkleCerifier.sol contract. Contract requires the merkle tree root to be passed as a parameter in the constructor.
- And then comes the main function: testMerkleVerification
testMerkleVerification() function:
- This function creates a encoded version of two leaves. One of them is valid and other one is invalid.
- After this, the function generates proof for both of these leaves. Ofcourse, one of the proofs is valid, other one is invalid.
- Then calls smart contract which we have deployed earlier and triggers verifyMerkleProof function by passing all the required params.
Merkle Verification in Javascript
We can also perform end-to-end merkle verifications purely in javascript / typescript via the same npm package which we used above. The only difference being this time we will relay on the package for verification instead of a smart contract. Our npm package has following function exported which can be used for merkle verification.
await merkleTree.verify(proof, leaf, root);
All of theabove discussed files and code is available in this hardhat project: https://github.com/ashwinYardi/merkletree-examples
Github repository also has instructions on how to run the scripts and test merkle verification. Give it a go and in case of anything, do let me know. Would love to discuss further on this topic.
Leave a Reply