solidity——默克尔树实现零成本空投

这篇具有很好参考价值的文章主要介绍了solidity——默克尔树实现零成本空投。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在web3.0中,大家对空投肯定不陌生。如果项目方要大规模给用户进行空投,所需gas将是一笔不小的开支,特别是在以太坊链上,其次是大规模连续转账交易所涉及的事物原子性等问题也值得考虑。接下来将介绍一种相对低成本、安全性高的空投实现方式:通过默克尔树验证用户信息有效性并发放空投。

一、默克尔树介绍

默克尔树(Merkle Tree)是一种二叉树数据结构,它使用哈希函数将大量数据块组织成树状结构,用于快速验证大型数据集的完整性。默克尔树的主要原理包括以下几个关键点:

  1. 哈希函数: 默克尔树使用哈希函数对数据块进行哈希运算。常用的哈希函数如 SHA-256,SHA-3 等。

  2. 叶子节点: 待存储的原始数据被分割成固定大小的块,每个块作为树的叶子节点,通过哈希函数生成一个哈希值。

  3. 构建树结构: 叶子节点按顺序两两配对,将它们的哈希值拼接后再次进行哈希运算,生成一个新的哈希值。这个过程一直重复,直到树的根节点生成。

  4. 根节点: 默克尔树的根节点是最终的哈希值,称为 Merkle 根。它是树中所有数据块的紧凑表示。

  5. 证明: 如果需要验证某个特定数据块是否在树中,可以提供一组哈希值,称为默克尔证明(Merkle Proof)。这个证明包括沿着树路径的一系列哈希值,从叶子节点到根节点。通过检查证明的有效性,可以快速验证数据块的完整性。

Merkle Tree​​允许对大型数据结构的内容进行有效和安全的验证(​​Merkle Proof​​)。对于有​​N​​个叶子结点的​​Merkle Tree​​,在已知​​root​​根值的情况下,验证某个数据是否有效(属于​​Merkle Tree​​叶子结点)只需要​​log(N)​​个数据(也叫​​proof​​),非常高效。如果数据有误,或者给的​​proof​​错误,则无法还原出​​root​​根植。 下面的例子中,叶子​​L1​​的​​Merkle proof​​为​​Hash 0-1​​和​​Hash 1​​:知道这两个值,就能验证​​L1​​的值是不是在​​Merkle Tree​​的叶子中。 因为通过叶子​​L1​​我们就可以算出​​Hash 0-0​​,我们又知道了​​Hash 0-1​​,那么​​Hash 0-0​​和​​Hash 0-1​​就可以联合算出​​Hash 0​​,然后我们又知道​​Hash 1​​,​​Hash 0​​和​​Hash 1​​就可以联合算出​​Top Hash​​,也就是root节点的hash。

solidity——默克尔树实现零成本空投,Solidity基础,哈希算法,算法,区块链,web3.0

二、实现方式

合约代码如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

contract AirDrop is ReentrancyGuardUpgradeable{

    uint256 private index;
    using SafeERC20 for IERC20;
    mapping (bytes32 => mapping (address => uint256)) private indexs;
    mapping(uint256 => mapping (address => bool)) private isClaimed;
    
    //设置根、空投token
    function setRootAndToken (bytes32 _root,address _address) external {
        indexs[_root][_address] = ++index;
    }

    function hasClaimed(bytes32 _root,address token,address _address) external view returns (bool) {
        uint256 _index = indexs[_root][token];
        return isClaimed[_index][_address];
    }
    
    function claim(
        address account,
        uint256 amount,
        bytes32[] calldata merkleProof,
        bytes32 _root,
        address token
    ) external nonReentrant{
        uint256 _index = indexs[_root][token];
        require(!isClaimed[_index][account], 'Already claimed.');

        bytes32 _leaf = keccak256(abi.encodePacked(account, amount));
        bool isValidProof = MerkleProof.verifyCalldata(merkleProof,_root,_leaf);
        require(isValidProof, 'Invalid proof.');

        isClaimed[_index][account] = true;
        IERC20 dropToken = IERC20(token);
        uint256 bal = dropToken.balanceOf(address(this));
        require(
            bal >= amount,
            "Insufficient balance"
        );

        dropToken.safeTransfer(account, amount);
    }
}
  • 后端导入空投白名单,对数据进行持久化,通常包括空投用户地址、空投数量,并计算出默克尔树root。
  • 通过调用合约中的setRootAndToken(bytes32 _root,address _address)函数,提前将root和空投token存储在合约状态变量中,并向合约账户转入本次空投总共花费的token数量。
  • 用户登陆前端页面后,从后端获取自己的空投数量、root、空投token等调用claim(address account,uint256 amount,bytes32[] calldata merkleProof,bytes32 _root,address token)所需参数,需要用户进行主动领取(其实是将成本转换到用户身上,目前主流的方式)。

