Skip to content

Transaction Lifecycle

Every transaction on Xian follows a defined path from creation to finalization. Understanding this lifecycle helps you debug failed transactions, optimize chi usage, and build reliable applications.

Overview

Step-by-Step

1. Transaction Creation

Using the Python SDK (xian-py), construct a transaction payload:

python
from xian_py import Wallet, Xian

wallet = Wallet()
xian = Xian("http://localhost:26657", "xian-local-1", wallet=wallet)

result = xian.send_tx(
    contract="currency",
    function="transfer",
    kwargs={"amount": 100, "to": "recipient_address"},
    chi=50000,
)

The payload contains:

  • contract -- the target contract name
  • function -- the exported function to call
  • kwargs -- keyword arguments for the function
  • chi -- maximum chi to spend
  • chain_id -- network identifier (prevents cross-chain replay)
  • nonce -- sequential counter (prevents replay on the same chain)

2. Signing

The SDK signs the transaction payload with the sender's Ed25519 private key. The signature is attached to the transaction and verified by validators.

3. Broadcasting

The signed transaction is sent to a CometBFT node via its RPC interface. Two modes:

ModeBehavior
broadcast_tx_syncWaits for CHECK_TX result, then returns
broadcast_tx_asyncReturns immediately, no validation feedback

4-7. CHECK_TX (Mempool Validation)

Before entering the mempool, the transaction passes through validation:

  1. Signature verification -- the Ed25519 signature must be valid for the payload and sender's public key
  2. Chain ID check -- the transaction's chain_id must match the network
  3. Nonce check -- the nonce must be the next expected value for this sender
  4. Fee-policy check -- in paid_metered mode, the sender must have enough XIAN to cover the requested chi limit; in free_metered mode, the submitted chi budget is checked against configured transaction and block caps instead

If any check fails, the transaction is rejected and never enters the mempool. Local pending-nonce reservations only happen after the transaction passes the static checks above, so bad signatures, wrong chain_id values, and malformed wire payloads do not temporarily block the sender's next valid nonce.

8. Mempool

Valid transactions sit in the mempool until a block proposer includes them in a block proposal.

9. Consensus

CometBFT runs Byzantine Fault Tolerant consensus. Once 2/3+ of validators agree on the block contents and order, the block is finalized.

The block header also fixes the timestamp that contracts will see as now during execution of that block.

10-14. FINALIZE_BLOCK (Execution)

Canonical block semantics are sequential: every validator must end up with the same result as if the transactions were executed in block order, one after the other.

Nodes may optionally speculate in parallel before acceptance, but any accepted speculative result still has to pass serial-equivalence checks against earlier transactions in the block. Conflicting speculative results are re-executed serially.

See Parallel Block Execution for the mechanism.

Per transaction, the execution flow is:

  1. Sandbox setup -- the contract runtime initializes with the sender's context (ctx.caller, ctx.signer)
  2. Function dispatch -- the specified @export function is called with the provided kwargs
  3. Metering -- the VM runtime charges compute units through the fixed gas schedule
  4. Storage operations -- reads and writes are charged per byte according to the fixed execution policy; VM-native execution charges storage through the VM host-operation schedule
  5. Block time injection -- all transactions in the block observe the same consensus timestamp as now
  6. Completion or failure:
    • Success -- state changes are buffered for commit, chi consumed are recorded
    • Failure (assertion, out of chi, runtime error) -- state changes are rolled back, chi are recorded, and paid networks charge the matching execution fee

15-17. Commit

After all transactions in the block are executed:

  1. All successful state changes are assembled in pending state
  2. The app_hash cache is updated as a Merkle root over consensus state
  3. The app_hash is returned to CometBFT for inclusion in the next block header
  4. Commit persists the pending state to LMDB in a single atomic batch

18-20. Post-Finalization

  • Event indexing -- CometBFT indexes events emitted during execution, making them searchable via /tx_search
  • WebSocket notifications -- subscribers receive real-time event data
  • RPC availability -- the block, transactions, and results are queryable via the CometBFT RPC

Transaction Result

After finalization, querying a transaction returns:

FieldDescription
hashTransaction hash
heightBlock height
status0 (success) or 1 (failure)
chi_usedActual chi consumed
resultReturn value from the contract function
stateState changes made (key-value pairs)
eventsEvents emitted by the contract

Failure Modes

FailureWhenState ChangesFee Charged
CHECK_TX rejectionBefore mempoolNoneNone
Assertion errorDuring executionRolled backPaid networks charge consumed chi; 0-fee networks charge 0
Out of chiDuring executionRolled backPaid networks charge the bounded failed-execution cost; 0-fee networks charge 0
Runtime errorDuring executionRolled backPaid networks charge consumed chi; 0-fee networks charge 0