Solidity on TRON: Writing Smart Contracts for TVM
Solidity is the dominant language for TRON smart contracts. Developers familiar with Ethereum ERC-20 and DeFi patterns can port much of their knowledge to TRC-20 and TVM — with attention to address formats, Energy costs, and toolchain differences.
This guide covers Solidity conventions on TRON, standard token implementation, common pitfalls, and the path from code to mainnet.
Solidity → TVM pipeline
- Write
.solfiles withpragma solidity - Compile via TronBox / TronIDE → bytecode + ABI
- Deploy with migration or TronWeb
- Interact via TronWeb or HTTP trigger
- Verify source on TronScan
Execution environment: TVM explained.
TRC-20 template
Standard fungible token using OpenZeppelin:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract ExampleToken is ERC20, Ownable {
constructor() ERC20("Example", "EXM") Ownable(msg.sender) {
_mint(msg.sender, 1_000_000 * 10 ** decimals());
}
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
}
mint with onlyOwner is convenient but dangerous if owner key compromised — security guide.
Address handling
- On-chain
addresstype is 20-byte hex internally - TronWeb converts Base58
T...↔ hex for APIs msg.senderis contract caller address in TVM
Always validate inputs:
require(to != address(0), "zero address");
Differences from Ethereum Solidity
| Topic | Ethereum habit | TRON adjustment |
|---|---|---|
| Gas | gasleft() | Energy metering via chain |
block.timestamp | ~12s resolution | ~3s blocks |
tx.origin | Anti-pattern | Still anti-pattern on TRON |
| Chainlink oracles | Widely used | Verify TRON oracle availability |
feeLimit | N/A | Set in TronWeb send options |
Re-test reentrancy guards, call patterns, and delegatecall on Shasta.
Energy-efficient patterns
- Pack storage variables
- Minimize on-chain strings (name/symbol acceptable at deploy)
- Avoid unbounded loops over user arrays
- Use
immutableandconstantwhere possible
Expensive contracts increase USDT transfer costs for integrators.
Testing workflow
- TronBox compile
- Deploy Shasta
- TronWeb script: transfer, approve, transferFrom
- Fuzz edge cases: zero amount, max uint, reentrancy attempt
Common vulnerabilities on TRON
Same class as EVM:
- Reentrancy
- Integer overflow (Solidity 0.8+ has built-in checks)
- Access control missing on
mint/withdraw - Honeypot blacklist (malicious tokens)
Auditors apply Ethereum methodologies to TVM bytecode.
Integration with dApps
Frontend calls via TronLink-injected TronWeb:
const contract = await tronWeb.contract(abi, contractAddress);
await contract.transfer(to, amount).send({ feeLimit: 100_000_000 });
Backend: send USDT programmatically.
Verify and publish ABI
Post-deploy:
- TronScan contract verification
- Publish ABI in docs for integrators
- Document admin functions and ownership
Compiler version pinning
Pin exact solc version in TronBox config. TronScan verification requires matching compiler settings including optimization runs. Mismatched settings fail verification even with correct source code.
Import paths and OpenZeppelin
Use @openzeppelin/contracts versions tested on TVM. Some Ethereum-specific patterns (e.g., certain oracle interfaces) need adaptation. Test SafeERC20 for TRC-20 interactions in your contracts — non-standard tokens exist on TRON with fee-on-transfer behavior breaking naive integrations.
Related guides
FAQ
Which Solidity version should I use on TRON?
Match the version supported by your TronBox or TronIDE compiler — commonly 0.8.x. Pin versions in production and verify with same compiler on TronScan.
Can I use OpenZeppelin contracts on TRON?
Yes. Import OpenZeppelin ERC20, Ownable, etc., and compile with TronBox. Verify license and version compatibility.
Is Solidity the only option?
Solidity dominates. Some tools experiment with other languages compiling to TVM — production tokens are almost all Solidity.
How do I call another contract?
interface + Contract(addr).method() pattern identical to Ethereum.
TRC-20 same file name as ERC20.sol?
OpenZeppelin package names say ERC20 but deploy to TRON as TRC-20 standard behavior.
Thanks — your feedback helps us improve the docs.