如何空投NFTs - Quicknode

  • QuickNode
  • 发布于 2024-12-26 18:57
  • 阅读 49

本文详细介绍了如何使用OpenZeppelin和Foundry进行NFT的空投,包括设置项目、创建智能合约和进行测试与部署。文中也讨论了空投的概念、优点、挑战和相关的法律问题,为Web3项目提供了实用指导。

概述

空投代币和NFT已成为web3项目吸引流动性和增加社区参与的首选方法。本指南将向你展示如何向大量用户空投NFT。我们将使用OpenZeppelin的代币实现来构建智能合约,并使用Foundry作为我们的智能合约开发工具包。

让我们开始吧!

你将需要什么

  • 对以太坊和智能合约(尤其是NFTsTokens)的中级知识
  • 一个可以访问私钥的EVM钱包(例如,MetaMask,Coinbase Wallet)
  • 已安装的Node.js(v20+)
  • 已安装的Foundry

你将要做什么

  • 学习向用户空投代币的基本知识
  • 创建一个QuickNode节点端点(在这里创建账户)
  • 使用OpenZeppelin和Foundry创建和部署NFT/空投智能合约
  • 在Sepolia测试网上测试你部署的NFT/空投智能合约
依赖项 版本
node.js 20.14.0
foundryup 0.2.0
forge-std 1.8.2
openzeppelin-solidity 5.0.2

什么是空投?

空投涉及将数字资产(如代币或NFT)分发到钱包地址,通常是那些参与特定活动的人,例如与DeFi平台互动或参与活动。空投在web3项目中变得越来越流行,作为提高可见性和吸引流动性的一种方式。空投不仅作为社区成员的奖励,也作为吸引新用户和保持参与的战略工具。

例如,2020年9月1日进行的Uniswap空投奖励了在设定日期之前与Uniswap协议互动的用户,赠送UNI代币。接收者必须主动领取他们的代币,这是一种在web3项目中常见的方法,将Gas费用的负担从项目转移到用户。这种“领取”方法有助于管理项目的财务影响,同时大规模分发奖励。

空投的好处

  • 社区参与: 空投可以通过奖励活跃参与者或早期采用者来提高社区参与度。这还可以吸引更多的交易量和流动性进入你的web3项目
  • 促销与意识: 分发免费NFT可以吸引关注并产生关于你项目的热议
  • 去中心化: 通过向广泛受众空投NFT,你可以帮助去中心化所有权,避免集中于少数地址

空投的挑战

  • 可扩展性: 处理大量地址的空投在技术上可能具有挑战性,因为涉及的地址数量庞大。在使用像以太坊主网的L1时,用户可能会花费数百美元(截至2024年);而其他L2链,如Optimism和Base则提供实惠得多的交易成本,web3项目仍在寻找节省Gas费用的替代方案,例如要求用户领取他们的空投(而不是直接发送,背负Gas费用)或通过像账户抽象这样的工具实现无Gas的铸造。在进行更大规模的空投时,区块链基础设施也是一个重要因素。你必须确保你的UI以及它所通信的RPC节点能够处理用户的流量。这也是QuickNode可以提供帮助的地方,它可以自动扩展以满足你的使用需求,并在关键时刻提供性能和可靠性。
  • 合规问题: 根据不同的地点,空投可能面临法律审查,主要是如果它们被视为一种金融促销。我们已经看到许多web3项目限制用户领取他们的NFT(尽管他们在技术上可能符合空投条件)

项目设置前提条件:创建一个QuickNode端点

要与区块链进行通信,你需要访问一个节点。你可以使用公共节点或部署和管理自己的基础设施;但是,如果你希望获得8倍更快的响应时间,可以将繁重的工作交给我们。请在这里注册一个免费账户。

登录QuickNode后,点击创建端点按钮,然后选择Ethereum链和Sepolia网络。

创建端点后,复制HTTP Provider URL链接并将其保存,因为在开发空投代码时你将需要它。

Sepolia QuickNode Endpoint

项目设置前提条件:通过QuickNode水龙头为你的钱包提供资金

为了将NFT合约部署到Sepolia测试网,你需要Sepolia测试网的ETH来支付Gas费用。Multi-Chain QuickNode Faucet使你轻松获得测试ETH!

导航到多链QuickNode水龙头,连接你的钱包(例如,MetaMask,Coinbase Wallet)或粘贴你的钱包地址以检索测试ETH。请注意,在以太坊主网的EVM水龙头上,要使用其必须满足0.001 ETH的主网余额要求。你还可以通过推特或使用QuickNode账户登录来获得额外奖励!

Multi-Chain QuickNode Faucet

项目设置前提条件:初始化并安装依赖项

在我们开始编码之前,请确保你已安装Node.js(v20+)和Foundry。接下来,让我们运行以下命令设置项目目录:

forge init airdrop

