Key takeaways
- Every TRON transaction declares exactly one type from a fixed protocol enum (
ContractType), and that type decides whether an account is created, whether code runs, what it costs, and which keys may sign it. - The numeric IDs are the runtime identity the node checks —
AccountCreateContractis 0,TransferAssetContractis 2. When a number drives attribution, verify it againstTron.proto. - "System contract" means two different things: every native type at the architecture level, versus the transfer-vs-call split exchanges use to read value movement.
- The signature field is a repeated field — that one design choice is what makes multi-sig possible, and every extra signature costs more Bandwidth.
A TRON transaction isn’t a generic envelope. It declares exactly one type, drawn from a fixed protocol enumeration, and that type determines everything downstream: whether the transaction creates an account, whether it runs contract code, what it costs, and which keys are allowed to authorize it. Read a transaction’s type and you’ve read the first thing that tells you what it actually did.
This walks the type taxonomy, the one transaction that exists only to create an account, the two clashing senses of “system contract,” and the anatomy that lets a single transaction carry several signatures at once.
The type taxonomy
Every transaction names its type through the protocol’s ContractType enum — the authoritative list lives in TRON’s own protocol-buffer definition, Tron.proto. There are roughly forty types, and they sort into recognizable families.
ContractType families and the numeric IDs the protocol checks.The numbers aren’t cosmetic. They’re the runtime identity the protocol checks. A transaction’s raw_data.contract field declares its ContractType, and “the specific content within the raw_data.contract field varies depending on the transaction type.” The type tells the node which handler runs and which fields to expect.
[!WARNING] Type numbers are easy to misremember, and the protocol enum is the only authority.
AccountCreateContractis type 0, not type 2 — type 2 isTransferAssetContract. When a number matters for attribution, check it againstTron.protorather than secondary documentation.
The one transaction that only creates an account
Most new accounts on TRON come into existence implicitly: a TRX or TRC-10 transfer to an address that doesn’t exist yet activates it as a side effect, the mechanism covered in Account Activation. But the protocol also has a type whose entire purpose is to create an account and do nothing else — AccountCreateContract.
Its message is about as direct as it gets: exactly three fields — the owner_address doing the creating, the account_address being brought into existence, and an account type. That’s the cleanest “who created whom” record the protocol produces — an explicit, single-purpose statement of creation, where an ordinary transfer only activates its recipient as a side effect.
Both paths cost the same 1 TRX account-creation fee, and both resolve to the question an investigator actually cares about: which externally-owned account stood this address up. The explicit type just makes the answer unambiguous.
Two senses of “system contract”
TRON’s documentation uses “system contract” in two different senses, and conflating them is the fastest way to misread the architecture.
The architecture sense. At the protocol level, “system contract” is the umbrella name for the implementation of every native transaction type. TRON’s own docs list transfers, TRC-10 transfers, staking, voting — and smart-contract creation and triggering — together: “We collectively refer to the implementation of these different transaction types as system contracts.” So even CreateSmartContract and TriggerSmartContract count, because they’re handlers built into the node.
The operational sense. When the question is what a transaction did with value, TRON’s exchange-integration guidance splits the types differently. It separates TransferContract and TransferAssetContract — labeled System Contract Type — from CreateSmartContract and TriggerSmartContract — labeled Smart Contract Type.
What matters operationally is where the value movement is legible. For a System Contract Type, the transfer is the transaction: sender, recipient, and amount are top-level fields you read straight off. For a Smart Contract Type, the transaction is a call, and the real value flow happens inside execution — in internal transfers a top-level reading never sees. Why that flow has to be decoded before you can see it is the subject of Smart Contracts and the TVM.
The two senses don’t contradict each other. One describes how the node is built; the other describes how a transaction should be read. Hold both and “system contract” stops sounding self-contradictory.
The anatomy of a transaction
Underneath the type, every transaction shares one structure: a raw_data payload, a signature array, and a result. The raw_data carries the type-specific contract, a reference to a recent block, an expiration, a timestamp, an optional fee limit, and an optional memo. This section is the frame for how each type is carried; the full byte-level treatment — how the txID is hashed, how the signature array recovers, how TAPOS and expiration bound the transaction — is the subject of the field-book chapter Anatomy of a Signed Transaction.
Transaction structure and the repeated signature field that makes multi-sig possible.The reference block is an anti-replay binding. The ref_block_bytes and ref_block_hash fields point at a recent block, implementing what TRON calls TAPOS: “The reference block is used in the TRON TAPOS mechanism to prevent a replay of a transaction on forks that do not include the referenced block.” A transaction is cryptographically tied to a point in chain history and can’t be lifted onto a fork that never contained the block it references.
Expiration bounds its lifetime. A transaction carries an expiration past which it can no longer be included in a block. For transactions built through the node API, that expiration is set automatically to the latest block’s timestamp plus 60 seconds — a narrow window that keeps stale transactions from lingering in the mempool.
The signature is the proof of authenticity — it “proves that the transaction could only have come from the sender and was not sent fraudulently.” And it’s where the most consequential design choice hides: the signature field is a repeated one. A single transaction can carry more than one signature.
Type meets permission
That signature array is what makes multi-signature possible, and it’s where transaction type and account permissions meet. A transaction names which permission authorizes it through a Permission_id field — “the default value is 0, indicating the owner permission.” Validity is then decided by quorum: “the corresponding operation is allowed only when the sum of the weights of the participating signatures exceeds the threshold.” Several parties co-sign one transaction, and it executes only when their accumulated weight clears the bar.
The coupling runs deeper than authorization counts. A permission’s scope is defined over transaction types: each permission carries an operations bitmask in which a bit corresponds to a ContractType ID. A key can be authorized to sign TransferContract transactions but not AccountPermissionUpdateContract ones — the type of a transaction is checked against the bit for that type in the authorizing permission. The full permission model — keys, weights, thresholds, and what rotating them signals — is the subject of The TRON Account Model; here the point is narrower: the transaction is the carrier, naming its permission and bearing the signatures that satisfy it.
One more consequence falls out of the structure. Because Bandwidth is measured by a transaction’s byte size — “the raw_data of the transaction, the transaction signature, and the transaction result” — a transaction with more signatures is physically larger and costs more Bandwidth. Every co-signer you add shows up in the byte count.
Sources
- tronprotocol/protocol —
core/Tron.proto— the authoritativeContractTypeenumeration and theTransaction/raw_data/Contractmessage structure (Permission_id, the repeatedsignature). - tronprotocol/protocol —
core/contract/account_contract.proto— theAccountCreateContractmessage and its three fields. - TRON Developer Hub — Transaction Types — the per-type contract payloads and the shared transaction structure.
- java-tron documentation — System Contracts — the protocol-architecture sense in which all native transaction types are “system contracts.”
- tronprotocol/documentation — TRC10/TRX transfer for exchanges — the operational split between System Contract Type and Smart Contract Type.
- TRON Developer Hub — Transaction — TAPOS and the reference block, the automatic expiration default (the node’s latest-block timestamp plus 60 seconds, a node-config interval capped at 24 hours), the signature’s role, and Bandwidth as the byte size of raw_data plus signature plus result.
- TRON Developer Hub — Multi-Signature —
Permission_id, the weight-threshold quorum rule, and the operations bitmask keyed by contract-type ID. - TRON Developer Hub — Account — the 1 TRX account-creation fee, charged identically whether activation happens implicitly (a transfer to a non-existent address) or via an explicit
AccountCreateContract.