SolidityFoundry Merkle Airdrop

news2024/11/18 14:30:48

Merkle airdrop

   Merkle Tree,也叫默克尔树或哈希树,是区块链的底层加密技术,被比特币和以太坊区块链广泛采用。Merkle Tree允许对大型数据结构的内容进行有效和安全的验证(Merkle Proof)。对于有N个叶子结点的Merkle Tree,在已知root根值的情况下,验证某个数据是否有效(属于Merkle Tree叶子结点)只需要ceil(log₂N)个数据(也叫proof),非常高效。如果数据有误,或者给的proof错误,则无法还原出root根植。 忘记的同学可以参考下方。

Merkle树-CSDN博客文章浏览阅读1.1k次,点赞2次,收藏6次。 merkle树 区块链中的每个区块都包含了产生于该区块的所有交易,且以Merkle树表示。 默克尔树(又叫哈希树)是一种二叉树,由一个根节点、一组中间节点和一组叶节点组成。最下面的叶节点包含存储数据或其哈希值,每个中间节点是它的两个孩子节点内容的哈希值,根节点也是由它的两个子节点内容的哈希值组成。默克尔树的特点是,底层数据的任何变动,都会传递到其父亲节点,一直到树...https://blog.csdn.net/xq723310/article/details/80153072

        所以我们可以利用Merkel的特性。在链下,创建以账户地址和数量为叶子(addr, amount)的Merkel数,并计算出root hash。然后将roothash放到链上,这样就不需要在链上记录大量address和amount,节省gas。当空投开始后,不需要项目方花费gas,去给每一位用户空投,用户可以自行调用合约领取;

        有用户想要领取空投时,可以进行调用合约进行claim,其实就是验证merkel的roothash;由于链上已经保存了一份roothash,只要在链上使用用户提供的信息生成的roothash与之前保存的一致,就可以证明该用户享有领取空投的权利。

准备工作

利用openzeppelin/merkle-tree,生成拥有空投资格用户的Merkel树.

import * as fs from 'fs'
import {StandardMerkleTree} from '@openzeppelin/merkle-tree'

// 1. build a tree
const elements = [
    ['0x0000000000000000000000000000000000000001', 1],
    ['0x0000000000000000000000000000000000000002', 2],
    ['0x0000000000000000000000000000000000000003', 3],
    ['0x0000000000000000000000000000000000000004', 4],
    ['0x0000000000000000000000000000000000000005', 5],
    ['0x0000000000000000000000000000000000000006', 6],
    ['0x0000000000000000000000000000000000000007', 7],
    ['0x0000000000000000000000000000000000000008', 8],
]

let merkleTree = StandardMerkleTree.of(elements, ['address', 'uint256'])
const root = merkleTree.root
const tree = merkleTree.dump()
console.log(merkleTree.render());
fs.writeFileSync('tree.json', JSON.stringify(tree))
fs.writeFileSync('root.json', JSON.stringify({root:root}))

// get proof
const proofs = []
const mtree = StandardMerkleTree.load(JSON.parse(fs.readFileSync("tree.json", "utf8")));
for (const [i, v] of mtree.entries()) {
  proofs.push({'account':v[0], 'amount':v[1],'proof':mtree.getProof(i)})
  if (v[0] === '0x0000000000000000000000000000000000000001') {
    const proof = mtree.getProof(i);
    console.log('Value:', v);
    console.log('Proof:', proof);
  }
}
fs.writeFileSync('proofs.json', JSON.stringify(proofs))

 首先我们使用StandardMerkleTree.of生成了一个八个账户的merkel树并且记录了roothash;然后我们使用,getProof给每个用户都生成他自己的验证proof。用户想要领取空投的时候,需要提供自己proof——其实就是mekle的验证路径,这个一般都是由项目方保存就行了,保存在链下就可以了。这里还输出了三个json文件,这个三个文件,后边测试的时候,需要用到;

  • tree.json:merkle tree的信息;
  • root.json:merkle的roothash
  • proofs.json:所有用户的proof数据

链上合约

merkle 合约,这个我们使用openzeppelin的MerkelProof库,主要是把验证函数实现一下,就可以了。验证的时候,需要提供用户的proof,address,amount,就可以了;

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sol";

contract MerkleAirdrop {
    using MerkleProof for bytes32[];
    bytes32 private _root;

    constructor(bytes32 root) {
        _root = root;
    }

    function verify(
        bytes32[] memory proof,
        address account,
        uint amount
    ) public view returns (bool) {
        bytes32 leaf = keccak256(
            bytes.concat(keccak256(abi.encode(account, amount)))
        );
        return proof.verify(_root, leaf);
    }

    function verifyCalldata(
        bytes32[] calldata proof,
        address account,
        uint amount
    ) public view returns (bool) {
        bytes32 leaf = keccak256(
            bytes.concat(keccak256(abi.encode(account, amount)))
        );
        return proof.verifyCalldata(_root, leaf);
    }

}

airdrop合约,主要实现了claim,享有空投资格的用户,调用之后,而就可以领取空投了;

