跳转至主要内容

如何撰写和部署非同质化代币(非同质化代币教程系列 1/3)

ERC-721AlchemySolidity智能合约
初学者
Sumi Mudgil
2021年4月22日
21 分钟阅读 minute read

随着非同质化代币将区块链带入公众视野,现在正是通过在以太坊区链上发布自己的非同质化代币合约(ERC-721 代币)来了解这一宣传的绝佳机会!

Alchemy 非常自豪能够推动非同质化代币领域的一些巨头,包括 Makersplace(最近在克利斯蒂拍卖行创造了 6900 万美元的数字艺术品销售记录)、Dapper Labs(NBA Top Shot & Crypto Kitties 的创作者)、OpenSea(世界上最大的非同质化代币市场)、Zora、Super Rare、NFTfi、Foundation、Enjin、Origin Protocol、Immutable 等。

在本教程中,我们将使用 MetaMask(opens in a new tab)Solidity(opens in a new tab)安全帽(opens in a new tab)Pinata(opens in a new tab)Alchemy(opens in a new tab) 演示如何在 Sepolia 测试网络上创建和部署 ERC-721 智能合约(如果还不明白其中的含义,请不要着急,我们会为你解释!)。

在本教程的第二部分,我们将了解如何使用我们的智能合约来铸造非同质化代币;在第三部分,我们将说明如何在 MetaMask 上查看你的非同质化代币。

当然,如果你有任何问题,请随时通过 Alchemy Discord(opens in a new tab) 联系我们或阅读 Alchemy 的非同质化代币应用程序接口相关文档(opens in a new tab)

步骤 1:连接到以太坊网络

有很多方法可以向以太坊区块链发出请求,但为了方便起见,我们将使用 Alchemy(opens in a new tab) 上的免费帐户。Alchemy 是一个区块链开发平台,能够提供应用程序接口,让我们无需运行自己的节点,即可与以太坊区块链进行通信。

在本教程中,我们将利用 Alchemy 平台的开发者工具进行监测和分析,以便了解智能合约部署的底层逻辑。 如果你还没有 Alchemy 帐户,你可以在此处(opens in a new tab)免费注册。

步骤 2:创建应用程序(和应用程序接口密钥)

创建了 Alchemy 帐户后,你可以通过创建应用程序来生成应用程序接口密钥。 这将使我们能够向 Sepolia 测试网络发出请求。 如果你想了解更多关于测试网络的信息,请查看本指南(opens in a new tab)

  1. 将鼠标悬停在Alchemy网页导航栏中的「App」,单击「Create App」并前往此页面。

创建你的应用程序

  1. 命名你的应用程序(我们选择使用“My First NFT!”),提供简短描述,选择“以太坊”作为区块链,并选择“Sepolia”作为你的网络。 合并后,其他测试网已被弃用。

配置并发布你的应用程序

  1. 点击"创建应用程序",完成! 你的应用程序应该会出现在下面的表格中。

步骤 3:创建一个以太坊帐户(地址)

我们需要一个以太坊帐户来发送和接收交易。 在本教程中,我们将使用 MetaMask——浏览器中的虚拟钱包,用来管理你的以太坊帐户地址。 如果你想了解更多关于以太坊交易的运作方式,请查看以太坊基金会的这个页面

你可以在这里(opens in a new tab)免费下载并创建一个 MetaMask 帐户。 在你创建帐户时,或者如果你已有一个帐户,请确保切换到右上角的“Sepolia Test Network”(这样我们就不会使用实际货币进行交易)。

将 Sepolia 设置为你的网络

步骤 4:从水龙头添加以太币

为了将我们的智能合约部署到测试网络,我们需要一些虚拟以太币。 想要获取以太币,可以访问由 Alchemy 托管的 Sepolia Faucet(opens in a new tab),登录并输入你的帐户地址,点击“Send Me ETH”。 你应该会很快在你的 MetaMask 帐户中看到以太币!

步骤 5:查看帐户余额

为了核实我们的余额,可以使用 Alchemy 的创作者工具(opens in a new tab)发出 eth_getBalance(opens in a new tab) 请求。 这将返回我们钱包中的以太币金额。 输入你的 Metamask 帐户地址并单击“发送请求”后,你应该会看到这样的响应:

1`{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}`

注:此结果以 wei 为单位,而非 ETH。 Wei 是以太币的最小计量单位。 wei 与 ETH 之间的转换为 1 eth = 1018 wei。 因此,如果我们将 0xde0b6b3a7640000 转换为十进制,我们得到 1*1018 wei,即 1 ETH。

哦! 这里显示了我们所有的虚拟货币。