接下来,进入项目目录,创建所需文件并安装依赖项:

cd airdrop
touch src/NFT.sol
touch test/NFT.t.sol
touch script/Claim.s.sol
touch remappings.txt
forge install OpenZeppelin/openzeppelin-contracts --no-commit

我们需要设置重映射,以便我们的智能合约(.sol 文件)能够正确访问OpenZeppelin库。将以下配置添加到remappings.txt文件中。

@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/

在该项目中配置RPC端点的一种方法是将其包含在foundry.toml文件中,但如果你计划将此代码开源,我们建议将其排除,并仅将其包含在命令行指令中。让我们创建环境变量以包括我们的RPC端点和私钥(我们将使用来部署智能合约):

export RPC_URL=<你的RPC端点>
export PRIVATE_KEY=<你的钱包私钥>

设置完项目目录并安装依赖项后,让我们继续创建智能合约。

创建空投智能合约

NFT合约将包含用户可以与之互动以领取其NFT的空投(例如,领取逻辑)。NFT智能合约具有以下功能:

  • 部署NFT合约时,部署者需要设置owner地址、地址数组initialWhitelist和用于元数据的baseTokenURI
  • 白名单上的用户可以调用claimNFT函数来铸造他们的NFT。不在白名单上的用户尝试调用此函数将收到错误。
  • 合约管理员(例如,拥有者)可以通过调用addToWhitelistremoveFromWhitelist函数添加/移除更多地址到白名单。
  • 合约管理员可以在任何时候调用pause函数来暂停领取。一种unpause函数也包含在内,以恢复领取。
  • 映射whitelisthasClaimed存储已列入白名单和已领取地址的列表。
  • 可选地,NFT合约继承ERC721URIStorage用于元数据使用和ERC721Burnable用于可销毁性。

NFT合约

打开src/NFT.sol文件并添加以下代码:

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract NFT is ERC721, ERC721URIStorage, ERC721Burnable, Pausable, Ownable {
    uint256 private _nextTokenId;
    mapping(address => bool) public whitelist;
    mapping(address => bool) public hasClaimed;
    string private _baseTokenURI;

    constructor(address initialOwner, address[] memory initialWhitelist, string memory baseTokenURI)
        ERC721("NFT", "NFT")
        Ownable(initialOwner)
 {
        transferOwnership(initialOwner);
        addToWhitelist(initialWhitelist);
        _baseTokenURI = baseTokenURI;
 }

    function claimNFT() public whenNotPaused {
        address to = msg.sender;
        require(whitelist[to], "Caller is not whitelisted");
        require(!hasClaimed[to], "Caller has already claimed an NFT");

        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        hasClaimed[to] = true;
 }

    function addToWhitelist(address[] memory addresses) public onlyOwner {
        for (uint i = 0; i < addresses.length; i++) {
            if (!whitelist[addresses[i]]) {
                whitelist[addresses[i]] = true;
 }
 }
 }

    function removeFromWhitelist(address[] memory addresses) public onlyOwner {
        for (uint i = 0; i < addresses.length; i++) {
            if (whitelist[addresses[i]]) {
                whitelist[addresses[i]] = false;
 }
 }
 }

    function pause() public onlyOwner {
        _pause();
 }

    function unpause() public onlyOwner {
        _unpause();
 }

    // 以下函数是Solidity的重写要求。
    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        whenNotPaused
        returns (string memory)
 {
        return super.tokenURI(tokenId);
 }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (bool)
 {
        return super.supportsInterface(interfaceId);
 }
}

测试

在将合约部署到测试网之前,让我们进行测试,以确保智能合约按预期工作。

将以下测试代码添加到test/NFT.t.sol文件中:

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

import "forge-std/Test.sol";
import "../src/NFT.sol";

contract NFTTest is Test {
    NFT nft;
    address owner;
    address[] initialWhitelist;

    function setUp() public {
        owner = address(this);
        initialWhitelist = [vm.addr(1), vm.addr(2), vm.addr(3), vm.addr(4)];
        nft = new NFT(owner, initialWhitelist, "https://api.example.com/");
        nft.transferOwnership(owner);  // 确保拥有者设置正确
 }

    function testClaimNFT() public {
        vm.startPrank(vm.addr(1));  // 模拟白名单用户
        nft.claimNFT();
        assertTrue(nft.balanceOf(vm.addr(1)) == 1, "应铸造1个NFT");
        vm.stopPrank();
 }

    function testFailClaimNFTNotWhitelisted() public {
        vm.startPrank(vm.addr(5));  // 非白名单用户
        nft.claimNFT();  // 这应该失败
        vm.stopPrank();
 }

    function testFailClaimNFTTwice() public {
        vm.startPrank(vm.addr(1));
        nft.claimNFT();
        nft.claimNFT();  // 尝试第二次领取应该失败
        vm.stopPrank();
 }

    function testPauseUnpause() public {
        nft.pause();
        assertTrue(nft.paused(), "合约应已暂停");

        nft.unpause();
        assertFalse(nft.paused(), "合约应已恢复");
 }
}

