Issuing a TRC-20 token
Compile and deploy a TRC-20 token with OpenZeppelin Contracts for TRON, TronBox, and TronWeb.
Prerequisites
This guide creates a fixed-supply token with the TRC20 implementation from OpenZeppelin Contracts for TRON, compiles it with TronBox, and deploys it to the Shasta testnet with TronWeb. Validate the contract on Shasta before considering a Mainnet deployment.
1. Prepare an account and development environment
Prepare Node.js 20 or later and fund a dedicated test account with Shasta test TRX. Deployment consumes Energy. Before deploying to Mainnet, estimate the Energy requirement and provide the deployment account with enough staked Energy or TRX.
The deployment script reads the private key from an environment variable. Use a dedicated test account and never store the key in source code, configuration files, or version control. The later token-recording and wallet-display steps require TronLink.
2. Prepare the TRC-20 contract code
Create contracts/MyToken.sol in the project:
// 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);
}
}This implementation uses 18 decimals by default. initialSupply is expressed in base units, so one million tokens is 1000000000000000000000000. To use another precision, override decimals() and calculate the initial supply with the same precision. If the project needs later minting, burning, pausing, or role-based administration, explicitly compose the appropriate extensions and restrict each administrative entry point.
3. Compile and deploy to Shasta
Create package.json in the project root with the dependency versions used by this tutorial:
{
"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"
}
}
}Create tronbox-config.js and configure TronBox to use Solidity 0.8.20:
// tronbox-config.js
module.exports = {
compilers: {
solc: {
version: '0.8.20',
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
}
};Install the dependencies and compile the contract:
npm install
npx tronbox compileSave the following script as deploy-trc20.mjs in the project root:
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 must be a 64-character hexadecimal Shasta test-account key');
}
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 does not contain a valid ABI and hexadecimal bytecode');
}
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 {
// The target node cannot retrieve the contract yet.
}
if (attempt < attempts) await wait(3000);
}
throw new Error(`The target node could not retrieve the contract in time. Check this address on 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}`);Run the script with the Shasta test-account key and clear the environment variable immediately afterward:
read -s TRON_PRIVATE_KEY
export TRON_PRIVATE_KEY
node deploy-trc20.mjs
unset TRON_PRIVATE_KEYThe script prints the address only after the target node can retrieve the deployed bytecode. Record the compiler version, optimizer settings, dependency lockfile, and contract address. Then call name(), symbol(), decimals(), and totalSupply(), pass the deployment address to balanceOf(), and compare the on-chain values with the constructor arguments.
4. Verify the TRC-20 contract (optional)
First flatten the contract and its dependencies into one file:
npx tronbox flatten contracts/MyToken.sol > MyToken.flat.solOpen the Shasta TRONSCAN verification tool, enter the contract address, and upload MyToken.flat.sol. See Contract verification for the complete process.
The verification build must exactly match the deployment build:
- Main contract:
MyToken - Solidity compiler version:
0.8.20 - License:
MIT - Optimization and Runs: optimization enabled, with Runs set to
200
After verification, check the constructor arguments, ABI, and on-chain bytecode again on TRONSCAN.
5. Record the token on TRONSCAN
Open the Shasta TRONSCAN token record tool, select TRC-20, and connect the account that deployed the contract.
Enter the token name, symbol, decimals, contract address, icon, project website, and description. The name, symbol, and decimals must match values queried from the contract; do not rely on the local source alone. Submit the form with the deployment account and allow time for TRONSCAN to synchronize the record.
Recording adds display metadata to TRONSCAN. It does not change the contract code, supply, or holder balances.
6. Add the token to TronLink
Switch TronLink to the network where the contract is deployed, then search for and add the token by its TRC-20 contract address. If it is not immediately discoverable, confirm the network and address, then wait for the TRONSCAN record to synchronize.
After adding the token, verify the displayed name, symbol, decimals, and balance, and make a small test transfer. Before a Mainnet deployment, review the permission model, supply rules, and integration requirements, and obtain a security review for any project-specific logic.
Related resources
- TRC-20 protocol interface — function and event reference
- Deploy a TRC-20 token with TronWeb — runnable compilation and deployment example
- Interacting with TRC-20 contracts — read state and call methods
- Contract verification — verify a deployed contract on TRONSCAN
Updated 6 days ago