solidty实现默克尔树空投

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

为了避免大量gas消耗,我们将在链下要根据白名单数据生成 Merkle Tree,我们这里使用 Javascript 来完成相关工作。

首先需要安装两个依赖包:

npm install --save keccak256 merkletreejs

实例代码如下:

const {ethers} = require("ethers");
const { MerkleTree } = require('merkletreejs');
const keccak256 = require('keccak256');
async function main() {

// 白名单地址,这里采用了硬编码,实际开发应该从数据库读取
// 这里我们随机生成几个地址,作为白名单示例
let list =[{address:"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"},{address:"0x39Ef50bd29Ae125FE10C6a909E42e1C6a94Dde29"},{address:"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"},{address:"0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2"}];
    //叶子结点数据
  let leafs = [];
  for (let k = 0; k < list.length; k++) {
    let leaf = ethers.utils.solidityKeccak256(["address"], [list[k].address]);
    leafs.push(leaf);
  }

  //树根
  let tree = new MerkleTree(leafs, keccak256, { sort: true });
 // 打印查看 Root 数据,需要设置到合约中
  let root = tree.getHexRoot();
  console.log("root = ",root);

  const rootHash = tree.getRoot();
  console.log("rootHash = ",rootHash);
  //叶子proof
  let proofs = [];
  leafs.map((item) => {
    proofs.push(tree.getHexProof(item));
  });

  let res = [];
  for (let index = 0; index < list.length; index++) {
    res.push([list[index].address, proofs[index]]);
  }
   //组装的数据
  console.log("res = ",res);
}


main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

 首先在Airdrop 合约里面设置 root

//set root
    function setMerkleRoot(bytes32 _root) external onlyOwner {
        root = _root;
    }

 然后调用getDrop 获取空投

完整的代码如下:

Airdrop.sol

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

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "./NFT.sol";