三、验证 

这里通过hardhat的Mocha+chai.js来做测试用例:

import {
    time,
    loadFixture,
  } from "@nomicfoundation/hardhat-toolbox/network-helpers";
  import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
  import { expect } from "chai";
  import { ethers } from "hardhat";
  import { MerkleTree } from 'merkletreejs'

  // Same as `abi.encodePacked` in Solidity
  function encodePacked(address:any, spots:any) {
    return ethers.solidityPacked(["address", "uint256"],[address, spots])
  }

  describe("AirDrop",function(){
    async function deployTokenFixture() {
        const [owner,account1,account2,account3] = await ethers.getSigners();
        const token = await ethers.getContractFactory("AirDrop")
        const _token = await token.deploy()
        await _token.waitForDeployment();

        const myToken = await ethers.getContractFactory("MyToken")
        const _myToken = await myToken.deploy(owner.address,"My Token","MTK",100);
        await _myToken.waitForDeployment();

        console.log("AirDrop: "+await _token.getAddress());
        console.log("MyToken: "+await _myToken.getAddress());

        return {_token,_myToken,owner,account1,account2,account3}
    }

    describe("test",function(){
        it("",async function () {
            const { _token,_myToken, owner,account1,account2,account3 } = await loadFixture(deployTokenFixture);
            let tokenAdd = await _token.getAddress()
            let myTokenAdd = await _myToken.getAddress()
            
            let tx = await _myToken.transfer(tokenAdd,2);
            await tx.wait();
            console.log("AirDrop bal = ",await _myToken.balanceOf(tokenAdd))
            let list = [
                encodePacked(owner.address, 2),
                encodePacked(account1.address, 2),
              ];

            console.log("list = ",list);
            
            //构造树
            let tree = new MerkleTree(list, ethers.keccak256, { hashLeaves: true,sortPairs: true });
            // 获取树根
            let root = tree.getHexRoot();
            console.log("tree = ",tree.toString());
            console.log("root = ",root);
            await _token.setRootAndToken(root,myTokenAdd)

            //获取树叶
            let leaf = ethers.keccak256(list[0])
            //获取证明
            let proof = tree.getHexProof(leaf);
            console.log("leaf = ",leaf);
            console.log("proof = ",proof);
            
            console.log("hasClaimed = ",await _token.hasClaimed(root,myTokenAdd,owner.address))
            await _token.claim(owner.address, 2,proof,root,myTokenAdd)
            console.log("hasClaimed = ",await _token.hasClaimed(root,myTokenAdd,owner.address))
        })
    })
  })

这是验证结果:

AirDrop
    test
AirDrop: 0x5FbDB2315678afecb367f032d93F642f64180aa3
MyToken: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
AirDrop bal =  2n
list =  [
  '0xf39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000002',
  '0x70997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000000000000000002'
]
tree =  └─ 1a7a429f400d85c26a5848cca75bdd5058aa94d7ff5b42d936d8b1c2d8ef9743
   ├─ f23ba99f1ec426341536c572dc91ecc790ff86f74ea7aa03df4846a680dd4e73
   └─ 274996539fafc4b0887fdcfbe1c73bc1147c223b1ebedc6e4e8462a80707d2c7

root =  0x1a7a429f400d85c26a5848cca75bdd5058aa94d7ff5b42d936d8b1c2d8ef9743
leaf =  0xf23ba99f1ec426341536c572dc91ecc790ff86f74ea7aa03df4846a680dd4e73
proof =  [
  '0x274996539fafc4b0887fdcfbe1c73bc1147c223b1ebedc6e4e8462a80707d2c7'
]
hasClaimed =  false
hasClaimed =  true
      ✔  (2099ms)


  1 passing (2s)

 欢迎大家批评指导,共同学习。(太困了!!!)文章来源地址https://www.toymoban.com/news/detail-765158.html