然后使用forge test命令运行测试:

forge test

输出将显示如下内容:

Ran 4 tests for test/NFT.t.sol:NFTTest
[PASS] testClaimNFT() (gas: 107481)
[PASS] testFailClaimNFTNotWhitelisted() (gas: 13262)
[PASS] testFailClaimNFTTwice() (gas: 106417)
[PASS] testPauseUnpause() (gas: 17536)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.96ms (1.02ms CPU time)

Ran 1 test suite in 176.35ms (1.96ms CPU time): 4 tests passed, 0 failed, 0 skipped (4 total tests)

部署

现在是将智能合约部署到测试网的时候了。运行下面的forge create命令来部署智能合约。

环境变量RPC_URLPRIVATE_KEY在项目设置部分之前已经设置。然而,在执行命令之前,你必须包含下面的构造函数参数。

  • intialOwner: 可执行添加/移除白名单地址、暂停/恢复合约等管理功能的NFT合约拥有者
  • array of whitelist addresses: 一个EVM兼容地址的数组。你在合约中使用的数组长度没有特定限制。然而,根据Gas成本和你所部署区块链的块Gas限制,实际上是存在限制的。
  • metadataURI: 指向你的NFT项目元数据的URL。你可以使用像IPFS这样的提供商在QuickNode上以去中心化的方式托管元数据。

重要:确保你在白名单数组中包含你自己的地址,以便在下一部分测试领取功能。

forge create --rpc-url=$RPC_URL --private-key=$PRIVATE_KEY src/NFT.sol:NFT --constructor-args "intialOwnerAddress" "[0xAddr1, 0xAddr2, 0xAddr3]" "http://examplemetadatauri.com"

你应该看到以下输出:

No files changed, compilation skipped
Deployer: 0xe0f2b924422909626D539b0FBd96239B31767400
Deployed to: 0xf4Bc97338Ddc886c5DA20f0fF91cF1c7Ea4e2760
Transaction hash: 0x664741640fb13b3ffdc659251fc8ba43a5d141d7b5c4d789820bf41485218c8c

现在,你的NFT合约已部署在测试网上,我们可以与之交互。

提示

如果你想在像Etherscan这样的区块浏览器上验证(开源)你的智能合约,请查看本指南:验证你的智能合约代码的不同方法

空投领取示例

现在,为了测试在Sepolia测试网上领取空投,请在script/Claim.s.sol文件中输入以下代码:

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

import "forge-std/Script.sol";
import "forge-std/console.sol";
import "../src/NFT.sol";

contract ClaimScript is Script {
    NFT public nftContract;

    function setUp() public {
        // 使用已部署合约地址初始化NFT合约接口
        nftContract = NFT("INSERT_DEPLOYED_CONTRACT_ADDRESS");  // 用实际合约地址替换
 }

    function run() public {
        vm.startBroadcast();  // 开始事务广播会话
        nftContract.claimNFT();
        vm.stopBroadcast();
 }
}

然后要执行脚本,运行以下命令:

forge script script/Claim.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast

重要:确保你在上面包含的私钥对应于你在部署时包含的白名单地址。

你将看到如下输出:

## Setting up 1 EVM.
==========================
Chain 11155111
Estimated gas price: 14.618536578 gwei
Estimated total gas used for script: 162145
Estimated amount required: 0.00237032261343981 ETH
==========================

Sending transactions [0 - 0].
⠁ [00:00:00] [#####################################################################################################################################################################] 1/1 txes (0.0s)##
Waiting for receipts.
⠉ [00:00:12] [#################################################################################################################################################################] 1/1 receipts (0.0s)
##### sepolia
✅  [Success]Hash: 0x7832ddd563e757e57aadb2fb55d16f282b9f4419a38feace946cbd3c56d4777b
Block: 5997706
Paid: 0.001167971638330575 ETH (117391 gas * 9.949413825 gwei)
==========================
ONCHAIN EXECUTION COMPLETE & SUCCESSFUL.
Total Paid: 0.001167971638330575 ETH (117391 gas * avg 9.949413825 gwei)

最终思考

就这样!你现在知道如何部署自己的NFT合约,使其兼容空投。请记住,区块链开发是一个不断发展的领域,保持更新是🔑。订阅我们的时事通讯,获取更多关于Web3和区块链的文章和指南。如果你有任何问题或需要进一步的帮助,请随时加入我们的Discord服务器或使用下面的表单提供反馈。通过关注我们的Twitter (@QuickNode)和我们的Telegram公告频道保持消息灵通并建立联系。

我们❤️反馈!

告诉我们你是否有任何反馈或新主题请求。我们期待你的来信。

  • 原文链接: quicknode.com/guides/eth...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。