contract Airdrop is Ownable {
    bytes32 root;
    mapping(address => bool) isGet;
    using SafeMath for uint256;
    constructor()  {
        
    }
    NFT public nft = NFT(0xD4Fc541236927E2EAf8F27606bD7309C1Fc2cbee) ;
    address public constant feeAddress = 0xA38f69a8177BF32c65234BA340101e9441A5BEFa;
    uint256 public nftPrice = 0.01 ether;
    uint256 public totalNft = 300;
    uint256 public mintCount = 0;
    uint256 public startTime = 1656984610;
    uint256 _startTokenId = 10;
    /* ============================================== EVENT START ================================================ */
    event GetDrop(address sender,uint256 number,uint256[] tokens,uint256 totalMoney,uint256 createTime);
    event SetNFTAddress(address sender,address oldAddress,address newAddress,uint256 createTime);
    event SetSaleTotal(address sender,uint256 oldNumber,uint256 newNumber,uint256 createTime);
    event SetStartTime(address sender,uint256 oldStartTime,uint256 newStartTime,uint256 createTime);
    event SetStartToken(address sender,uint256 oldStartToken,uint256 newStartToken,uint256 createTime);
    event SetNftPrice(address sender,uint256 oldPrice,uint256 newPrice,uint256 createTime);
    /*=============================================== EVENT END ================================================== */
    /* =================================== Mutable Functions START ================================ */
    
    //set root
    function setMerkleRoot(bytes32 _root) external onlyOwner {
        root = _root;
    }
   //varify and get drop
    function getDrop(
        address _address,
        uint256 _number,
        bytes32[] calldata _proofs
    ) external payable{
        require(startTime<=block.timestamp);
        require(isGet[_address] == false, "has got");
        uint256 totalMoney = nftPrice.mul(_number);
        require(msg.value == totalMoney,"amount fail");
        bytes32 _leaf = keccak256(abi.encodePacked(_address));
        bool _verify = MerkleProof.verify(_proofs, root, _leaf);
        require(_verify, "fail");
        isGet[_address] = true;
        uint256[] memory _tokens = buyNft(_number);
        payable(feeAddress).transfer(msg.value);
        emit GetDrop(_msgSender(),_number,_tokens,totalMoney,block.timestamp);
    }

    function setNFTAddress(address _address) public onlyOwner{
        emit SetNFTAddress(_msgSender(),address(nft),_address,block.timestamp);
        nft = NFT(_address);
    }

    function buyNft(uint256 number) internal returns(uint256[] memory){
        require(number>0,"min limit 1");
        require(number<=residual(),"Exceed maximum mint");
        mintCount = mintCount.add(number);
        uint256 tokenBase = _startTokenId;
        _startTokenId = _startTokenId.add(number);
        uint256[] memory _tokens = new uint256[](number);
        for(uint256 i=0;i<number;i++){
            uint256 tokenId = tokenBase.add(i);
            _tokens[i] = tokenId;
            nft.safeMint(_msgSender(), tokenId);
        }
        return _tokens;
    }

    

    function setTotalNft(uint256 _total) public onlyOwner{
        emit SetSaleTotal(_msgSender(),totalNft,_total,block.timestamp);
        totalNft = _total;
    }

    function setStartTime(uint256 _startTime) public onlyOwner{
        emit SetStartTime(_msgSender(),startTime,_startTime,block.timestamp);
        startTime = _startTime;
    }
    function _setStartTokenId(uint256 _startToken) public onlyOwner{
        require(_startToken > _startTokenId,"_startToken must > old number");
        emit SetStartToken(_msgSender(),_startTokenId,_startToken,block.timestamp);
        _startTokenId = _startToken;
    }
    function _setNftPrice(uint256 _price) public onlyOwner{
        require(_price > 0,"_startToken must > old number");
        emit SetNftPrice(_msgSender(),nftPrice,_price,block.timestamp);
        nftPrice = _price;
    }
     /* =================================== Mutable Functions END ================================ */
    /* ====================================== View Functions START ================================ */
    function hasGet(address _address) public view returns (bool) {
        return isGet[_address];
    }
    function residual() public view returns(uint256){
        return totalNft.sub(mintCount);
    }
    /* ====================================== View Functions END ================================ */
}

NFT.sol文章来源地址https://www.toymoban.com/news/detail-517524.html

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

