# Merkle Tree Airdrop

By [point](https://paragraph.com/@point) · 2022-05-03

---

现在都用merkle tree 空投，记录下

merkle tree原理
-------------

用leaves一层又一层计算出root hash，验证时拿leaf+和该leaf有关的leaf再计算一遍root hash是否一样

具体说明看这里，肯定能懂

[https://www.npmjs.com/package/merkletreejs](https://www.npmjs.com/package/merkletreejs)

步骤：
---

### 1.生成root hash

    const { MerkleTree } = require("merkletreejs");
    const keccak256 = require("keccak256");
    //获取hash root
    const leaves = ["地址1","地址2".....].map((x) => keccak256(x));
    const tree = new MerkleTree(leaves, keccak256);
    const root = tree.getHexRoot();
    
    //获取leaf验证的路径
    const leaf = keccak256("a");
    const proof = tree.getProof(leaf);
    

### 2.solidity校验，airdorp

    //SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;
    
    import "@openzeppelin/contracts/access/Ownable.sol";
    import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
    import "hardhat/console.sol";
    contract TestMPT is Ownable {
        //记录root hash
        bytes32 public saleMerkleRoot;
        //是否领取过空投
        mapping(address => bool) public claimed;
        //设置root hash
        function setSaleMerkleRoot(bytes32 merkleRoot) external onlyOwner {
            saleMerkleRoot = merkleRoot;
        }
        //获取root hash
        function getSaleMerkleRoot() external view returns(bytes32) {
            return saleMerkleRoot;
        }
        
        //校验是否合法
        modifier isValidMerkleProof(bytes32[] calldata merkleProof, bytes32 root,bytes memory leaf) {
            require(
                MerkleProof.verify(
                    merkleProof,
                    root,
                    keccak256(abi.encodePacked(leaf))
                ),
                "Address does not exist in list"
            );
            _;
        }
        //获取airdrop
        function getDrop(bytes32[] calldata merkleProof,bytes memory leaf,uint256 _amount)
            external
            isValidMerkleProof(merkleProof, saleMerkleRoot,leaf)
        {
            require(!claimed[msg.sender], "Address already claimed");
            claimed[msg.sender] = true;
            //todo 业务处理
        }
    }
    

### 3.etherjs调用airdrop合约

    const { ethers, artifacts, network } = require("hardhat");
    const { writeAbiAddr } = require("./artifact_saver.js");
    
    async function main() {
    
      const contactName = "TestMPT";
      const Greeter = await ethers.getContractFactory(contactName);
      const greeter = await Greeter.deploy();
      await greeter.deployed();
    
      // 将abi address等信息保存到文件
      const artifact = await artifacts.readArtifact(contactName);
      await writeAbiAddr(artifact, greeter.address, contactName, network.name);
    
      //设置root hash
      const [owner] = await ethers.getSigners();
      const counter = await ethers.getContractAt(
        contactName,
        greeter.address,
        owner
      );
      await counter.setSaleMerkleRoot(   "0xc7ec7ffb250de2b95a1c690751b2826ec9d2999dd9f5c6f8816655b1590ca544"
      );
      const merkleRoot = await counter.getSaleMerkleRoot();
    //数组里的是获取leaf的验证路径，第二个参数是要验证的内容
    //特殊情况，你只有一个leaf，那要传空数组
      const result = await counter.getDrop(
        [
          "0x1575cc1dded49f942913392f94716824d29b8fa45876b2db6295d16a606533a4",
          "0x6c42c6099e51e28eef8f19f71765bb42c571d5c7177996f177606138f65c0c2b",
          "0xb0d6f760008340e3f60414d84b305702faa6418f44f31de07b10e05bf369eb3b",
          "0x4c880bf401add28c4e51270dfe16b28c3ca1b3d263ff7c5863fc8214b4046364",
        ],
        "0xc12ae5Ba30Da6eB11978939379D383beb5Df9b33"
      );
    }
    
    main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });

---

*Originally published on [point](https://paragraph.com/@point/merkle-tree-airdrop)*
