全面掌握Solidity智能合约开发

2024年09月25日更新 796 人订阅
原价: ¥ 46 限时优惠
专栏简介 跟我学 Solidity :开发环境 跟我学 Solidity:关于变量 跟我学 Solidity : 变量的存储 跟我学 Solidity :引用变量 跟我学 Solidity :函数 跟我学 Solidity :合约的创建和继承 跟我学 Solidity :工厂模式 用Web3.js构建第一个Dapp 跟我学Solidity:事件 Solidity 中 immutable (不可变量)与constant(常量) [译] Solidity 0.6.x更新:继承 解析 Solidity 0.6 新引入的 try/catch 特性 探究新的 Solidity 0.8 版本 探索以太坊合约委托调用(DelegateCall) 停止使用Solidity的transfer() 使用工厂提高智能合约安全性 Solidity 怎样写出最节省Gas的智能合约[译] Solidity 优化 - 编写 O(1) 复杂度的可迭代映射 Solidity 优化 - 控制 gas 成本 Solidity 优化 - 减少智能合约的 gas 消耗的8种方法 Solidity 优化 - 如何维护排序列表 Solidity 优化:打包变量优化 gas 使用 Solidity 瞬态存储操作码 在 Solidity中使用值数组以降低 gas 消耗 Gas 优化:Solidity 中的使用动态值数组 计算Solidity 函数的Gas 消耗 Solidity 技巧:如何减少字节码大小及节省 gas 一些简单的 Gas 优化基础 "Stack Too Deep(堆栈太深)" 解决方案 智能合约Gas 优化的几个技术 合约实践:避免区块Gas限制导致问题 如何缩减合约以规避合约大小限制 Solidity 类特性 无需gas代币和ERC20-Permit还任重而道远 智能合约实现白名单的3个机制 Solidity智能合约安全:防止重入攻击的4种方法 Solidity 十大常见安全问题 [译]更好Solidity合约调试工具: console.log 智能合约开发的最佳实践 - 强烈推荐 全面理解智能合约升级 Solidity可升级代理模式: 透明代理与UUPS代理 使用OpenZeppelin编写可升级的智能合约 实战:调整NFT智能合约,减少70%的铸币Gas成本 Solidity 优化 - 隐藏的 Gas 成本 Gas 技巧:Solidity 中利用位图大幅节省Gas费 Solidity Gas 优化 - 理解不同变量 Gas 差异 关于Solidity 事件,我希望早一点了解到这些 Solidity 编码规范推荐标准 深入了解 Solidity bytes OpenZeppelin Contracts 5.0 版本发布 Solidity Gas优化:高效的智能合约策略 智能合约安全的新最低测试标准:Fuzz / Invariant Test 智能合约的白名单技术 模糊测试利器 - Echidna 简介 智能合约设计模式:代理 离线授权 NFT EIP-4494:ERC721 -Permit

智能合约实现白名单的3个机制

对比使用mapping、默克尔树、及离线签名 3 中方式处理白名单的优缺点。

简介

白名单是推广NFT项目和奖励早期进入及热情参与者的好方法。有很多方法可以实现白名单机制,每种方法都有自己的优势和劣势。现在主要有3种实现白名单机制的方法,本文介绍它们,并谈谈它们的优点和缺点。

最原始的方式--将白名单保存在存储中

对于熟悉其他语言和现代计算系统的开发者来说,将数据存储在堆或存储器中似乎是处理一系列数据的一个相当合理和简单的方法。

