← All articles

Smart Contracts and the TVM

Most of what an investigator reads on TRON is not a value transfer. A USDT payment, a SunSwap trade, a JustLend deposit — each is a smart contract being executed, not TRX moving from one address to another. The distinction is not cosmetic. It changes which resource the transaction burns, whether a new account can come into existence, and where the real counterparty is recorded.

The component that runs that code is the TRON Virtual Machine. Understanding it explains why a TRC-20 transfer never activates its recipient, why a wallet’s Energy footprint is a fingerprint of contract activity, and why the to field of a USDT transfer points at Tether’s contract rather than the person being paid.

What the TVM is

The TRON Virtual Machine (TVM) is the runtime environment for TRON smart contracts. Every node in the network maintains its own TVM instance and runs the same code against the same inputs, which is what lets the network agree on the result.

A state machine, not a ledger. A pure payments chain only needs to track balances. A chain that runs arbitrary code needs to track the full state of every contract — storage slots, balances, code — and advance it deterministically. The TRON documentation frames this directly: with smart contracts, “instead of a simple distributed ledger, TRON is actually a distributed state machine.” Each block applies a transition function to the previous state and produces the next one.

A stack machine. The TVM executes as a stack machine with a depth of 1024 items, where each item is a 256-bit word. The 256-bit width was chosen for ease of use with 256-bit cryptography such as Keccak-256 hashes and secp256k1 signatures. If that architecture sounds familiar, it should — it is the same design as Ethereum’s virtual machine, and that is not a coincidence.

A near-twin of the EVM

In TRON’s documentation, the TVM “exhibits fundamental compatibility with the Ethereum Virtual Machine.” Solidity contracts and compiled EVM bytecode largely port across with minimal changes. For an investigator, the practical takeaway is that intuition built on Ethereum contracts mostly transfers — but the exceptions are exactly the places where TRON-specific behavior hides.

The opcodes are the same as the EVM’s except for a small set of TRON-specific additions. Three are worth knowing:

OpcodeWhat it doesWhy it exists
CALLTOKEN (0xD0)Call a contract while attaching a TRC-10 token valueTRON has a native TRC-10 token layer the EVM has no concept of
TOKENBALANCE (0xD1)Read an address’s balance of a given TRC-10 tokenSame — native multi-token support at the protocol level
ISCONTRACT (0xD4)Test whether an address is a contractThe chain exposes natively what an address string cannot reveal

[!NOTE] ISCONTRACT is a quiet gift to anyone reasoning about TRON addresses. A base58 T... string looks identical whether it belongs to a person’s wallet or a deployed contract — the prefix does not distinguish them. The TVM has a dedicated opcode to make that determination on-chain, which is a reminder that “is this address a contract” is a real, answerable question rather than a guess.

Some EVM opcodes are inert on TRON because TRON’s consensus model has no use for them. TRON runs delegated proof-of-stake — see /learn/dpos-super-representatives/ — not proof-of-work, so the DIFFICULTY opcode returns 0. There is no per-block gas auction, so GASLIMIT returns 0 as well, and both the GASPRICE and BASEFEE opcodes return TRON’s fixed energyPrice. One more divergence matters for anyone tracing deterministically-deployed contracts: TRON’s CREATE2 address derivation uses a 0x41 prefix where the EVM uses 0xff — the same 0x41 byte that sits behind every TRON mainnet address.

Triggering a contract is its own transaction type

TRON does not bury contract calls inside ordinary transfers. The protocol defines distinct transaction types, and the difference between them is the difference between moving value and running code:

Transaction typeNumeric typeWhat it does
TransferContract1Moves native TRX
TransferAssetContract2Moves a TRC-10 token
CreateSmartContract30Deploys a contract
TriggerSmartContract31Calls a function on a deployed contract

A TriggerSmartContract transaction carries a function selector (for example, transfer(address,uint256)), ABI-encoded parameters, an optional amount of TRX to send into the contract, and a fee limit capping what the caller is willing to spend. It names a contract_address — the contract being called — and nothing else at the top level.

This is why reading TRC-20 flow requires decoding, not just reading. When a wallet sends USDT, the transaction is a TriggerSmartContract calling transfer(address,uint256) on Tether’s contract. The transaction’s contract_address is the USDT contract. The actual recipient and amount are encoded inside the parameter field, not exposed as a top-level to. An investigator who reads only the surface fields sees a wallet “interacting with the USDT contract” and misses who was paid. The counterparty is real, but it has to be decoded out of the call data.

Energy: the cost of computation