import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract NFT is ERC721PresetMinterPauserAutoId {
    using Counters for Counters.Counter;
    using EnumerableSet for EnumerableSet.UintSet;
    using SafeMath for uint256;
    // Count the amount mint
    Counters.Counter private _tokenCounter;
    EnumerableSet.UintSet private _tokenList;
    // limit  mint max 10000
    uint256 constant totalNumber = 10240;
    mapping(address=>EnumerableSet.UintSet)  private userTokens;
    address immutable dead = 0x000000000000000000000000000000000000dEaD;
    //base
    string private baseTokenURI = "ipfs://QmRcb6Mpwxwnjr3d5hPiXj6vdi4hZv6WGeGQWYgib2BDpA/";
    bytes32 public constant SUPER_ROLE = keccak256("SUPER_ROLE");
    constructor() ERC721PresetMinterPauserAutoId("Hash Eagle", "Eagle", "") {
        _setupRole(SUPER_ROLE, _msgSender());
    }
    uint256 constant openTotal = 7000;
    uint256 constant foundTotal = 3240;
    EnumerableSet.UintSet _superTokens;
    EnumerableSet.UintSet _openMints;
    EnumerableSet.UintSet _foundMints;
    /* ==================================== EVENT START ======================================== */
    event SafeMint(address indexed sender,address indexed to, uint256 tokenId,  uint256 createtime);
    event ChangeURI(address indexed sender, string oldUri, string newUri, uint256 createtime);
    event Burn(address indexed sender, address owner,uint256 tokenId, uint256 createtime);
    event AddSuperId(address indexed sender, uint256[] tokenIds, uint256 createtime);
    /* ==================================== EVENT END ======================================== */

    /* ==================================== ERROR START ======================================== */
    error Unauthorized();
    /* ==================================== ERROR END ======================================== */


    /* =================================== Mutable Functions START ================================ */
    function safeMint(address _to, uint256 _tokenId) public onlyRole(MINTER_ROLE){
        require(_openMints.length()<openTotal,"total limit mint 7000");
        require(!_superTokens.contains(_tokenId),"is not super tokenId");
        _saMinit(_to,_tokenId);
        require(_openMints.add(_tokenId),"_openMints add error");
    }
    function _saMinit(address _to, uint256 _tokenId)  internal {
        require(_tokenId>0,"tokenId can not zero");
        require(mintCount()<totalNumber,"mint  limit");
        require(!_tokenList.contains(_tokenId),"cannot repeat mint");
        require(_tokenList.length()<totalNumber,"token  mint limit 10240");
        //mint(If _tokenId already exists, the call will return with an error)
        _mint(_to, _tokenId);
        
        require(userTokens[_to].add(_tokenId),"userTokens add error");
        require(_tokenList.add(_tokenId),"_tokenList add error");
        emit SafeMint(_msgSender(),_to,_tokenId,block.timestamp);
    }
    function safeMintSuperId(address _to, uint256 _tokenId) public onlyRole(SUPER_ROLE){
        require(_foundMints.length()<foundTotal,"total limit mint 3240");
        // require(_superTokens.contains(_tokenId),"is not super tokenId");
        _saMinit(_to,_tokenId);
        require(_foundMints.add(_tokenId),"_foundMints add error");
    }

    function changeURI(string calldata _tokenURI) external onlyRole(MINTER_ROLE) {
        require(keccak256(abi.encodePacked(_tokenURI)) != keccak256(abi.encodePacked(baseTokenURI)), "same tokenURI");
        emit ChangeURI( _msgSender(), baseTokenURI, _tokenURI, block.timestamp);
        baseTokenURI = _tokenURI;
    }


    function addSuperId(uint256[] calldata tokenIds) public  onlyRole(SUPER_ROLE){
        require(tokenIds.length>0 && tokenIds.length<=30 ,"length must >0 or length <=30");
        uint256 total = 0;
        total = total.add(_foundMints.length()).add(tokenIds.length).add(_superTokens.length());
        require(total<=foundTotal,"limit 3240");
        for(uint256 i=0;i<tokenIds.length;i++){
            require(tokenIds[i]>0,"tokenId can not zero");
            require(!_exists(tokenIds[i]),"It's already cast");
            require(!_superTokens.contains(tokenIds[i]),"It's already cast");
            require(_superTokens.add(tokenIds[i]),"_superTokens add error");
        }
        emit AddSuperId(_msgSender(), tokenIds, block.timestamp);
    }

    //get mint count
    function mintCount() view public returns(uint256){
        return _tokenList.length();
    }
    function mint(address /*to*/) public virtual override(ERC721PresetMinterPauserAutoId){
        revert Unauthorized();
    }

    function _burn(uint256 tokenId) internal override(ERC721) {
        address owner = ERC721.ownerOf(tokenId);
        transferFrom(owner, dead, tokenId);
        emit Burn(_msgSender(), owner,tokenId, block.timestamp);
    }
    /* =================================== Mutable Functions END ================================ */

    /* ====================================== View Functions START ================================ */
    function getMyNFT() external view returns(uint256[] memory) {
        return userTokens[_msgSender()].values();
    }
    function getSuperIds() public view  returns (uint256[] memory) {
        return _superTokens.values();
    }
    function totalSupply() public view virtual override(ERC721Enumerable) returns (uint256) {
        return totalNumber;
    }

    function getOpenMints() public view  returns (uint256[] memory) {
        return _openMints.values();
    }
    function getFoundMints() public view  returns (uint256[] memory) {
        return _foundMints.values();
    }

    function _baseURI() internal view virtual override(ERC721PresetMinterPauserAutoId) returns(string memory)
    {
        return baseTokenURI;
    }
    /* ====================================== View Functions END ================================ */
}

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

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

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