步骤 6:初始化我们的项目

首先,需要为我们的项目创建一个文件夹。 导航到你的命令行,然后输入:

1mkdir my-nft
2cd my-nft

现在我们进入了项目文件夹,我们将使用 npm init 来初始化项目。 如果还没有安装 npm,请遵循此处的说明(opens in a new tab)(我们还需要 Node.js(opens in a new tab),所以请下载此工具!)。

1npm init

其实如何回答安装问题并不重要,以下提供一个回答的样例供参考:

1package name: (my-nft)
2version: (1.0.0)
3description: My first NFT!
4entry point: (index.js)
5test command:
6git repository:
7keywords:
8author:
9license: (ISC)
10About to write to /Users/thesuperb1/Desktop/my-nft/package.json:
11
12{
13 "name": "my-nft",
14 "version": "1.0.0",
15 "description": "My first NFT!",
16 "main": "index.js",
17 "scripts": {
18 "test": "echo \"Error: no test specified\" && exit 1"
19 },
20 "author": "",
21 "license": "ISC"
22}
显示全部

批准 package.json,我们就可以开始了!

步骤 7:安装安全帽 (Hardhat)(opens in a new tab)

安全帽是一个用于编译、部署、测试和调试以太坊软件的开发环境。 它帮助开发者在本地构建智能合约和去中心化应用程序并部署到实时链上。

在我们的 my-nft 项目内运行:

1npm install --save-dev hardhat

查看此页面,了解更多有关安装说明(opens in a new tab)的详细信息。

步骤 8:创建安全帽项目

在我们的项目文件夹中运行:

1npx hardhat

然后应该能看到一条欢迎消息和选项,用于选择你想要做的事情。 选择“创建一个空的 hardhat.config.js”:

1888 888 888 888 888
2888 888 888 888 888
3888 888 888 888 888
48888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
5888 888 "88b 888P" d88" 888 888 "88b "88b 888
6888 888 .d888888 888 888 888 888 888 .d888888 888
7888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
9👷 Welcome to Hardhat v2.0.11 👷‍
10? 你想做什么? …
11Create a sample project
12❯ Create an empty hardhat.config.js
13Quit
显示全部

这将生成一个 hardhat.config.js 文件,我们将在其中指定项目的所有设置(步骤 13)。

步骤 9:添加项目文件夹

为了使我们的项目有条理,我们将创建两个新的文件夹。 在你的命令行中导航到项目的根目录,然后输入:

1mkdir contracts
2mkdir scripts
  • contracts/ 是我们保存非同质化代币智能合约代码的位置。

  • scripts/ 是我们存放脚本的位置,用于部署我们的智能合约和与之交互。

步骤 10:编写合约

现在我们的环境已经配置完成,接下来将是更令人兴奋的内容:编写我们的智能合约代码!

在你喜欢的编辑器中打开 my-nft 项目(我们喜欢用 VSCode(opens in a new tab))。 智能合约用一种名为 Solidity 的语言编写,我们将用它来编写我们的 MyNFT.sol 智能合约。

  1. 导航到 contracts 文件夹并创建一个名为 MyNFT.sol 的新文件

  2. 下面是我们的非同质化代币智能合约代码,基于 OpenZeppelin(opens in a new tab) 库的 ERC-721 实现。 复制并粘贴以下内容到你的 MyNFT.sol 文件。

    1//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
    2// SPDX-License-Identifier: MIT
    3pragma solidity ^0.8.0;
    4
    5import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
    6import "@openzeppelin/contracts/utils/Counters.sol";
    7import "@openzeppelin/contracts/access/Ownable.sol";
    8import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
    9
    10contract MyNFT is ERC721URIStorage, Ownable {
    11 using Counters for Counters.Counter;
    12 Counters.Counter private _tokenIds;
    13
    14 constructor() ERC721("MyNFT", "NFT") {}
    15
    16 function mintNFT(address recipient, string memory tokenURI)
    17 public onlyOwner
    18 returns (uint256)
    19 {
    20 _tokenIds.increment();
    21
    22 uint256 newItemId = _tokenIds.current();
    23 _mint(recipient, newItemId);
    24 _setTokenURI(newItemId, tokenURI);
    25
    26 return newItemId;
    27 }
    28}
    显示全部
    复制
  3. 因为我们要从 OpenZeppelin 合约库继承类,请在你的命令行中运行 npm install @openzeppelin/contracts,将该库安装到我们的文件夹中。

那么,这段代码究竟了什么? 让我们把它逐行分解。