contract Airdrop is MerkleAirdrop{
    event Claim(address to, uint256 amount);

    MockIToken public token;

    constructor(address _token, bytes32 _root) MerkleAirdrop(_root){
        token = MockIToken(_token);
    }

    function claim(bytes32[] memory proof, address account, uint256 amount)
        external returns (bool)
    {
        verify(proof, account, amount);

        token.mint(account, amount);

        emit Claim(account, amount);
    }
}

interface MockIToken {
    function mint(address to, uint256 amount) external;
}

contract MockToken is ERC20 {
    constructor(string memory name, string memory symbol)
    ERC20(name, symbol) {}

    function mint(address account, uint amount) external {
        _mint(account, amount);
    }
}

 foundry测试

        使用foundry进行,测试部分比较简单,就是测试了,merkelproof的verify函数以及airdrop的cliam函数;这次测试比较有趣的部分是foundry的json解析部分。也不是特别难,大家可以自行搜索foundry的文档进行学习与联系。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "forge-std/Test.sol";
import {stdJson} from "forge-std/StdJson.sol";
import "../src/MerkleAirdrop.sol";
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";

contract MerkleAirdropTest is Test {
    using stdJson for string;

    struct Proof {
        address account;
        uint amount;
        bytes32[] proof;
    }

    string private _jsonTree = vm.readFile("test/data/tree.json");
    string private _jsonRoot = vm.readFile("test/data/root.json");
    string private _jsonProofs = vm.readFile("test/data/proofs.json");
    bytes32 private _rootHash = _jsonRoot.readBytes32(".root");
    MerkleAirdrop private _testing;
    Airdrop private airdrop;
    MockToken private token;

    function setUp() public {
        _testing = new MerkleAirdrop(_rootHash);
        token = new MockToken("test", "TEST");
        airdrop = new Airdrop(address(token), _rootHash);
    }

    function test_verify() external {
        Proof[] memory proofs = abi.decode(_jsonProofs.parseRaw(""), (Proof[]));
        for (uint i = 0; i < proofs.length; ++i) {
            assertTrue(
                _testing.verify(
                    proofs[i].proof,
                    proofs[i].account,
                    proofs[i].amount
                )
            );
        }
    }

    function test_claim() external {
        Proof[] memory proofs = abi.decode(vm.parseJson(_jsonProofs), (Proof[]));
        for (uint i = 0; i < proofs.length; ++i) {
            vm.expectEmit();
            emit Airdrop.Claim(proofs[i].account, proofs[i].amount);
            airdrop.claim(
                proofs[i].proof,
                proofs[i].account,
                proofs[i].amount
            );
            assertEq(token.balanceOf(proofs[i].account), proofs[i].amount);
        }
    }
}

所有代码点这里;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2066110.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

框架——MyBatis的参数传递,基本的增删改

MyBatis环境搭建步骤&#xff08;超全解析&#xff01;&#xff01;&#xff01;&#xff09;&#xff1a; http://t.csdnimg.cn/VDMjDhttp://t.csdnimg.cn/VDMjD 1.参数传递 在AdminDao类中写下面两个方法 单个参数直接传递 &#xff1a; //根据id查询管理员对象 Admin fin…

C语言:函数详解(2)

目录 一、数组做函数参数 二、嵌套调用和链式访问 2.1 嵌套调用 2.2 链式访问 三、函数的声明和定义 3.1 单个文件 3.2 多个文件 一、数组做函数参数 在使用函数解决问题的时候&#xff0c;难免会将数组作为参数传递给函数&#xff0c;在函数内部对数组进行操作。 比如…

推荐大模型书籍|《扩散模型从原理到实战》

就在几年前&#xff0c;“通用人工智能”(Artificial General Inte11igence&#xff0c;AGI)似乎还是一个只存在于科幻小说中的概念&#xff0c;在现实中的实现方法仍在探索中。然而到了2022年&#xff0c;基于大语言模型的AIGC(AI Generated Content)领域的快速发展&#xff0…

List<对象>转JSON字符串以及JSON字符串转List<对象>

签名&#xff1a;但行好事&#xff0c;莫问前程。 文章目录 前言一、工作需求二、List<对象>转JSON字符串三、JSON字符串转List<对象>总结 前言 记录一下List<对象>转JSON字符串以及JSON字符串转List<对象> 一、工作需求 产品需求中有一个需求要求页…

最少钱学习并构建大模型ollama-llama3 8B

学习大模型时可能面临一些困难&#xff0c;这些困难可能包括&#xff1a; 计算资源限制&#xff1a;训练大模型通常需要大量的计算资源&#xff0c;包括CPU、GPU等。如果设备资源有限&#xff0c;可能会导致训练时间长、效率低下或无法完成训练。 内存限制&#xff1a;大模型通…

卫星图揭秘《黑神话:悟空》山西取景地

8月20日&#xff0c;国产单机游戏巨制《黑神话&#xff1a;悟空》在全球同步解锁&#xff0c;游戏以《西游记》为背景&#xff0c;并以其精美的画面和深刻的剧情吸引了全球玩家&#xff0c;被誉为“国产游戏之光”。 游戏画面在国内36处实地取景&#xff0c;其中有27个来自山西…

