# Merkle Tree Airdrop **Published by:** [point](https://paragraph.com/@point/) **Published on:** 2022-05-03 **URL:** https://paragraph.com/@point/merkle-tree-airdrop ## Content 现在都用merkle tree 空投,记录下merkle tree原理用leaves一层又一层计算出root hash,验证时拿leaf+和该leaf有关的leaf再计算一遍root hash是否一样 具体说明看这里,肯定能懂 https://www.npmjs.com/package/merkletreejs步骤:1.生成root hashconst { 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; }); ## Publication Information - [point](https://paragraph.com/@point/): Publication homepage - [All Posts](https://paragraph.com/@point/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@point): Subscribe to updates