在我们的智能合约的顶部,我们导入了三个 OpenZeppelin(opens in a new tab) 智能合约类:

  • @openzeppelin/contracts/token/ERC721/ERC721.sol 包含 ERC-721 标准的实现,我们的非同质化代币智能合约将继承此标准。 (要成为有效的非同质化代币,你的智能合约必须实现 ERC-721 标准的所有方法。) 要更加详细地了解继承的 ERC-721 功能,请在这里(opens in a new tab)查看接口定义。

  • @openzeppelin/contracts/utils/Counters.sol 提供了只能以 1 为增量或减量的计数器。 我们的智能合约使用一个计数器来跟踪非同质化代币总铸币量,并在我们的新非同质化代币上设置唯一 ID。 (每个使用智能合约铸造的非同质化代币必须分配一个唯一 ID——在这里,我们的唯一 ID 仅由存在的非同质化代币总量决定。 例如,我们用智能合约铸造的第一个非同质化代币的 ID 是“1”,第二个非同质化代币的 ID 是“2”,以此类推)

  • @openzeppelin/contracts/access/Ownable.sol在我们的智能合约上设置了访问控制(opens in a new tab),因此只有智能合约的所有者(你)可以铸造非同质化代币。 (注意:包含访问控制完全是一种偏好。 如果你希望任何人都能使用你的智能合约铸造非同质化代币,请删除第 10 行的 Ownable 和第 17 行的 onlyOwner)。

导入语句后面,是我们自定义的非同质化代币智能合约。合约非常短,只包含一个计数器、一个构造函数和独立函数! 这要归功于我们继承的 OpenZepelin 合约,它们实现了我们创建非同质化代币所需的大多数方法。例如 owerOf 函数,其作用是返回非同质化代币所有者,以及 transferFrom 函数,其作用是将非同质化代币的所有权从一个帐户转移到另一个帐户。

在我们的 ERC-721 构造函数中,你会注意到我们传递 2 个字符串,“MyNFT”和“NFT”。 第一个变量是智能合约的名称,第二个变量是其符号。 你可以随意命名这些变量!

最后,我们得到了函数 mintNFT(address recipient, string memory tokenURI),它让我们能够铸造非同质化代币! 你将注意到,这个函数包含两个变量:

  • address recipient 指定了接收新铸造非同质化代币的地址

  • string memory tokenURI 是一个可以解析为 JSON 文档的字符串,该文档用于描述非同质化代币的元数据。 非同质化代币的元数据是它的核心所在,使它拥有可配置的属性,例如名称、描述、图像和其他属性。 在本教程第二部分,我们将描述如何配置元数据。

mintNFT 调用了继承的 ERC-721 库中的一些方法,最终返回一个数字,代表新铸造非同质化代币的 ID。

步骤 11:将 Metamask 和 Alchemy 连接至你的项目

我们已经创建了 Metamask 钱包、Alchemy 帐户,并且编写了一个智能合约,现在是将这三者连接起来的时候了。

从虚拟钱包发送的每笔交易都需要使用你独有的私钥签名。 为了给程序提供此项许可,我们可以安全地将私钥(和 Alchemy 应用程序接口密钥)存储在一个环境文件中。

如需了解更多关于发送交易的信息,请查看关于使用 web3 发送交易的 本教程

首先,在项目目录中安装 dotenv 软件包:

1npm install dotenv --save

然后在我们的项目根目录中创建 .env 文件,并将你的 MetaMask 私钥和超文本传输协议 Alchemy 应用程序接口网址加入其中。

  • 遵循这些说明(opens in a new tab),从 MetaMask 导出你的私钥

  • 请从下方获取超文本传输协议 Alchemy 应用程序接口网址并将其复制到剪贴板

复制你的 Alchemy 应用程序接口网址

你的 .env 现在应该如下所示:

1API_URL="https://eth-sepolia.g.alchemy.com/v2/your-api-key"
2PRIVATE_KEY="your-metamask-private-key"

为了将它们实际连接到我们的代码,我们将在步骤 13 中在 hardhat.config.js 文件中引用这些变量。

page-tutorials-env-banner

步骤 12:安装 Ethers.js

Ethers.js 是一个软件库,通过以更加方便用户的方法打包标准 JSON RPC 方法,从而更容易与以太坊互动,并向以太坊提出请求。

安全帽使我们更容易将插件(opens in a new tab)集成到工具和扩展功能中。 我们将利用 Ethers 插件(opens in a new tab)完成合约部署(Ethers.js(opens in a new tab) 有非常简洁的部署方法)。

在你的项目目录中输入:

1npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.0.0

下一步中,我们还将在 hardhat.config.js 中使用以太币。

步骤 13:更新 hardhat.config.js

