发行 TRC-20 Token
使用 OpenZeppelin Contracts for TRON、TronBox 和 TronWeb 编译并部署 TRC-20 Token。
前置阅读
本指南使用 OpenZeppelin Contracts for TRON 提供的 TRC20 实现创建固定供应量 Token,并使用 TronBox 编译、TronWeb 部署到 Shasta 测试网。确认合约行为符合预期后,再考虑部署到主网。
1. 准备账户和开发环境
准备 Node.js 20 或更高版本,并为测试账户领取 Shasta 测试 TRX。部署会消耗能量;主网部署前应先估算能耗,并为部署账户准备足够的质押能量或 TRX。
部署脚本从环境变量读取私钥。请仅使用专门的测试账户,不要把私钥写入源码、配置文件或版本库。后续登记和钱包展示步骤需要 TronLink。
2. 准备 TRC-20 合约代码
在项目的 contracts/ 目录中创建 MyToken.sol:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {TRC20} from "@openzeppelin/tron-contracts/token/TRC20/TRC20.sol";
contract MyToken is TRC20 {
constructor(string memory name_, string memory symbol_, uint256 initialSupply)
TRC20(name_, symbol_)
{
_mint(msg.sender, initialSupply);
}
}该实现默认使用 18 位精度。initialSupply 使用最小单位,例如 100 万枚 Token 应传入 1000000000000000000000000。需要其他精度时,应覆盖 decimals(),并按相同精度计算初始供应量。若项目还需要后续增发、销毁、暂停或角色权限,应按业务规则显式组合相应扩展并限制管理入口。
3. 编译并部署到 Shasta
在项目根目录创建 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,将编译器设置为 Solidity 0.8.20:
// tronbox-config.js
module.exports = {
compilers: {
solc: {
version: '0.8.20',
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
}
};安装依赖并编译合约:
npm install
npx tronbox compile将以下脚本保存为项目根目录下的 deploy-trc20.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/MyToken.json', import.meta.url), 'utf8')
);
if (!Array.isArray(artifact.abi) || !/^(0x)?[0-9a-fA-F]+$/.test(artifact.bytecode ?? '')) {
throw new Error('MyToken.json 缺少有效的 ABI 或十六进制字节码');
}
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: ['MyToken', 'MTK', '1000000000000000000000000']
});
const address = tronWeb.address.fromHex(contract.address);
await waitForContract(address);
console.log(`Contract available at: ${address}`);使用 Shasta 测试账户私钥运行脚本,并在运行后立即清除环境变量:
read -s TRON_PRIVATE_KEY
export TRON_PRIVATE_KEY
node deploy-trc20.mjs
unset TRON_PRIVATE_KEY脚本只有在目标节点能够查询到合约字节码后才输出地址。记录编译器版本、优化设置、依赖锁文件和合约地址;随后调用 name()、symbol()、decimals() 和 totalSupply(),并将部署账户地址传给 balanceOf(),确认链上结果与构造参数一致。
4. 验证 TRC-20 合约(可选)
先将合约及其依赖展平为单文件:
npx tronbox flatten contracts/MyToken.sol > MyToken.flat.sol打开 Shasta TRONSCAN 验证工具,填写合约地址并上传 MyToken.flat.sol。有关完整流程,请参见合约验证。
验证时必须使用与部署构建完全一致的配置:
- 主合约:
MyToken - Solidity 编译器版本:
0.8.20 - 许可证:
MIT - 优化与 Runs:启用优化,Runs 为
200
验证成功后,应再次在 TRONSCAN 上核对构造参数、合约 ABI 和链上字节码。
5. 在 TRONSCAN 上登记 Token
打开 Shasta TRONSCAN Token 登记工具,选择 TRC-20,然后使用部署合约的账户连接并签名。
填写 Token 名称、符号、精度、合约地址、图标、项目网站和说明。名称、符号与精度必须与合约查询结果一致;不要仅依据本地源码填写。提交后等待 TRONSCAN 完成数据同步。
登记只是在 TRONSCAN 中补充 Token 展示信息,不会改变合约代码、供应量或持有人余额。
6. 将 Token 添加到 TronLink
在 TronLink 中切换到合约所在网络,通过 TRC-20 合约地址搜索并添加 Token。如果暂时无法搜索到,先确认合约地址和网络正确,再等待 TRONSCAN 完成登记信息同步。
添加后,核对 TronLink 显示的名称、符号、精度和余额,并使用少量测试 Token 完成一次转账测试。主网部署前还应检查权限配置、供应量规则和外部集成需求,并对新增的业务逻辑进行安全审查。
相关资源
- TRC-20 协议接口 —— 函数与事件参考
- 用 TronWeb 部署 TRC-20 Token —— 可运行的编译与部署示例
- 与 TRC-20 合约交互 —— 读取状态并调用方法
- 合约验证 —— 在 TRONSCAN 上验证已部署的合约
Updated about 12 hours ago
