在这个Demo里将使用 Metamask、Solidity、Hardhat、Pinata 和 Alchemy 在 Sepolia 测试网络上创建和部署一个 ERC-721 智能合约。整体分为两步:编写ERC-721合约并部署,铸造NFT。最新版文章请查看:【Web3】在以太坊上创建 NFT(ERC-721)
创建和部署一个 ERC-721 智能合约
在以太坊区块链上发布一个 NFT 智能合约。通常是 ERC-721 或 ERC-1155 智能合约。两种 NFT 智能触点标准的最大区别在于,ERC-1155 是一种多令牌标准,包括批量功能,而 ERC-721 是一种单令牌标准,因此一次只支持传输一个令牌。
准备工作
- 配置Hardhat环境【Web3】部署Hardhat开发环境
- 准备一个MetaMask钱包
- 创建 Alchemy 应用程序
在Alchemy上注册账号并创建一个APP,创建网络选择Sepolia测试网,如果没有测试币可以在Ethereum Sepolia Faucet领取,每24h可以领0.5SepoliaEth。
编写合约
创建一个智能合约MyNFT.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("Sherry's first NFT", "NFT") {}
function mintNFT(
address recipient,
string memory tokenURI
) public onlyOwner returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
⚠️这里需要注意一个问题,在v5.0.0的版本中openzeppelin删除了Counters.sol文件,所以建议使用4.0.0版本的openzeppelin,或者修改合约Remove
Counters.sol
· Issue #4233 · OpenZeppelin/openzeppelin-contracts
部署合约
- 配置环境变量(这一步其实不做也可以,直接在hardhat.config.js里写死)
npm install dotenv --save
在根目录创建.env文件
API_URL = "https://eth-sepolia.g.alchemy.com/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"
- 配置harhat.config.js文件,连接到Alchemy网络
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
require("@nomicfoundation/hardhat-ethers");
const { API_URL, PRIVATE_KEY } = process.env;
module.exports = {
solidity: "0.8.17",
defaultNetwork: "sepolia",
networks: {
hardhat: {},
sepolia: {
url: API_URL,
accounts: [PRIVATE_KEY],
},
},
};
- 部署
npx hardhat run scripts/deploy.js --network sepolia
完了之后可以查询订单详情https://sepolia.etherscan.io/
可以查看对应的小狐狸钱包里的代币数量也会对应减少,Alchemy面板里也会有对应的操作日志。
在这里查看
这里有两个重要的请求:eth_sendRawTransaction(将智能合约写入 Sepolia 链)和 eth_getTransactionByHash(根据哈希值读取交易信息)(发送交易时的典型模式)。
铸造NFT
调用 NFT 智能合约上的铸币函数来铸造 NFT。铸币只是在区块链上发布不可篡改代币的唯一实例。
使用 IPFS 配置 NFT 的元数据
使用便捷的 IPFS 和工具包Pinata来存储我们的 NFT 资产和元数据,确保我们的 NFT 真正去中心化。
- 上传文件
- 制作metadata
我们要将NFT部署到链上就需要metadata,也就是NFT 的元数据。元数据使NFT具有名称、描述、图像(或其他数字资产)等属性。以下是 tokenURI 的示例,其中包含 NFT 的元数据。
{
"name": "Sparks",
"description": "Description of NFT",
"external_url": "https://peach-wonderful-marlin-556.mypinata.cloud",
"image": "ipfs://QmXbdfGRf91dy1VFMH19SfzVBAD8YQBqYobj1LnezaTrbs"
}
- 将metadata上传到Pinata
调用合同的 mintNFT 函数
还记得上传到 Pinata 的 metadata.json 吗?从 Pinata 获取它的哈希代码,并将以下内容传递给 mintNFT https://gateway.pinata.cloud/ipfs/ 调用,在scripts目录下创建一个mint-nft.js文件。
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
// Create a signer
const privateKey = process.env.PRIVATE_KEY;
const signer = new ethers.Wallet(privateKey, provider);
// Get contract ABI and address
const abi = contract.abi;
const contractAddress = "0x80bF4c753cb9BF7d12d41ADfa21FD8F2c09D9393";
// Create a contract instance
const myNftContract = new ethers.Contract(contractAddress, abi, signer);
// Get the NFT Metadata IPFS URL
const tokenUri =
"https://peach-wonderful-marlin-556.mypinata.cloud/ipfs/QmZ2ca5M4coR3GEg66K3dpVqf9TkvbFAiLFqWkjcGUnzBD";
// Call mintNFT function
const mintNFT = async () => {
let nftTxn = await myNftContract.mintNFT(signer.address, tokenUri);
await nftTxn.wait();
console.log(
`NFT Minted! Check it out at: https://sepolia.etherscan.io/tx/${nftTxn.hash}`
);
};
mintNFT()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
通常,铸币函数要求您传递两个变量作为参数,第一个是接收者,指定接收您新铸币的 NFT 的地址,第二个是 NFT 的 tokenURI,一个解析为描述 NFT 元数据的 JSON 文档的字符串。
- 开始铸造NFT
node scripts/mint-nft.js
点击打开可以在测试网上看到
查看已部署的NFT
- 在OpenSea 里看对应的图片
可以在 OpenSea 上搜索您的合同地址,查看 NFT。网址:https://testnets.opensea.io/zh-CN/assets/sepolia/
- 在MetaMask里查看
将Contract Address复制并导入到钱包里(不知道为什么图片没有展示出来)
给NFT设置价格
打开OpenSea里对应的NFT界面,点击NFT下的待售清单按钮可以设置NFT的价格。
设置成功会连接到MetaMask钱包签署合约。
设置成功后的页面
说在最后
至此我们就完成了一个NFT从创造到交易的全过程。