Contents

Merkle Airdrop

How to play?

  • Airdrop a bunch of ERC20 tokens to users
  • Deploy a smart contract
  • Send an email with wallet address, token amount and proof to each user
  • The user claims their tokens from the smart contract

Build a merkle tree

  • import { MerkleTree } from 'merkletreejs';
    import keccak256 from 'keccak256';
    import { solidityPacked } from 'ethers';
    
    function main() {
      const whitelist = [
        { address: '0x2173dEd80D3c17D2f349234a31ab8789D28Dd333', amount: 100 },
        { address: '0x2AFAF65965e08DBd0e0aD7a5594E6b5BE398CBd6', amount: 200 },
        { address: '0xfbc89Db3927bCCda61F550d3d9f3998750b7b93f', amount: 300 },
      ];
    
      const leaves = whitelist.map((x) => keccak256(solidityPacked(['address', 'uint256'], [x.address, x.amount])));
      const tree = new MerkleTree(leaves, keccak256, { sortPairs: true });
      const merkleRoot = tree.getRoot().toString('hex');
    
      console.log('Merkle Root:', '0x' + merkleRoot);
      console.log('Merkle Tree:\n', tree.toString());
    
      for (let i = 0; i < leaves.length; i++) {
        const proof = tree.getHexProof(leaves[i], i);
        console.log(`Proof for ${whitelist[i].address}: [${proof}]`);
      }
    }
    
    main();
    

Deploy a contract

  • // SPDX-License-Identifier: UNLICENSED
    pragma solidity ^0.8.26;
    
    import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
    
    contract MerkleAirdrop {
        bytes32 public merkleRoot;
        mapping(address => bool) public claimed;
    
        constructor(bytes32 _merkleRoot) {
            merkleRoot = _merkleRoot;
        }
    
        function claim(address user, uint256 amount, bytes32[] calldata proof) external {
            require(!claimed[user], "Already claimed");
    
            bytes32 leaf = keccak256(abi.encodePacked(user, amount));
            bool isValid = MerkleProof.verify(proof, merkleRoot, leaf);
            require(isValid, "Invalid proof");
    
            claimed[user] = true;
    
            // the logic of minting NFT
            // ...
        }
    }
    
  • See the depolyed MerkleAirdrop on Holesky Testnet