Merkle Tree Airdrop
May 3rd, 2022

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

merkle tree原理

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

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

步骤:

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;
});
Subscribe to point
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from point

Skeleton

Skeleton

Skeleton