因此,要将白名单存储在存储器中,你可以简单地声明一个mapping映射,记录所有符合白名单条件的有效地址。然而,在 EVM 中,使用这种方式将消耗你大量的Gas,而且是一种非常低效的方法。不过,对于任何人来说,通过etherscan或几行代码来测试他或她是否在白名单上将会更容易。

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

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract PrimitiveWhiteList is ERC721Enumerable, Ownable {

  uint256 public constant MINT_PRICE = 0.1 ether;
  mapping(address => bool) public whitelist;

  constructor() ERC721("Primitive Whitelist", "PW") {}

  function whitelistMint(uint256 amount) external payable {
    require(msg.value == amount * MINT_PRICE, "Ether send below price");
    require(whitelist[msg.sender], "Not in whitelist");

    // start minting
    uint256 currentSupply = totalSupply();

    for (uint256 i = 1; i <= amount; i++) {
        _safeMint(msg.sender, currentSupply + i);
    }
  }

  function addWhitelist(address _newEntry) external onlyOwner {
    whitelist[_newEntry] = true;
  }

  function removeWhitelist(address _newEntry) external onlyOwner {
    require(whitelist[_newEntry], "Previous not in whitelist");
    whitelist[_newEntry] = false;
  }

}

优点:容易验证,编码简单,容易添加地址或删除地址

缺点:效率真的很低,对于发布者(项目方)来说真的很贵

聪明的方法一 - Merkle Tree Airdrop

执行白名单的另一种方式是利用默克尔(Merkle)树。Merkle树是区块链中的一个重要角色。Merkle树利用了hash的特性,即输入的轻微变化将导致完全不同的输出,以及两个输入导致相同输出的概率几乎是不可能的事实。

merkle树的结构如下所示。当前节点的哈希值等于其左侧子节点、右侧子节点及其数据的哈希值。因此,从散列的属性来看,任何变化都会导致完全不同的输出;我们可以利用这个属性来实现白名单。

Merkle

所以,让我们想象一下,你想知道L1是否等于一个地址,你可以提取Hash 0-1,Hash 1,以及被测试的地址。然后,你按照hash规则,将hash的输出与Top Hash进行比较。如果结果相同,你可以确保L1等于输入地址。

然而,要做到这一点,你需要生成一棵merkle树,并将一半的构建树过程从链上拿走,以节省Gas。我们将使用javascript来生成merkle树。如果你对ether.js比较熟悉,你也可以使用ethers.utils.solidityKeccak256来哈希配对。另外,记得要将JSON文件存储为以下内容:{&lt;address>: <amount>, &lt;address>: <amount>}。你也可以根据你的需要,把数据改成另一种类型来调整。


import fs from "fs";
import { MerkleTree } from "merkletreejs";
import Web3 from "web3";
import keccak256 from "keccak256";
import dotenv from "dotenv";

// Establish web3 provider
dotenv.config();
const web3 = new Web3(process.env.MAINNET_RPC_URL);

// hashing function for solidity keccak256
const hashNode = (account, amount) => {
    return Buffer.from(
        web3.utils
            .soliditySha3(
                { t: "address", v: account },
                { t: "uint256", v: amount }
            )
            .slice(2),
        "hex"
    );
};

// read list, Note: the root path is at cwd
// the json file structure: {"&lt;address>": &lt;amount>, "&lt;address>": &lt;amount>, ...}
const readRawList = (path) => {
    const rawdata = fs.readFileSync(path);
    const data = JSON.parse(rawdata);

    return data;
};

const generateMerkleTree = (data) => {
    const leaves = Object.entries(data).map((node) => hashNode(...node));

    const merkleTree = new MerkleTree(leaves, keccak256, { sortPairs: true });
    const merkleRoot = merkleTree.getHexRoot();

    return [merkleRoot, merkleTree];
};

const checkTree = (pairs, tree, root) => {
    for (const [key, value] of Object.entries(pairs)) {
        const leaf = hashNode(key, value);
        const proof = tree.getProof(leaf);

        // hex proof for solidity byte32[] input
        // const hexProof = tree.getHexProof(leaf);

        if (!tree.verify(proof, leaf, root)) {
            console.err("Verification failed");
            return false;
        }
    }

    return true;
};

function main(filepath, outputPath) {
    const rawData = readRawList(filepath);
    co...

剩余50%的内容订阅专栏后可查看

点赞 10
收藏 19
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论