到目前为止,我们已经添加了几个依赖项和插件,现在我们需要更新 hardhat.config.js,以使项目了解所有这些新的组件。

按如下所示更新你的 hardhat.config.js 代码:

1/**
2* @type import('hardhat/config').HardhatUserConfig
3*/
4require('dotenv').config();
5require("@nomiclabs/hardhat-ethers");
6const { API_URL, PRIVATE_KEY } = process.env;
7module.exports = {
8 solidity: "0.8.1",
9 defaultNetwork: "sepolia",
10 networks: {
11 hardhat: {},
12 sepolia: {
13 url: API_URL,
14 accounts: [`0x${PRIVATE_KEY}`]
15 }
16 },
17}
显示全部

步骤 14:编写合约

为了确保一切正常,我们来编译一下合约。 编译任务是安全帽的内部任务之一。

在命令行中运行:

1npx hardhat compile

你可能会看到关于源文件中未提供 SPDX 许可证识别码的警告,但无需担心,但愿其他的一切正常! 如果遇到问题,你可以随时在 Alchemy cord(opens in a new tab) 社区中发消息询问。

步骤 15:编写部署脚本

合约已经写完,配置文件也准备妥当,现在是写合约部署脚本的时候了。

转到 scripts/ 文件夹,创建一个名为 deploy.js 的新文件,在其中添加以下内容:

1async function main() {
2 const MyNFT = await ethers.getContractFactory("MyNFT")
3
4 // Start deployment, returning a promise that resolves to a contract object
5 const myNFT = await MyNFT.deploy()
6 await myNFT.deployed()
7 console.log("Contract deployed to address:", myNFT.address)
8}
9
10main()
11 .then(() => process.exit(0))
12 .catch((error) => {
13 console.error(error)
14 process.exit(1)
15 })
显示全部
复制

安全帽在合约教程(opens in a new tab)中对这些代码的每一行均提供了很好的解释,我们在这里直接引用他们的解释。

1const MyNFT = await ethers.getContractFactory("MyNFT");

ethers.js 中的 ContractFactory 是用于部署新智能合约的抽象对象,因此这里的 MyNFT 是我们非同质化代币合约实例的工厂。 使用 hardhat-ethers 插件时,ContractFactory 和合约实例默认与第一个签名者相连。

1const myNFT = await MyNFT.deploy();

调用 ContractFactory 代码中的 deploy() 函数会启动合约部署,然后返回解析为合约的 Promise。 这个对象包括我们智能合约中每个函数的对应调用方法。

步骤 16:部署合约

我们终于准备好部署我们的智能合约啦! 返回项目目录的根目录,在命令行中运行:

1npx hardhat --network sepolia run scripts/deploy.js

你会看到类似以下所示的信息:

1在地址 0x4C5266cCc4b3F426965d2f51b6D910325a0E7650 部署的合约

如果我们访问 Sepolia etherscan(opens in a new tab) 并搜索我们的合约地址,应该能够看到它已成功部署。 如果未能立即看到它,请稍等片刻,因为部署可能需要一些时间。 交易将类似以下:

在 Etherscan 上查看你的交易地址

From 地址应匹配你的 MetaMask 帐户地址,To 地址将显示“合约创建”。 如果我们点击进入交易,将会在“To”字段中看到我们的合约地址:

在 Etherscan 区块浏览器 上查看你的合约地址

太棒了! 你刚刚在以太坊(测试网)区块链上部署了你的非同质化代币智能合约!

为了更深入了解到底发生了什么,我们转到 Alchemy 仪表板(opens in a new tab)中的 Explorer 选项卡。 如果你有多个 Alchemy 应用程序,请确保按应用程序筛选,然后选择“MyNFT”。

使用 Alchemy 的浏览器仪表板查看“后端”调用

在这里,你会看到一系列 JSON-RPC 调用,都是在我们调用 .deploy() 函数时 Hardhat/Ethers 替我们在后端完成的。 这里有两项重要调用,一个是 eth_sendRawTransaction,这是实际将我们的智能合约写入 Sepolia 链的请求,另一个是 eth_getTransactionByHash,这是在提供哈希值时读取有关我们交易信息的请求(发送交易时的典型模式)。 如需了解更多关于发送交易的信息,请查看关于使用 Web3 发送交易的教程。

以上即为本教程第 1 部分的全部内容。 在第 2 部分,我们将通过铸造非同质化代币与我们的智能合约进行交互,在第 3 部分,我们将向你演示如何在以太坊钱包中查看你的非同质化代币

上次修改时间: @nhsz(opens in a new tab), 2024年5月1日

本教程对你有帮助吗?