Ethereum非同质化通证(NFT)的铸造与展示
前言
随着非同质化通证(NFT)日渐活跃,让更多的人了解到NFT,也让更多的项目甚至个人可以通过在Ethereum区块链上发布自己的非同质化通证(基于ERC-721协议)从而走进Web3.0的世界。
在本篇文章中,将着重介绍如何在Ethereum区块链上发布自己的基于ERC-721协议的非同质化通证。其中我们将涉及到 MetaMask(区块链钱包)、Solidity(智能合约语言)、Hardhat(以太坊软件的开发环境)、Alchemy(区块链开发平台)和Pinata(星际文件系统平台)的学习和使用。并在 Goerli 测试网络上创建和部署 ERC-721 智能合约的完整过程。
上一篇文章Ethereum非同质化通证(NFT)的编写与部署中通过一个具体的例子操作了Ethereum非同质化通证(NFT)的编写与部署。本篇文章将讲述如何通过这个智能合约铸造一个非同质化通证以及如何在以太坊钱包中显示出这个通证。
一、安装 Web3.js
在上一篇文章中,已经使用到了 Ethers.js 这个库,这里的 Web3.js 与之类似,也是用于轻松创建对以太坊区块链请求的库。这里将使用到 Alchemy Web3.js,一个增强版 web3 库,提供自动重试和强大的 WebSocket 支持。
在项目根目录中运行
npm install @alch/alchemy-web3
二、创建 mint-nft.js 文件
在 scripts 目录中,创建一个 mint-nft.js 文件并添加以下代码:
require("dotenv").config()
const API_URL = process.env.API_URL
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(API_URL)
三、获取合约应用程序二进制接口
合约的 ABI(应用程序二进制接口)是与智能合约交互的接口。项目中使用的HARDHAT能自动生成应用程序的二进制接口,并将其保存在 NewNFT.json 文件中。为了使用该接口,需要通过在 mint-nft.js 文件中添加以下代码行来解析内容:
const contract = require("../artifacts/contracts/NewNFT.sol/NewNFT.json")
console.log(JSON.stringify(contract.abi)) //将应用程序二进制接口发送到控制台以便查看
在项目终端中运行,即可查看发送到控制台的应用程序二进制接口:
node scripts/mint-nft.js
四、使用IPFS为非同质化通证配置元数据
在上一篇文章中,提到了 mintNFT 智能合约函数包含有一个 tokenURI 参数,该参数应解析为描述非同质化通证元数据的 JSON 文档,它是非同质化通证的核心,赋予非同质化通证可配置的属性,如名称、描述、图像和其他属性。这里将要使用到IPFS来存储配置这些元数据。IPFS是一个去中心化的存储网络,用于在分布式文件系统中存储和共享数据。
前言中提到的 Pinata 将来完成这部分工作,这是一个非常方便的 IPFS 应用程序接口和工具包,用于存储非同质化通证资产和元数据,以确保非同质化通证真正的去中心化。
登录 Pinata 后,点击 Upload 选择 File ,在弹出层中点击 Select a file ,然后在 Name 中输入文件的名字,再点击 Upload 完成上传。
上传完成后,将在 My Files 列表中看到文件信息,其中有文件的 CID 列,可以通过单击旁边的复制按钮来复制CID。 通过 https://gateway.pinata.cloud/ipfs/[CID] 这个URL即可访问到上传的文件内容。比如我上传的这个CSDN程序猿。
在项目根目录中,创建一个名为 nft-metadata.json 的文件,并添加以下 json 代码:
{
"attributes": [
{
"trait_type": "Breed",
"value": "Ape"
},
{
"trait_type": "Eye",
"value": "Glasses"
},
{
"trait_type": "Face",
"value": "Smiled"
}
],
"description": "Very powerful programmer ape.",
"image": "ipfs://QmXectapuW3yBCWwgjf78h3Dqdo2zpD5qaq4zXb63qXbp9",
"name": "Caesar"
}
这个文件是非同质化通证的灵魂所在,其中的 JSON 数据可随意修改。这个例子中定义了非同质化通证中Breed、Eye、Face这几个属性,可以删除或添加相应的属性内容。最重要的是,确保 image 字段指向 IPFS 中的 CID。编辑完成之后以同样的方式上传至 Pinata 中。
五、创建合约实例
为了与合约进行交互,需要在代码中创建一个实例。这里需要用到合约地址,在上一篇文章中描述了如何获取到合约的地址。
这里将直接使用之前创建的智能合约地址:0x094180BBc8f5e8e9697C0F633D4004e3ecea6510
接下来将使用 web3 的合约方法,用ABI和地址构建合约,在 mint-nft.js 文件中添加以下内容:
const contractAddress = "0x094180BBc8f5e8e9697C0F633D4004e3ecea6510"
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)
六、更新 .env 文件
为了创建并发送交互到以太坊链,将使用以太坊公共账户地址来获得账户随机数。将地址公钥添加到 .env 文件中,这时的 .env 文件应该长得像这样:
API_URL = "https://eth-goerli.alchemyapi.io/v2/api-key"
PRIVATE_KEY = "metamask-private-key"
PUBLIC_KEY = "metamask-account-address"
七、创建及签署Transaction
1.创建Transaction
定义一个名为 mintNFT(tokenData) 的函数并通过执行以下操作创建Transaction:
- 从 .env 文件中获取 PRIVATE_KEY 和 PUBLIC_KEY 等配置信息
- 将 PUBLIC_KEY 传递给 getTransactionCount 函数获取一个 nonce 值用来防止重放攻击
- 然后,用以下信息设置Transaction:
‘from’: PUBLIC_KEY ETH账户的地址(务必是0x开头的完整地址)
‘to’: contractAddress 希望与之交互并发送交易的合约地址
‘nonce’: nonce 账户随机数和从地址发送的交易数量
‘gas’: estimatedGas 预估完成交易所需的Gas
‘data’: 实际这笔交易中所进行的操作,这里是铸造一个非同质化通证。
现在 mint-nft.js 文件看起来应该长得这样:
require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/NewNFT.sol/NewNFT.json");
console.log(JSON.stringify(contract.abi)) //将应用程序二进制接口发送到控制台以便查看
const contractAddress = "0x094180BBc8f5e8e9697C0F633D4004e3ecea6510";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
}
2.签署Transaction
当前已经创建了Transaction,接下来需要签署它以便将其发送出去, 在这里需要用户账户的私钥。
require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/NewNFT.sol/NewNFT.json");
console.log(JSON.stringify(contract.abi)) //将应用程序二进制接口发送到控制台以便查看
const contractAddress = "0x094180BBc8f5e8e9697C0F633D4004e3ecea6510";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce
//the transaction
const tx = {
from: PUBLIC_KEY,
to: contractAddress,
nonce: nonce,
gas: 500000,
data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(),
}
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"本次Transaction的Hash值是:",
hash,
"\n可以在Alchemy的Mempool中查询本次Transaction的状态信息。"
)
} else {
console.log(
"提交本次Transaction出现了一些问题:",
err
)
}
}
)
}).catch((err) => {
console.log(" Promise failed:", err)
})
}
在新加入的代码中,web3.eth.sendSignedTransaction 会返回交易的哈希值,可以用这个哈希值来确保交易被正常开采没有被网络丢弃。在交易签署部分,增加了一些错误检查,以便知晓交易是否成功通过。
八、在 mint-nft.js 中调用 mintNFT 函数并运行它
在上面的操作中,已经将 nft-metadata.json 文件上传到 Pinata 。现在从 Pinata 中获取到 nft-metadata.json 文件的 CID 值,将其作为参数传递给 mintNFT 函数。
可以通过 https://gateway.pinata.cloud/ipfs/[CID] 访问到 nft-metadata.json 文件。
将包含CID的 IPFS URI 地址传递给 mintNFT 函数,最终 mint-nft.js 文件应该类似于这样:
require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/NewNFT.sol/NewNFT.json");
console.log(JSON.stringify(contract.abi)) //将应用程序二进制接口发送到控制台以便查看
const contractAddress = "0x094180BBc8f5e8e9697C0F633D4004e3ecea6510";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, "latest") //get latest nonce
//the transaction
const tx = {
from: PUBLIC_KEY,
to: contractAddress,
nonce: nonce,
gas: 500000,
data: nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI(),
}
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"本次Transaction的Hash值是:",
hash,
"\n可以在Alchemy的Mempool中查询本次Transaction的状态信息。"
)
} else {
console.log(
"提交本次Transaction出现了一些问题:",
err
)
}
}
)
}).catch((err) => {
console.log(" Promise failed:", err)
})
}
mintNFT("ipfs://QmZ643xL21yDh2BzBNHHuVqpXmYnDDfy69Asn3QSJxcbcu")
现在,在项目根目录的命令行中运行:
node scripts/mint-nft.js
稍等片刻,就能看到这样的响应:
本次Transaction的Hash值是: 0x0b0ab9a67c884c513dbd6f7e786e1a59a0d79f26606aaff0678c5367d5fd4b15
可以在Alchemy的Mempool中查询本次Transaction的状态信息。
在 Alchemy 的 Mempool 中查询到本次 Transaction 的状态为被开采:
在 Etherscan 中即可看到本次 Transaction 的完整信息(记住合约地址和 ERC-721 Token ID 待会会用到):
现在,已经在以太坊区块链上成功部署和铸造了一个非同质化通证!之后,使用 mint-nft.js 就可以随心所欲的铸造更多的非同质化通证。只要确保传给 mintNFT 函数的是一个新的 tokenURI 来描述非同质化通证的元数据。否则,将最终生成一堆相同属性只具有不同 ID 的非同质化通证。
九、查看NFT
这里需要使用到手机端 MetaMask 钱包,可以在 MetaMask 官网下载到。打开它导入浏览器上 MetaMask 钱包,并且确保它在 Goerli 测试网中。然后把刚刚记录下来的合约地址和 Token ID 导入到 MetaMask 钱包中,即可看到刚刚铸造的非同质化通证了。添加进去之后可能无法立即展现,给它一点时间,它一定会出现的!
总结
用了两篇文章的篇幅
Ethereum非同质化通证(NFT)的编写与部署
Ethereum非同质化通证(NFT)的铸造与展示
通过一个具体的例子,操作了Ethereum上非同质化通证(NFT)的编写、部署、铸造和展示的具体方法和流程,希望能对大家有所帮助。
最后,欢迎大家走进 Web3.0 的世界!