ZooBC wiki
Transactions

The transaction envelope

You are looking at an old version of this page: revision #89 by zoobc.how, 2026-10-26 20:39 UTC. See the current version

Every ZooBC transaction, whatever its type, is one byte string with the same layout, called the envelope. The type's own data sits in a single field, the body; everything else (who sends, who receives, the fee, the time, an optional message) is shared by all 73 types. Knowing the envelope is enough to read, price and check any transaction.

The fields in order

The envelope drawn as a strip of fields in order, from the type to the signature, with a gas limit and a chain id crossed out above it
The envelope drawn as a strip of fields in order, from the type to the signature, with a gas limit and a chain id crossed out above it
FieldSizeNotes
type4 bytes, little-endianthe [[transactionstransaction type]] number
version1 byte1, 2 or 3
timestamp8 bytesUnix seconds
senderan address
recipientan addressor the 4-byte empty marker 02 00 00 00
fee8 bytesin atomic units
survival8 bytesversions 2 and 3
expiry4 bytesversion 3
body4-byte length, then the bodyat most 65,536 bytes
escrow partvariableor the 4-byte empty marker
message4-byte length, then the messageoptional
signaturevariablealways last

An address is a 4-byte type number followed by a payload: 36 bytes in all for a native ZBC_ account, 24 for an Ethereum account (see accounts and addresses). The smallest well-formed unsigned envelope is 29 bytes. Every amount is a whole number of atomic units, and 1 ZBC is 100,000,000 of them.

What is not there

There is no gas limit, because there is no virtual machine to meter: each type is a fixed rule, and its price is known before signing. There is no chain-id field: the signature covers the chain's genesis block hash instead (see signing a transaction). And there is no nonce field: the chain keeps each sender's nonce in its own state.

Versions

  • Version 1 has no survival field.
  • Version 2 adds the survival field.
  • Version 3 also adds the expiry field, which sets how long the transaction stays valid (see expiry and timestamps).

A version-1 transaction that states a survival deposit is refused, and so is a version 1 or 2 transaction that states an expiry: their bytes have no place for them.

Survival, body, escrow part and message

  • Survival is either 0 or at least 10,000,000 atomic units (0.1 ZBC). It opens a longevity record that keeps the transaction stored after ordinary pruning, and it is never refunded (see paying to keep data (longevity)). A transaction that carries a foreign chain's signed transaction in its message may not pay one.
  • The body holds at most 65,536 bytes, laid out as its type defines.
  • The escrow part may be carried only by SendZBC and TransferToken; it turns the payment into an escrow.
  • The message holds at most 256 bytes when plain, with no zero byte; at most 320 bytes when encrypted (an encrypted message starts with "ZBE1"); and at most 4,096 bytes when it carries a foreign chain's native transaction from Ethereum, Solana or Bitcoin.

The fee field

The fee is declared in atomic units and must be at least the transaction's floor: a base action fee of 0.025 ZBC times the fee scale that the registered nodes vote on, plus a charge for the body and message bytes that every registered node keeps. Because the floor depends only on the transaction and the chain's parameters, a wallet can ask any node for it before signing, with GET /api/v1/blockchain/estimate-fee.

A fee below the floor is refused, and a block that carries one is invalid. Anything declared above the floor comes back to the payer in the same block, so overpaying by mistake costs nothing. The Empty type, which moves nothing, must still pay a fee above zero, while a few duties that registered nodes perform for the network, such as storage proofs and event attestations, must declare a fee of exactly 0. Fees explains the formula, the refund and where the fees go.

See also