Opcodes
Full TVM opcode reference, including TRON-specific opcodes for TRC-10, staking, and voting.
Prerequisites
The TVM opcodes are the same as the EVM opcodes, with a small set of TRON-specific additions for TRC-10 tokens, staking (Stake 2.0), voting, and contract introspection. Each section below groups related opcodes; for the high-level differences from the EVM, see TVM vs EVM.
Energy entries marked [A*] link to the formula in the Appendix: Energy cost calculations.
Arithmetic — 0x00 … 0x0B
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0x00 | STOP | 0 | halt execution | ||
| 0x01 | ADD | 3 | a, b | a + b | (u)int256 addition modulo 2²⁵⁶ |
| 0x02 | MUL | 5 | a, b | a × b | (u)int256 multiplication modulo 2²⁵⁶ |
| 0x03 | SUB | 3 | a, b | a − b | (u)int256 subtraction modulo 2²⁵⁶ |
| 0x04 | DIV | 5 | a, b | a // b | uint256 division |
| 0x05 | SDIV | 5 | a, b | a // b | int256 division |
| 0x06 | MOD | 5 | a, b | a % b | uint256 modulus |
| 0x07 | SMOD | 5 | a, b | a % b | int256 modulus |
| 0x08 | ADDMOD | 8 | a, b, N | (a + b) % N | (u)int256 addition modulo N |
| 0x09 | MULMOD | 8 | a, b, N | (a × b) % N | (u)int256 multiplication modulo N |
| 0x0A | EXP | A1 | a, b | a ** b | uint256 exponentiation modulo 2²⁵⁶ |
| 0x0B | SIGNEXTEND | 5 | b, x | SIGNEXTEND(x, b) | sign-extend x from (b+1) bytes to 32 bytes |
Comparison and bitwise — 0x10 … 0x1E
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0x10 | LT | 3 | a, b | a < b | uint256 less-than |
| 0x11 | GT | 3 | a, b | a > b | uint256 greater-than |
| 0x12 | SLT | 3 | a, b | a < b | int256 less-than |
| 0x13 | SGT | 3 | a, b | a > b | int256 greater-than |
| 0x14 | EQ | 3 | a, b | a == b | (u)int256 equality |
| 0x15 | ISZERO | 3 | a | a == 0 | (u)int256 iszero |
| 0x16 | AND | 3 | a, b | a & b | bitwise AND |
| 0x17 | OR | 3 | a, b | a | b | bitwise OR |
| 0x18 | XOR | 3 | a, b | a ^ b | bitwise XOR |
| 0x19 | NOT | 3 | a | ~a | bitwise NOT |
| 0x1A | BYTE | 3 | i, x | i-th byte of x | i-th byte from the left (i starts at 0) |
| 0x1B | SHL | 3 | shift, val | val << shift | shift left |
| 0x1C | SHR | 3 | shift, val | val >> shift | logical shift right |
| 0x1D | SAR | 3 | shift, val | val >> shift | arithmetic shift right |
| 0x1E | CLZ | 5 | x | leadingZeros(x) | Osaka. Returns the number of leading zero bits in the 256-bit unsigned integer x; returns 256 when x = 0 |
CLZ is controlled by the Osaka governance proposal. Upgrading a node to GreatVoyage-v4.8.2 only provides the implementation; contracts cannot rely on this opcode until the corresponding proposal has been activated on the target network.
Hashing — 0x20
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0x20 | SHA3 | A2 | ost, len | keccak256(mem[ost:ost+len]) | Keccak-256 (despite the historical "SHA3" name) |
Environment — 0x30 … 0x3F
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Mem / Storage | Notes |
|---|---|---|---|---|---|---|
| 0x30 | ADDRESS | 2 | address(this) | address of executing contract | ||
| 0x31 | BALANCE | 20 | addr | addr.balance | balance, in sun | |
| 0x32 | ORIGIN | 2 | tx.origin | address that originated the tx | ||
| 0x33 | CALLER | 2 | msg.sender | address of msg sender | ||
| 0x34 | CALLVALUE | 2 | msg.value | msg value, in sun | ||
| 0x35 | CALLDATALOAD | 3 | idx | msg.data[idx:idx+32] | read word from msg data at index idx | |
| 0x36 | CALLDATASIZE | 2 | len(msg.data) | length of msg data, in bytes | ||
| 0x37 | CALLDATACOPY | A3 | dstOst, ost, len | mem[dstOst:dstOst+len] := msg.data[ost:ost+len] | copy msg data | |
| 0x38 | CODESIZE | 2 | len(this.code) | length of executing contract's code, in bytes | ||
| 0x39 | CODECOPY | A3 | dstOst, ost, len | mem[dstOst:dstOst+len] := this.code[ost:ost+len] | copy executing contract's code | |
| 0x3A | GASPRICE | 2 | tx.gasprice | returns energyPrice on TVM | ||
| 0x3B | EXTCODESIZE | 20 | addr | len(addr.code) | size of code at addr, in bytes | |
| 0x3C | EXTCODECOPY | A4 | addr, dstOst, ost, len | mem[dstOst:dstOst+len] := addr.code[ost:ost+len] | copy code from addr | |
| 0x3D | RETURNDATASIZE | 2 | size | size of returned data from last external call | ||
| 0x3E | RETURNDATACOPY | A3 | dstOst, ost, len | mem[dstOst:dstOst+len] := returndata[ost:ost+len] | copy returned data from last external call | |
| 0x3F | EXTCODEHASH | 400 | addr | hash | addr.exists ? keccak256(addr.code) : 0 |
Block and chain — 0x40 … 0x4A
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0x40 | BLOCKHASH | 20 | blockNum | blockHash(blockNum) | |
| 0x41 | COINBASE | 2 | block.coinbase | address of proposer of current block | |
| 0x42 | TIMESTAMP | 2 | block.timestamp | timestamp of current block | |
| 0x43 | NUMBER | 2 | block.number | number of current block | |
| 0x44 | DIFFICULTY | 2 | 0 | always 0 on TVM (no Proof-of-Work) | |
| 0x45 | GASLIMIT | 2 | 0 | always 0 on TVM | |
| 0x46 | CHAINID | 2 | chain_id | push current chain id onto stack | |
| 0x47 | SELFBALANCE | 5 | address(this).balance | balance of executing contract, in sun | |
| 0x48 | BASEFEE | 2 | block.basefee | returns energyPrice on TVM (no EIP-1559) | |
| 0x49 | BLOBHASH | 3 | index | versionedHash | Cancun. Always returns 0 on TVM (no blob transactions) |
| 0x4A | BLOBBASEFEE | 2 | block.blobbasefee | Cancun. Always returns 0 on TVM (no blob transactions) |
Stack, memory, storage, control flow — 0x50 … 0x5F
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Mem / Storage | Notes |
|---|---|---|---|---|---|---|
| 0x50 | POP | 2 | a | remove item from top of stack and discard | ||
| 0x51 | MLOAD | A5 | ost | mem[ost:ost+32] | read word from memory at offset ost | |
| 0x52 | MSTORE | A5 | ost, val | mem[ost:ost+32] := val | write a word to memory | |
| 0x53 | MSTORE8 | A6 | ost, val | mem[ost] := val & 0xFF | write a single byte to memory | |
| 0x54 | SLOAD | 50 | key | storage[key] | read word from storage | |
| 0x55 | SSTORE | A7 | key, val | storage[key] := val | write word to storage | |
| 0x56 | JUMP | 8 | dst | $pc := dst (must land on a JUMPDEST) | ||
| 0x57 | JUMPI | 10 | dst, cond | $pc := cond ? dst : $pc + 1 | ||
| 0x58 | PC | 2 | $pc | program counter | ||
| 0x59 | MSIZE | 2 | len(mem) | size of memory in current execution context, in bytes | ||
| 0x5A | GAS | 2 | gasRemaining | the amount of available Energy | ||
| 0x5B | JUMPDEST | 1 | valid jump destination marker; $pc := $pc + 1 | |||
| 0x5C | TLOAD | 100 | key | transient[key] | Cancun. read word from transient storage (cleared at end of tx) | |
| 0x5D | TSTORE | 100 | key, val | transient[key] := val | Cancun. write word to transient storage (cleared at end of tx) | |
| 0x5E | MCOPY | A3 | dstOst, ost, len | mem[dstOst:dstOst+len] := mem[ost:ost+len] | Cancun. memory-to-memory copy | |
| 0x5F | PUSH0 | 2 | 0 | Shanghai. push constant 0 onto stack |
Stack manipulation — PUSH, DUP, SWAP
These families share opcode-and-Energy structure — only the size of the operand or the stack position differs.
| Opcode range | Name | Energy | Stack effect | Notes |
|---|---|---|---|---|
| 0x60 – 0x7F | PUSH1 … PUSH32 | 3 | push an N-byte immediate value onto stack | N = (opcode − 0x5F). PUSH1 pushes 1 byte; PUSH32 32. |
| 0x80 – 0x8F | DUP1 … DUP16 | 3 | clone the N-th item from the top of the stack | N = (opcode − 0x7F). DUP1 clones the top of stack. |
| 0x90 – 0x9F | SWAP1 … SWAP16 | 3 | swap the top of stack with the N-th item below | N = (opcode − 0x8F). SWAP1 swaps top with the next item. |
Logging — 0xA0 … 0xA4
| Opcode | Name | Energy | Initial Stack | Notes |
|---|---|---|---|---|
| 0xA0 | LOG0 | A8 | ost, len | LOG0(memory[ost:ost+len]) |
| 0xA1 | LOG1 | A8 | ost, len, topic0 | LOG1(memory[ost:ost+len], topic0) |
| 0xA2 | LOG2 | A8 | ost, len, topic0, topic1 | LOG2(memory[ost:ost+len], topic0, topic1) |
| 0xA3 | LOG3 | A8 | ost, len, topic0, topic1, topic2 | LOG3(memory[ost:ost+len], topic0, topic1, topic2) |
| 0xA4 | LOG4 | A8 | ost, len, topic0, topic1, topic2, topic3 | LOG4(memory[ost:ost+len], topic0, topic1, topic2, topic3) |
TRON-specific — TRC-10 (0xD0 … 0xD3)
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0xD0 | CALLTOKEN | A9 | callEnergy, addr, val, tokenId, argOst, argLen, retOst, retLen | success | call addr with val and TRC-10 tokenId attached |
| 0xD1 | TOKENBALANCE | 20 | tokenId, address | balance | balance of address on tokenId TRC-10 token |
| 0xD2 | CALLTOKENVALUE | 2 | value | TRC-10 token value attached to current call | |
| 0xD3 | CALLTOKENID | 2 | tokenId | TRC-10 token ID attached to current call |
TRON-specific — Introspection (0xD4)
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0xD4 | ISCONTRACT | 20 | address | isContract | true if address is a contract |
TRON-specific — Stake 1.0 (legacy, 0xD5 … 0xD7)
Legacy. New code should use the Stake 2.0 opcodes below.
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0xD5 | FREEZE | A10 | resourceType, frozenBalance, receiverAddress | success | freeze frozenBalance of resourceType to receiverAddress |
| 0xD6 | UNFREEZE | 20000 | resourceType, targetAddress | success | unfreeze all resourceType on targetAddress |
| 0xD7 | FREEZEEXPIRETIME | 50 | resourceType, targetAddress | expireTime | expiration of resourceType on targetAddress |
TRON-specific — Voting (0xD8 … 0xD9)
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0xD8 | VOTEWITNESS | A11 | amountArrayLength, amountArrayOffset, witnessArrayLength, witnessArrayOffset | success | vote amounts in amountArray for SRs in witnessArray |
| 0xD9 | WITHDRAWREWARD | 20000 | withdrawReward | claim accumulated voting rewards to the contract balance |
TRON-specific — Stake 2.0 (0xDA … 0xDF)
Current resource-staking and delegation opcodes (TIP-467).
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Notes |
|---|---|---|---|---|---|
| 0xDA | FREEZEBALANCEV2 | 10000 | resourceType, frozenBalance | success | stake TRX to acquire resources |
| 0xDB | UNFREEZEBALANCEV2 | 10000 | resourceType, unfreezeBalance | success | begin unstaking — starts the unstake delay (14 days on Mainnet) |
| 0xDC | CANCELALLUNFREEZEV2 | 10000 | success | cancel all pending unstake operations | |
| 0xDD | WITHDRAWEXPIREUNFREEZE | 10000 | success | withdraw TRX whose unstake waiting period has elapsed | |
| 0xDE | DELEGATERESOURCE | 10000 | resourceType, delegateBalance, receiverAddress | success | delegate resource to another address |
| 0xDF | UNDELEGATERESOURCE | 10000 | resourceType, unDelegateBalance, receiverAddress | success | cancel resource delegation |
Calls and contract creation — 0xF0 … 0xFF
| Opcode | Name | Energy | Initial Stack | Resulting Stack | Mem / Storage | Notes |
|---|---|---|---|---|---|---|
| 0xF0 | CREATE | A12 | val, ost, len | addr | create contract | |
| 0xF1 | CALL | A9 | callEnergy, addr, val, argOst, argLen, retOst, retLen | success | mem[retOst:retOst+retLen] := returndata | call addr with val and arguments |
| 0xF2 | CALLCODE | A13 | gas, addr, val, argOst, argLen, retOst, retLen | success | mem[retOst:retOst+retLen] := returndata | like DELEGATECALL but does not propagate original msg.sender and msg.value |
| 0xF3 | RETURN | A14 | ost, len | halt execution and return mem[ost:ost+len] | ||
| 0xF4 | DELEGATECALL | A15 | callEnergy, addr, argOst, argLen, retOst, retLen | success | mem[retOst:retOst+retLen] := returndata | delegate call — executes in caller's context (storage, balance, msg.sender) |
| 0xF5 | CREATE2 | A16 | val, ost, len, salt | addr | addr = keccak256(0x41 ++ address(this) ++ salt ++ keccak256(mem[ost:ost+len]))[12:] | |
| 0xFA | STATICCALL | A15 | gas, addr, argOst, argLen, retOst, retLen | success | mem[retOst:retOst+retLen] := returndata | call without permitting state changes |
| 0xFD | REVERT | A14 | ost, len | revert(mem[ost:ost+len]) — rolls back state, refunds remaining Energy | ||
| 0xFF | SUICIDE | A17 | addr | halt execution; behavior depends on ALLOW_TVM_SELFDESTRUCT_RESTRICTION (active on Mainnet) — for contracts not created in the current transaction, only transfers balance and assets instead of deleting the account. Also exposed as SELFDESTRUCT in Solidity. See TVM vs EVM — SELFDESTRUCT. |
Osaka precompiled contract
GreatVoyage-v4.8.2 implements P256VERIFY (TIP-7951) for secp256r1/P-256 signature verification. The precompiled contract is located at 0x0000000000000000000000000000000000000100 and has a fixed cost of 6900 Energy.
The input must be exactly 160 bytes, formed by concatenating five 32-byte fields: hash || r || s || qx || qy. A valid signature returns a 32-byte value of 1. An invalid input length, signature, or public key returns empty bytes without reverting the call.
Like CLZ, P256VERIFY is controlled by the Osaka governance proposal. Before deploying contracts that depend on it, confirm that the proposal has been activated on the target network instead of relying on the node version alone.
Appendix: Energy cost calculations
A01: memNeed(offset, size)
Calculate the memory needed.
offset— the start position in memory.size— data length to handle, beginning atoffset.- Returns
offset + size.
A02: calcMemEnergy(oldMemorySize, memorySize, copySize)
Calculate the memory Energy cost.
oldMemorySize— old memory size.memorySize— new memory size after the operation.copySize— for copy operations, the data size to copy.memWords = (memorySize + 31) / 32oldMemWords = oldMemorySize / 32energyCost = 3 * memWords + memWords² / 512 − (3 * oldMemWords + oldMemWords² / 512) + 3 * ((copySize + 31) / 32)
A03: penalty(energyCost)
If the Dynamic Energy Model is allowed, return the penalty.
DYNAMIC_ENERGY_FACTOR = 10000Factor— contract context factor.penalty = energyCost * factor / DYNAMIC_ENERGY_FACTOR − energyCost- return
penalty > 0 ? penalty : 0
A04: isDeadAccount(address)
Returns true if the address is not a contract, otherwise false.
A05: sizeInWords(len)
Returns len == 0 ? 0 : (len − 1) / 32 + 1.
A1: EXP Energy cost
byte_len_exponent— number of bytes in the exponent (bin the stack representation).energy_cost = 10 + 10 * byte_len_exponent
A2: SHA3 Energy cost
energy_cost = 30 + calcMemEnergy(oldMemSize, memNeed(ost, len), 0)
A3: COPY operations
Applies to CALLDATACOPY, CODECOPY, RETURNDATACOPY, and MCOPY (not EXTCODECOPY).
energy_cost = calcMemEnergy(oldMemSize, memNeed(dstOst, len), len)
A4: EXTCODECOPY
energy_cost = 20 + calcMemEnergy(oldMemSize, memNeed(dstOst, len), len)
A5: MLOAD, MSTORE
energy_cost = calcMemEnergy(oldMemSize, memNeed(ost, 32), 0)
A6: MSTORE8
energy_cost = calcMemEnergy(oldMemSize, memNeed(ost, 1), 0)
A7: SSTORE
oldValue— old value ofstorage[key].energy_cost = (oldValue == null && val != 0) ? 20000 : 5000
A8: LOG0 to LOG4
nTopics— number of topics.energyCost = 375 + 375 * nTopics + 8 * len + calcMemEnergy(oldMemSize, memNeed(ost, len), 0)
A9: CALLTOKEN, CALL
energyCost_1 = 40
+ (val != 0 ? 9000 : 0)
+ (val != 0 && isDeadAccount(addr) ? 25000 : 0)
+ calcMemEnergy(oldMemSize,
max(memNeed(orgOst, orgLen), memNeed(retOst, retLen)),
0)
energyCost_2 = energyCost_1 + penalty(energyCost_1)
energyLimitLeft = energyLimit - energyCost_2
energyCost = energyCost_2 + min(callEnergy, energyLimitLeft)A10: FREEZE
energyCost = 20000 + (isDeadAccount(receiverAddress) ? 25000 : 0)
A11: VOTEWITNESS
energyCost = 30000 + (ALLOW_ENERGY_ADJUSTMENT
? calcMemEnergy(oldMemSize,
max(memNeed(amountArrayOffset, amountArrayLength),
memNeed(witnessArrayOffset, witnessArrayLength)),
0)
: calcMemEnergy(oldMemSize,
max(memNeed(amountArrayOffset, amountArrayLength * 32 + 32),
memNeed(witnessArrayOffset, witnessArrayLength * 32 + 32)),
0))A12: CREATE
energyCost = 32000 + calcMemEnergy(oldMemSize, memNeed(ost, len), 0)
A13: CALLCODE
energyCost_1 = 40
+ (val != 0 ? 9000 : 0)
+ calcMemEnergy(oldMemSize,
max(memNeed(orgOst, orgLen), memNeed(retOst, retLen)),
0)
energyCost_2 = energyCost_1 + penalty(energyCost_1)
energyLimitLeft = energyLimit - energyCost_2
energyCost = energyCost_2 + min(callEnergy, energyLimitLeft)A14: RETURN, REVERT
energy_cost = calcMemEnergy(oldMemSize, memNeed(ost, len), 0)
A15: DELEGATECALL, STATICCALL
energyCost_1 = 40 + calcMemEnergy(oldMemSize,
max(memNeed(orgOst, orgLen), memNeed(retOst, retLen)),
0)
energyCost_2 = energyCost_1 + penalty(energyCost_1)
energyLimitLeft = energyLimit - energyCost_2
energyCost = energyCost_2 + min(callEnergy, energyLimitLeft)A16: CREATE2
energyCost = 32000 + calcMemEnergy(oldMemSize, memNeed(ost, len), 0) + 6 * sizeInWords(len)
A17: SUICIDE
Cost depends on which proposal gates are active:
| Gate | Live obtainer account | Dead obtainer account |
|---|---|---|
| Base (no gates) | 0 | 0 |
ALLOW_ENERGY_ADJUSTMENT only | 0 | 25,000 (NEW_ACCT_CALL) |
ALLOW_TVM_SELFDESTRUCT_RESTRICTION (active on Mainnet) | 5,000 | 30,000 (5,000 + 25,000) |
See TVM vs EVM — SELFDESTRUCT restricted behavior for the semantic shift that accompanies the cost change.
Related resources
- TVM vs EVM — high-level summary of differences from the EVM
- TVM — the TRON Virtual Machine
- Solidity on TRON — Solidity-level access to TRON-specific opcodes
- Stake 2.0 Solidity SDK reference — high-level wrappers for the staking and voting opcodes
- Smart contracts introduction — what smart contracts are
- java-tron
Op.java— opcode definitions in source
Updated 5 days ago