到了这里,关于solidity——默克尔树实现零成本空投的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 区块链——默克尔树

    默克尔树是一种二叉树,由一组叶节点、一组中间节点和一个根节点构成,看下图: 我们从最底部开始看,D0、D1、D2和D3是叶子节点包含的数据,也就是叶子节点的value,继续往上看,N0、N1、N2和N3是就是叶子节点,它是将数据(也就是D0、D1、D2和D3)进行hash运算后得到的ha

    2024年04月10日
    浏览(26)
  • 区块链的数据结构(二)——默克尔树(Merkle Tree)

            区块链中的另外一个数据结构是Merkle tree,在比特币中使用的就是这种结构:         可能没有听说过Merkle tree,但一定听说过binary tree(二叉树)。         Merkle tree和binary tree的区别:Merkle tree用哈希指针代替了普通的指针         每个框内的两个哈希值

    2024年02月21日
    浏览(30)
  • 区块链学习笔记(2)难度整定,区块形成,区块体,Merkle树,Merkle Proof默克尔证明

           是在每个完整节点中独立自动发生的。每2016个区块,所有节点都会按统的公式自动调整难度,这个公式是由最新2016个区块的花要时长与期望时长(期望时长为20160分钟,即两周,是按每10分钟一个区块的产生速率计算出的总时长 )比较得出的,根据实际时长与期望时

    2023年04月08日
    浏览(67)
  • solidity实现智能合约教程(3)-空投合约

    猛戳订阅学习专栏🍁🍁 👉 solidity系列合约源码+解析 👈 🍁🍁 空投就是一种营销策略,通过空投活动将某种数字货币或代币分发给用户,通常需要用户完成一项简单的任务,如分享新闻、介绍朋友或拥有某种数字货币,目前也被广泛应用于宣传新币种,在数字货币市场中

    2024年02月07日
    浏览(57)
  • solidity函数签名的实现-solidity实现智能合约教程(8)

    猛戳订阅学习专栏🍁🍁 👉 solidity系列合约源码+解析 👈 🍁🍁 函数选择器: solidity调用函数时,calldata的前4个字节为指定要调用的函数,这4个字节称为函数选择器。 以下面的代码为例。它通过地址addr的调用合约的transfer方法。 abi.encodeWithSignature(…)返回的前4个字节是函数

    2024年02月16日
    浏览(40)
  • Solidity实现简单的智能合约

    Solidity是以太坊中编写智能合约的语言,编译成字节码之后可以运行在以太坊虚拟机上。solidity语法与JavaScript很相似,有编程基础的开发者可以轻松上手,智能合约一旦部署就无法修改。 首先介绍我们的编译工具: Remix remix是一款非常好用的在线编译工具,我们通过这个工具

    2023年04月09日
    浏览(30)
  • Solidity--如何实现椭圆算法签名(ECDSA)

    椭圆算法签名 椭圆曲线算法签名(ECDSA)是一种数字签名算法,其基于椭圆曲线密码学(ECC)。它是一种非对称密码算法,即发送方和接收方都有不同的密钥。在数字签名中,发送方使用它的私钥对数据进行签名,以证明数据的完整性和发送方身份。接收方可以使用发送方的

    2023年04月09日
    浏览(24)
  • Factory工厂合约的实现-solidity实现智能合约教程(6)

    猛戳订阅学习专栏🍁🍁 👉 solidity系列合约源码+解析 👈 🍁🍁 何为工厂合约呢?相信有过编程经验的小伙伴都听说过工厂模式,此处的工厂合约其实和他的概念相似,该合约将承担创建其他合约的任务。在基于类的编程中,此模式的主要动机来自单一职责原则(一个类不需

    2024年02月11日
    浏览(30)
  • golang 实现solidity keccak256(abi.encode(xxx))签名

    golang 代码 需要找到对应 solidity方法的abi。 solidity 代码

    2024年01月17日
    浏览(30)
  • 【区块链 | Solidity】跟我学 Solidity(7):事件

    如何使用事件记录数据 原文: https://medium.com/better-programming/learn-solidity-events-2801d6a99a92 作者: wissal haji 欢迎订阅《Solidity智能合约零基础开发教程专栏》系列文章。 在今天的文章中,我们将看到如何使用web3.js从区块链中读取事件。 Solidity中的事件就像你习惯于使用其他语

    2024年01月25日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包