本教程适合对智能合约、区块链开发、NFT 铸币感兴趣的开发者。请谨记:所有操作仅用于教育用途,不构成投资建议。
NFT(非同质化代币)在 2025 年依旧是数字资产、游戏、社交身份验证的核心载体。下面将手把手带你完成一整套 ERC-721 智能合约开发 → Hardhat 编译/部署 → Rinkeby 测试网验证 → OpenSea 上架 的流程,全程约 45 分钟即可跑通。
NFT 概念速读
NFT(Non-Fungible Token)的本质是“唯一且不可分割”的数据资产:
- 与 ERC-20(同质化代币)相比,它像一张签名画,无法切成 10 份卖“碎片”。
- 代表作品、门票、域名、链游装备等一切需要可验证稀缺性的数字产权。
核心关键词:NFT铸造、ERC721、OpenZeppelin、Hardhat部署、Rinkeby测试网。
第一步:初始化 Hardhat 项目
1.1 安装依赖
创建文件夹 nft-web3-example,进入后:
npm init -y
npm install --save-dev hardhat
npm install --save-dev \
@nomiclabs/hardhat-waffle \
ethereum-waffle \
chai \
@nomiclabs/hardhat-ethers \
ethers1.2 快速脚手架
npx hardhat
# 选择 Create a basic sample project完成后你将得到:
contracts/
scripts/
test/
hardhat.config.js第二步:编写 ERC-721 智能合约 FOOL_NFT.sol
2.1 必需品
安装 OpenZeppelin:
npm install @openzeppelin/contracts2.2 合约源码
在 contracts/FoolNFT.sol 粘贴:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
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/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract FoolNFT is ERC721, ERC721URIStorage, ERC721Burnable, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("FoolNFT","FOOL") {}
function safeMint(address to, string calldata uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function freeMint(address to, string calldata uri) public {
uint256 tokenId = _tokenIdCounter.current();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
_tokenIdCounter.increment();
}
/* 以下为 Solidity 要求覆盖的函数 */
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
function currentCounter() public view returns (uint256) {
return _tokenIdCounter.current();
}
}- OpenZeppelin已帮你解决重入攻击、溢出、权限控制,开箱即用。
freeMint任意用户可调用,仅测试环境使用。
第三步:管理节点与密钥
我们将使用 Infura 远程节点 与 MetaMask 测试账号,所有凭证放在 .env。
3.1 .env 示例
PUBLIC_KEY=0xD9... # MetaMask 公开地址
PRIVATE_KEY=0x9a... # MetaMask 私钥(测试用)
INFURA_API=https://rinkeby.infura.io/v3/xxx
NETWORK=rinkeby安装 dotenv:
npm install dotenv第四步:编译 + 部署
4.1 编译简写
npm i -g hardhat-shorthand
hh compilehh 硬编码别名,秒通关。
4.2 部署脚本
scripts/deploy.js
const { ethers } = require("hardhat");
async function main() {
const FoolNFT = await ethers.getContractFactory("FoolNFT");
const nft = await FoolNFT.deploy();
await nft.deployed();
console.log("NFT合约地址:", nft.address);
}
main()
.then(() => process.exit(0))
.catch(err => { console.error(err); process.exit(1); });运行:
hh run scripts/deploy.js --network rinkeby输出:
NFT合约地址: 0x1F8fa1e7...85b👉 还在为部署手续费发愁?先用Rinkeby免费领取测试ETH再动手
4.3 水龙头领取测试币
- 打开 fauceth.komputing.org。
- 选择
Rinkeby→ 粘贴钱包地址 → 领取 0.1 ETH。
第五步:铸造 NFT
5.1 图片永久存到 IPFS
使用 nft.storage(基于 IPFS + Filecoin 长期存储)。
注册 nft.storage →
API Token→ 复制到.env:NFT_STORAGE_TOKEN=ey... NFT_CONTRACT_ADDRESS=0x1F8fa1e7...85b安装 SDK:
npm install nft.storage
5.2 mint.js 脚本
scripts/mint.js
require("dotenv/config");
const { ethers } = require("hardhat");
const { NFTStorage, File } = require("nft.storage");
const fs = require("fs");
const path = require("path");
const client = new NFTStorage({ token: process.env.NFT_STORAGE_TOKEN });
const provider = new ethers.providers.InfuraProvider("rinkeby", process.env.INFURA_PROJECT_ID);
const wallet = new ethers.Wallet(`0x${process.env.PRIVATE_KEY}`, provider);
// 已有合约
const contractArtifact = require("../artifacts/contracts/FoolNFT.sol/FoolNFT.json");
const contract = new ethers.Contract(process.env.NFT_CONTRACT_ADDRESS, contractArtifact.abi, provider);
const contractWithSigner = contract.connect(wallet);
async function mint(filepath, name, description) {
const image = fs.readFileSync(filepath);
const metadata = await client.store({
name,
description,
image: new File([image], name, { type: "image/png" })
});
const tx = await contractWithSigner.freeMint(process.env.PUBLIC_KEY, metadata.url);
await tx.wait();
console.log("区块哈希:", tx.blockHash);
}
async function main() {
const assets = fs.readdirSync("./assets");
for (const file of assets) {
await mint(`./assets/${file}`, file, "A FoolNFT test");
}
}
main().catch(e => { console.error(e); process.exit(1); });执行:
hh run scripts/mint.js --network rinkeby第六步:OpenSea / LooksRare 查看 NFT
- 打开 testnets.opensea.io 或 Rinkeby-LookRare。
- 用同一 MetaMask 登录 → 进入个人页 → My NFTs。
- 你就能看到刚刚铸造的
FoolNFT!
场景扩展:一键发同款 NFT
开发者可:
- 把合约里的 metadata 加入属性字段(性别、稀有度等)。
- 用 React + RainbowKit 做前端,10 行代码完成钱包连接 + 购买按钮。
- 在主网上架前务必进行 Solidity 静态分析 & 审计,避免致命漏洞。
FAQ
Q1:部署到 Rinkeby 失败,提示 “ insufficient funds ”?
A:领取测试 ETH 后仍不够?尝试 npx hardhat node 本地私链测试。
Q2:production 网络如何降低铸造 Gas?
A:使用 OpenZeppelin 的 ERC721A 或者 Layer2(如 Polygon、Arbitrum)。
Q3:NFT 图片就存在一个中心化 CDN 不行吗?
A:中心化存储容易被和谐下线;IPFS += 长期可验证,保证 去中心化 NFT 属性。
Q4:Solidity 版本必须 0.8.x 吗?
A:Hardhat 默认支持 0.8 全线特性,包含内置溢出检查,比旧版更省心。
Q5:我怎么把 NFT 转让给朋友?
A:在合约里加一条 safeTransferFrom,再调用浏览器钱包内置的转账即可。
Q6:源码开源与可升级性冲突怎么办?
A:使用 ERC721Upgradeable + 透明代理 (OpenZeppelin UPGRADE) 模式,留好后门给错打补丁。
最后的彩蛋
本文仓库完整源码在: github.com/hicoldcat/nft-web3-example
克隆并 npm install 后,跟着 README 再跑一遍,加深记忆。祝你早日拥有自己的 数字藏品品牌!