Running code costs something, and on TRON that something is Energy. Energy is “the unit that measures the amount of computation required by the TRON Virtual Machine to perform specific operations,” and “the execution of each instruction of a smart contract consumes a certain amount of Energy while running.” Every opcode the TVM executes has an Energy price, metered as the contract runs.

Energy is the contract resource. Ordinary transactions — a plain TRX transfer — consume Bandwidth instead. That split is itself an investigative signal: a wallet that only ever sends TRX burns Bandwidth and leaves no Energy footprint, while a wallet interacting with DeFi, tokens, or any contract necessarily consumes Energy. Energy usage is, in effect, a record of contract activity.

Where Energy comes from. Energy is generated by staking TRX, or received as a delegation from another account — the staking mechanics are covered in /learn/stake-2/, and delegation as an ownership signal in /learn/fueling/. When a caller’s staked Energy is insufficient, the shortfall is paid by burning TRX at the network’s Energy unit price. As of June 2026 that price is 100 sun per Energy unit (0.0001 TRX), but it is a governance-adjustable network parameter rather than a protocol constant — which is precisely why the TVM’s GASPRICE opcode returns a fixed value instead of a market rate. TRON has no fluctuating gas auction; the price is set by network governance and changes only when governance changes it.

Failed calls are not free. A reverted contract execution still costs the Energy it consumed before failing, and the transaction is still recorded on-chain. A require-style revert charges only the Energy used up to the point of failure; an assert-style failure can consume the entire allocation up to the transaction’s fee limit. For an investigator this means failed transactions are part of a wallet’s footprint and carry real cost — they are not noise to be discarded.

Why a contract cannot quietly create an account

This is the load-bearing fact, and it has a history worth getting right.

In TRON’s original design, a system-level transfer (TransferContract or TransferAssetContract) to a non-existent address would create and activate that account, charging the standard 1 TRX activation fee. A transfer made from inside a smart contract did not. As the protocol documentation put it, “if the transfer to address does not exist it can not create an account by smart contract transfer.” A contract that tried to send TRX or a TRC-10 token to an uninitialized address failed outright.

That inconsistency was deliberate to fix. TIP-54 — now Final — addressed it: a contract can now cause an account to be activated when it transfers TRX or TRC-10 to an uninitialized address. The catch is the cost. Account activation triggered through a contract incurs an extra 25,000 Energy charge (as of June 2026), a deliberate price that keeps activation from being a free side effect of contract execution.

[!SOURCE] TIP-54’s specification discusses the change at a proposal-stage figure (a 0.1 TRX activation fee); the current dev-hub documentation quantifies it as a 25,000 Energy surcharge. These describe the proposal and finalized stages of the same change. Treat the 25,000 Energy figure as the authoritative current cost.

The principle this preserves is the one TRONORIGIN’s attribution logic depends on. Even after TIP-54, a contract cannot conjure an account out of nothing. Activation still requires a value transfer the contract pays for, and that contract call was itself initiated by an externally-owned account — a real wallet that signed a TriggerSmartContract. Activation always traces back to an externally-owned account, never to a contract acting on its own. The forensic consequences of that rule — who counts as a first-activator, and why a router or a token contract never does — are the subject of /learn/account-activation/.

What this means on-chain

Three practical consequences fall out of how the TVM works:

A TRC-20 transfer is code execution, not a value transfer. It is a TriggerSmartContract, so it consumes Energy rather than Bandwidth, it does not by itself activate the recipient (only TRX and TRC-10 transfers do that), and its on-chain contract_address is the token contract — never the counterparty. A wallet that holds only USDT and has never received TRX may sit at an address that was activated by someone else entirely. TRONORIGIN treats a token-only funding history as a weak attribution signal for exactly this reason: a TRC-20 transfer tells you less about who controls an address than a native transfer does.

An Energy footprint is a contract-activity fingerprint. Because Energy is consumed only by TVM execution, a wallet’s Energy history maps its contract usage. The deterministic, fixed-price nature of that metering is what makes pattern analysis possible: identical contract calls cost near-identical Energy, so a wallet whose Energy consumption barely varies is behaving like an automated process, while a wallet with variable consumption is interacting with a range of contracts the way a person does.

The chain records intent, but contracts do not have it. A contract executes the call it is given; it does not decide to send funds. When tracing a flow through a contract, the question is never “what did the contract want” but “which externally-owned account triggered this execution, and what did the call data say to do.” The TVM is the machine that carries out that instruction faithfully — which is exactly why the instruction, not the machine, is where attribution lives. Reading a TRON address well, covered in /learn/reading-tron-address/, starts from that separation.

Sources