发行 TRC-721 Token
使用 OpenZeppelin Contracts for TRON、TronBox 和 TronWeb 部署 TRC-721 系列并铸造 NFT。
前置阅读
本指南使用 OpenZeppelin Contracts for TRON 创建一个由合约所有者铸造的 TRC-721 系列,并使用 TronBox 编译、TronWeb 部署到 Shasta 测试网。示例为每个 NFT 单独保存元数据 URI,但不启用链上枚举扩展。
1. 准备账户和开发环境
准备 Node.js 20 或更高版本,并为专用测试账户领取 Shasta 测试 TRX。部署和铸造都会消耗能量。部署脚本从环境变量读取测试账户私钥;不要把私钥写入源码、配置文件或版本库。
后续登记和钱包展示步骤需要 TronLink。先确认 TronLink 与部署脚本均连接到 Shasta,避免将测试操作发送到主网。
2. 创建 TRC-721 合约
在项目的 contracts/ 目录中创建 MyNFT.sol:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import {TRC721} from "@openzeppelin/tron-contracts/token/TRC721/TRC721.sol";
import {TRC721URIStorage} from "@openzeppelin/tron-contracts/token/TRC721/extensions/TRC721URIStorage.sol";
import {Ownable} from "@openzeppelin/tron-contracts/access/Ownable.sol";
contract MyNFT is TRC721, TRC721URIStorage, Ownable {
uint256 private _nextTokenId;
constructor(string memory name_, string memory symbol_)
TRC721(name_, symbol_)
Ownable(msg.sender)
{}
function safeMint(address to, string memory uri)
public
onlyOwner
returns (uint256 tokenId)
{
tokenId = _nextTokenId++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function tokenURI(uint256 tokenId)
public
view
override(TRC721, TRC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(TRC721, TRC721URIStorage)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}构造函数设置系列名称和符号,并将部署者设为所有者。safeMint 受 onlyOwner 限制,Token ID 从 0 开始递增。调用继承的 renounceOwnership() 后,所有者权限会被永久移除,届时将无法再调用 safeMint。项目需要多个铸造者时,可以改用 AccessControl;只有确实需要在链上遍历系列 Token 时,才应组合 TRC721Enumerable,因为枚举会增加铸造和转账的能量消耗。
3. 安装依赖并编译
在项目根目录创建 package.json,固定本教程使用的依赖版本:
{
"name": "tron-token-example",
"version": "1.0.0",
"private": true,
"dependencies": {
"@openzeppelin/tron-contracts": "5.6.0",
"tronweb": "6.5.0"
},
"devDependencies": {
"tronbox": "4.10.0"
},
"overrides": {
"diff": "8.0.3",
"serialize-javascript": "7.0.5",
"ws": "8.21.0",
"tronbox": {
"tronweb": "6.5.0"
}
}
}创建 tronbox-config.js:
module.exports = {
compilers: {
solc: {
version: '0.8.25',
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
}
};编译合约:
npm install
npx tronbox compile编译成功后,build/contracts/MyNFT.json 中包含部署所需的 ABI 和字节码。保存编译器版本、优化设置和依赖锁文件,后续源码验证必须复用相同配置。
4. 部署到 Shasta
将以下脚本保存为项目根目录下的 deploy-nft.mjs:
import { TronWeb } from 'tronweb';
import { readFile } from 'node:fs/promises';
const privateKey = process.env.TRON_PRIVATE_KEY;
if (!/^[0-9a-fA-F]{64}$/.test(privateKey ?? '')) {
throw new Error('TRON_PRIVATE_KEY 必须是 Shasta 测试账户的 64 位十六进制私钥');
}
const artifact = JSON.parse(
await readFile(new URL('./build/contracts/MyNFT.json', import.meta.url), 'utf8')
);
const tronWeb = new TronWeb({
fullHost: 'https://api.shasta.trongrid.io',
privateKey
});
const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
async function waitForContract(address, attempts = 20) {
for (let attempt = 1; attempt <= attempts; attempt++) {
try {
const deployed = await tronWeb.trx.getContract(address);
if (deployed.contract_address && deployed.bytecode) return;
} catch {
// 目标节点尚未查询到该合约。
}
if (attempt < attempts) await wait(3000);
}
throw new Error(`目标节点未能在指定时间内查询到合约,请在 Shasta TRONSCAN 核对地址:${address}`);
}
const contract = await tronWeb.contract().new({
abi: artifact.abi,
bytecode: artifact.bytecode,
feeLimit: 1_000_000_000,
callValue: 0,
parameters: ['My NFT Collection', 'MNFT']
});
const address = tronWeb.address.fromHex(contract.address);
await waitForContract(address);
console.log(`Contract available at: ${address}`);使用测试账户私钥运行脚本,并在运行后立即清除环境变量:
read -s TRON_PRIVATE_KEY
export TRON_PRIVATE_KEY
node deploy-nft.mjs
unset TRON_PRIVATE_KEY脚本只有在目标节点能够查询到合约字节码后才输出地址。保存该地址,并在 Shasta TRONSCAN 上检查合约名称、符号和部署者地址。
如需公开验证源码,先运行 npx tronbox flatten contracts/MyNFT.sol > MyNFT.flat.sol,再将展平文件提交到 Shasta TRONSCAN 验证工具。验证时选择 Solidity 0.8.25、启用优化并将 Runs 设为 200。
5. 铸造第一个 NFT
先准备 NFT 元数据 JSON,并将其上传到可长期访问的位置。tokenURI 应指向该 JSON;常见字段包括 name、description 和 image。使用 BTFS 托管时,参见将 NFT 元数据上传到 BTFS。
在 Shasta TRONSCAN 中打开已部署合约,进入 合约 > 写合约,连接部署账户,然后调用 safeMint:
| 参数 | 填写内容 |
|---|---|
to | 接收 NFT 的 TRON 地址 |
uri | NFT 元数据 JSON 的 URI |
safeMint 仅允许当前所有者调用。交易确认后,首枚 NFT 的 Token ID 为 0。调用 ownerOf(0) 和 tokenURI(0),分别核对持有人和元数据 URI。不要仅根据钱包弹窗判断铸造成功,应检查交易执行结果。
6. 在 TRONSCAN 上登记系列
打开 Shasta TRONSCAN 的 Token 创建页面,选择 TRC721,并使用部署合约的账户连接和签名。
填写合约地址、系列名称、符号、图标、网站和说明。名称与符号必须与合约查询结果一致。登记只会补充 TRONSCAN 的展示信息,不会修改合约或 NFT 元数据。
7. 在 TronLink 中查看 NFT
将 TronLink 切换到 Shasta,通过合约地址添加该 TRC-721 系列。同步完成后,核对 Token ID、持有人和元数据展示结果。若图片或属性未显示,先直接访问 tokenURI(0) 返回的 JSON 和其中的资源 URI,再检查钱包或索引服务是否支持所用 URI 方案。
主网部署前,应测试铸造权限、所有权转移、元数据可用性和异常输入,并对项目新增的业务逻辑进行安全审查。
相关资源
- TRC-721 —— 标准概览
- TRC-721 协议接口 —— 函数与事件参考
- TRC-721 合约交互 —— 读取和写入已部署的 TRC-721 合约
- 安全最佳实践 —— 部署前的安全检查
Updated about 5 hours ago