相关文章

  • 区块链 — 默克尔树

    默克尔树(又叫哈希树)是一种典型的二叉树结构,有一个根节点、一组中间节点和一组叶节点组成。默克尔树最早由 Merkle Ralf 在 1980 年提出,曾广泛用于文件系统和P2P系统中。比如 git、区块链、IPFS 等。 主要特点: 最下面的叶节点包含存储数据或其哈希值; 非叶子节点(

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

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

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

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

    2023年04月08日
    浏览(78)
  • 以太坊区块链ERC-721协议的实现(NFT代币标准)

    ERC-721是以太坊区块链上用于NFT(非同质化代币)的一个标准,是一种其他开发人员都遵守的模板或者格式,用于创建代表数字资产的独特代币,并且每个ERC-721代币都是独一无二的。使用统一的标准可以使合约代码变得更简单,复用性更强。ERC-721的出现促进了NFT的创建,并在

    2024年02月04日
    浏览(55)
  • ERC721和加密收藏品(ERC721 & Crypto-Collectibles)

    唷!这里的气氛开始升温了…… 在这节课中,我们将更深入一些。 我们将讨论代币、ERC721标准和加密可收集资产。 换句话说,我们要做的是让你可以和朋友交换僵尸。 我们来谈谈代币。 如果你在以太坊领域呆过一段时间,你可能听过人们谈论代币——特别是 ERC20代币 。 以

    2024年03月11日
    浏览(43)
  • ERC20 | ERC-20/ERC-721/ERC-1155/ERC-3525 区别

    EIP 20 的地址:https://eips.ethereum.org/EIPS/eip-20 ERC 是 Ethereum Request for Comment 的缩写,也就是以太坊改进建议。提交 ERC 后,以太坊社区会对这个草案进行评估,最终会接受或者拒绝该建议。如果接受的话,ERC 会被确认为 EIP。 EIP 是 Ethereum Improvement Proposals 的缩写,也就是被接纳的

    2024年02月05日
    浏览(41)
  • 【ERC-721与ERC-1155有什么区别?】

    无论您是加密爱好者还是第一次使用密码,您都可能听说过以太坊。它是最具影响力的开源区块链平台之一,用于创建智能合约、加密货币和去中心化应用程序。以太坊的智能合约改变了加密货币的世界,区块链技术现在渴望成为全球多个行业的标准。 大多数区块链发烧友已

    2024年02月01日
    浏览(48)
  • Eth Of Erc20 And Erc721

    public,可以修饰变量和函数, 被修饰的函数或变量可以被任何合约调用(或访问),默认的变量和函数使用该属性。 private,可以修饰变量和函数,被修饰者只能被当前合约内部的代码所调用(或访问),不能被外部合约调用或继承它的子合约调用(或访问)。 extermal,只能修饰函数

    2024年02月15日
    浏览(44)
  • OpenZeppelin——ERC721

    ERC721是一个代币标准,ERC721官方简要解释是Non-Fungible Tokens,简写为NFT,多翻译为非同质化代币。 那怎么理解 非同质化 代币呢? 非同质化代表独一无二,以卡牌游戏为例,盲盒开出的英雄属性是随机生成,尽管职业相同,但每个英雄不一样,一个英雄对应一个TokenId,就是一个

    2024年02月22日
    浏览(54)
  • 区块链 | ERC721 标准

    目录 正文 1  ERC721 接口 事件 方法 2  ERC165 接口 3  可选实现接口:ERC721Metadata 4  可选实现接口:ERC721Enumerable 补充说明 1  NTF IDs 2  与 ERC-20 的兼容性 3  交易、挖矿、销毁 🥕 原文: 剖析非同质化代币 ERC721 标准 🥕 写在前面: 本文属搬运博客,自己留存学习。 ERC721 作为

    2024年04月28日
    浏览(39)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包