Best practices
Production-grade best practices for writing, deploying, and operating TRON smart contracts.
Prerequisites
This page collects the practices that consistently separate production-grade TRON contracts from those that ship with avoidable bugs. Apply these alongside the patterns in Security before deploying to Mainnet.
Design for Energy from day one
- Keep loops bounded. TVM execution time is capped at 80 ms per transaction (chain parameter
#13, API keygetMaxCpuTimeOfOneTx— query the live value viawallet/getchainparametersrather than hard-coding 80). Any loop with an unbounded size will hitOUT_OF_TIMEin production. - Charge storage access carefully.
SSTOREis the most expensive routine opcode — far more thanMSTORE. Prefer in-memory accumulation, then write to storage once. - Cache repeated reads. Re-reading a storage slot in the same function costs Energy each time; loading into a local variable is free after the first load.
- Use events instead of on-chain queryable state when all you need is an off-chain indexer. See Event log.
Access control
- Use a well-tested library (OpenZeppelin's
OwnableorAccessControl) rather than rolling your own admin modifier. - Use explicit allow-lists or role checks for sensitive functions. The legacy
require(tx.origin == msg.sender)pattern that rejected contract callers is now considered an anti-pattern — it can be bypassed during contract construction (whenextcodesize == 0) and breaks account-abstraction interoperability. TRON'stx.originandmsg.senderdistinction matches Ethereum's, but rely on it only for tracing, not for security checks. - Separate roles. A single owner-can-do-everything pattern is a phishing target.
Reentrancy
- Apply the checks-effects-interactions pattern: validate, update state, then make external calls.
- Use OpenZeppelin's
ReentrancyGuardmodifier on functions that call out to untrusted contracts as defense-in-depth. - Treat any external call in a function that also modifies balance, ownership, or allowance state as a candidate for review.
For the full reentrancy walkthrough with example exploits, see Security.
Fallback and receive functions
- Keep
fallback()andreceive()cheap. They run on every unexpected TRX transfer; an expensive fallback locks funds for users whosefee_limitis too low to cover it. - Emit an event from
fallback()so you can reconstruct unexpected transfers off-chain.
Recursive and nested calls
- The TVM's call depth is 64 layers. Recursive contract calls need explicit depth tracking or they hit the limit in production load.
- Always handle the possibility that a sub-call reverts. For low-level
.call()or.delegatecall(), check the returnedboolflag — these never revert on failure, they returnfalse. See VM exception handling.
Deployment and upgrade
- Deploy to testnet(Shasta/Nile) first. Always. See the Quickstart for the full flow.
- Verify the deployed contract on TRONSCAN immediately after deployment. See Contract verification.
- If the contract may need to evolve, use a battle-tested proxy pattern (UUPS or transparent) from day one. Retrofitting upgradeability later is expensive and risky. See Upgrading smart contracts.
After deployment
- Monitor Energy consumption per call. A sudden change is often the first sign of an attack or misuse.
- Keep a runbook for common incidents: what to pause, what to upgrade, who to notify.
- Subscribe to TRON protocol announcements — proposal changes (Energy pricing, the Dynamic Energy Model,
fee_limitrules) can affect your contract's cost profile. See FeeLimit & Energy cost for cost calibration.
Related resources
- Security — TVM-specific security considerations
- Smart contract errors — frequent deployment mistakes
- Migrating from Ethereum — porting walkthrough
- VM exception handling — exception types and call depth
- FeeLimit & Energy cost —
fee_limitcalibration - Upgrading smart contracts — proxy patterns
Updated 7 days ago