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 key getMaxCpuTimeOfOneTx — query the live value via wallet/getchainparameters rather than hard-coding 80). Any loop with an unbounded size will hit OUT_OF_TIME in production.
  • Charge storage access carefully. SSTORE is the most expensive routine opcode — far more than MSTORE. 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 Ownable or AccessControl) 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 (when extcodesize == 0) and breaks account-abstraction interoperability. TRON's tx.origin and msg.sender distinction 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 ReentrancyGuard modifier 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() and receive() cheap. They run on every unexpected TRX transfer; an expensive fallback locks funds for users whose fee_limit is 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 returned bool flag — these never revert on failure, they return false. 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_limit rules) can affect your contract's cost profile. See FeeLimit & Energy cost for cost calibration.

Related resources