日志审计Graylog 使用教程-kafka收取消息

Graylog 是一个开源的日志管理和分析平台。它主要用于收集、存储、处理和分析大量的日志数据。Graylog 的核心功能包括&#xff1a; 日志收集&#xff1a;Graylog 可以从各种来源&#xff08;如服务器、网络设备、应用程序等&#xff09;收集日志数据。它支持多种输入方式&…

122-域信息收集应用网络凭据CS插件AdfindBloodHound

参考&#xff1a;【内网安全】域信息收集&应用网络凭据&CS插件&Adfind&BloodHound_ladon adfinder bloodhound-CSDN博客 工作组和域环境 我的理解&#xff1a; 工作组就是还是局域网一样只是大一点里面的电脑很多&#xff0c;每个电脑还是都是单独的电脑没有…

萤石C++ SDK Demo播放4G摄像头

萤石开放平台官方提供的Demo是有问题的&#xff0c;无法直接使用。提交工单后&#xff0c;技术人员回复要修改配置文件才能使用。 1. 下载SDK 2.修改配置文件EZPCOpenSDK_v5.1.18_build20230808\demo\win32\EzvizQtDemo.ini 3.运行EzvizQtDemo1.exe 点击左上角【登录】按钮&am…

黑神话悟空,高清壁纸、原画,游戏截图

黑神话悟空&#xff0c;高清壁纸、原画&#xff0c;游戏截图&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/cd17c05c4f33

安卓工控主板在轨道交通中的应用特点

安卓工控主板在轨道交通中的应用特点主要体现在以下几个方面&#xff1a; 一、高效的数据处理与通信能力 强大的处理能力&#xff1a;安卓工控主板通常搭载高性能的处理器&#xff0c;如某些型号可能搭载飞腾D2000八核CPU等&#xff0c;这些处理器能够高效处理轨道交通系统中…

C语言学习——用指针处理链表

目录 11.7用指针处理链表 链表概述 简单链表 处理动态链表所需的函数 malloc函数 calloc函数 free函数 建立动态链表 输出链表 对链表的删除操作 对链表的插入操作 对链表的综合操作 11.7用指针处理链表 链表概述 链表是一种常见的数据结构。它是动态地进行存储分…

【架构设计】-- aarch(ARM) and X86

1、aarch(ARM) 架构 &#xff08;1&#xff09;操作系统支持&#xff1a;早期为 32 位操作系统&#xff0c;目前大部分都是 64 位操作系统 &#xff08;2&#xff09;全称&#xff1a;Advanced RISC Machine&#xff0c;由英国ARM Holdings plc公司开发 这种架构主要⽤于智能…

chatglm3-6b下载时,需要下载哪些文件

在huggingface或modelscope上下载chatglm3-6b时&#xff0c;会发现有两种可执行文件&#xff0c;一种是.bin&#xff0c;一种是.safetensors&#xff0c;在使用的时候你如果直接用git命令git clone https://www.modelscope.cn/ZhipuAI/chatglm3-6b.git直接下载&#xff0c;你会…

【中仕公考怎么样】事业编ABCDE类对应的专业

事业编考试分为ABCDE五个类别&#xff0c;对应的专业分别是&#xff1a; 综合应用能力(A类)&#xff1a; 招聘专业&#xff1a;汉语言与文秘类、法律类、新闻传播类、治安学、治安管理、社会工作、老年服务、青少年服务、思想政治教育、安全工程、公共事业管理、行政管理、人力…

前端打字效果

页面效果链接&#xff0c;点击查看https://live.csdn.net/v/419208?spm1001.2014.3001.5501 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, …

图像处理之:Video Processing Subsystem(一)

免责声明&#xff1a; 本文所提供的信息和内容仅供参考。作者对本文内容的准确性、完整性、及时性或适用性不作任何明示或暗示的保证。在任何情况下&#xff0c;作者不对因使用本文内容而导致的任何直接或间接损失承担责任&#xff0c;包括但不限于数据丢失、业务中断或其他经济…

deque容器的所有操作

1.deque原理 2.deque构造函数 只读迭代器这么写&#xff1a; 3.deque赋值操作 4.deque大小操作 5.deque插入和删除操作 6.deque数据存取 7.deque排序

Linux阿里云服务器,利用docker安装EMQX

第一步&#xff0c;给云服务器docker进行加速 阿里云搜索“镜像加速器”&#xff0c;找到下面这个菜单&#xff0c;点进去 然后找到镜像工具下的镜像加速器 把这个加速器地址复制 然后在自己的云服务器中&#xff0c;找到docker的文件夹 点击json配置文件 把地址修改为刚刚…

边坡监测预警摄像机

边坡是指山体或河岸等地表的斜坡部分&#xff0c;由于受到地质构造、气候变化等因素的影响&#xff0c;边坡可能存在塌方、滑坡等危险情况。为了及时监测和预警边坡的变化情况&#xff0c;可以使用边坡监测预警摄像机 。 边坡监测预警摄像机是一种结合了摄像技术和智能算法的设…