Animica L2 — Transaction Lifecycle
The explicit state machine every L2 transaction moves through, from submission to L1 settlement, and what each status does and does not guarantee about finality.
4 min read 839 words
View docs/l2/TRANSACTION_LIFECYCLE.md on GitHub
Source: docs/l2/TRANSACTION_LIFECYCLE.md — this page mirrors the repository documentation.
Every L2 transaction moves through an explicit state machine
(l2/constants.py::TxStatus). The design rule is honesty about finality:
sequencer acceptance is never presented as L1 finality.
The nine states
| # | State | Meaning | Set where |
|---|---|---|---|
| 1 | RECEIVED | Raw bytes arrived at the sequencer; decoded, not yet judged | Sequencer.submit |
| 2 | VALIDATED | Passed cheap structural checks (size limits, chain id, scheme) and dedupe | admission pipeline |
| 3 | SOFT_CONFIRMED | Signature verified (ML-DSA-65) + nonce/balance admission passed; ordered into the open batch. The sequencer promises inclusion | admission pipeline |
| 4 | BATCHED | The batch containing the tx closed and executed; the tx has a receipt and is inside a committed batch with a header + DA blob | Sequencer.tick → _seal |
| 5 | PROVEN | The batch’s proof was generated by the active settlement backend (l2/proof.py) | seal pipeline |
| 6 | L1_SUBMITTED | The batch commitment (state root + DA) has been submitted to Animica L1 as an anchoring tx to the bridge address | settlement submitter |
| 7 | L1_FINALIZED | The anchoring L1 tx is L1_FINALITY_DEPTH (64) blocks deep. This is real finality: withdrawals in this batch become claimable on L1 | L1 tracking |
| 8 | FAILED | Rejected before ordering (bad signature, bad nonce, insufficient balance, queue full, expired, oversized…). Never entered a batch; TxRecord.reason says why | admission pipeline |
| 9 | REVERTED | Admitted, executed inside a batch, but the authoritative re-validation at execution time failed (e.g. balance raced away). The tx changed nothing but is part of the batch record with a reverted receipt | executor |
Terminal states: L1_FINALIZED, FAILED, REVERTED.
Notes:
- Admission (step 3) is an optimization, never trusted: the executor
re-validates nonce/balance/expiry authoritatively at execution time. That is
why
REVERTEDexists as distinct fromFAILED. - In the all-in-one dev node,
SOFT_CONFIRMED → BATCHED → PROVENcan happen within onetick(); a production deployment separates proving and settlement onto their own services behind the same interfaces.
Three grades of confirmation
| Grade | State(s) | Who vouches | Appropriate for |
|---|---|---|---|
| Soft | SOFT_CONFIRMED | The designated sequencer alone (a promise) | Low-value UX: show “paid” optimistically, small-amount micropayments between parties who already accept sequencer trust |
| Proven | BATCHED + PROVEN | Deterministic re-execution: anyone with the DA blob + prior state can verify the transition (l2_verifyBatch) | Values where you want the tx to be independently checkable but can tolerate the anchoring not yet being L1-final |
| L1-finalized | L1_FINALIZED | Animica L1 consensus (anchoring tx 64 blocks deep) | Withdrawals, exchange credits, anything irreversible |
Worked user flow (the README example)
Alice deposits 10,000 ANM to L2, pays Bob 25 ANM, and Bob exits to L1. All amounts on the wire are integer nanos (1 ANM = 10⁹ nanos).
1. DEPOSIT Alice sends 10,000 ANM on L1 to the canonical bridge address,
memo magic ANML2D1 naming her L2 account.
└─ bridge observes it: OBSERVED → CONFIRMED (12 blocks)
→ FINALIZED (64 blocks). Not credited before that —
an L1 reorg must never mint L2 ANM.
2. CREDIT Once FINALIZED, a protocol-minted DEPOSIT_CLAIM credits
Alice's L2 account 10,000 ANM (fee = 0 for deposit claims).
The bridge marks the deposit credited; locked_on_l1 and
credited_total move in lockstep.
3. SEND Alice signs a TRANSFER of 25 ANM (25_000_000_000 nanos) to
Bob with her ML-DSA-65 key and submits it
(l2_sendRawTransaction). → RECEIVED → VALIDATED
4. SOFT-CONFIRM Signature + nonce/balance admission pass in milliseconds.
→ SOFT_CONFIRMED
Bob's wallet can show the incoming 25 ANM as "pending".
5. BATCH The open batch closes (tx count / bytes / age — default
250 ms max age). Deterministic execution debits Alice
25 ANM + fee, credits Bob 25 ANM, fee → L2 treasury.
→ BATCHED
6. ROOT The batch header commits prev_state_root → new_state_root
(plus transactions/receipts/escrow/data roots).
7. PROOF The VALIDITY backend produces the proof (DA blob + public
inputs); anyone can re-derive the new root from it.
→ PROVEN
8. L1 SETTLE The commitment is submitted to L1 (memo ANML2C1).
→ L1_SUBMITTED
64 L1 blocks later: → L1_FINALIZED
9. WITHDRAW Bob signs a WITHDRAW of his 25 ANM naming his L1 address.
It burns on L2 (state BURNED, unique nullifier =
sha3_256(withdraw txid)). When its batch reaches
L1_FINALIZED the withdrawal becomes CLAIMABLE; Bob's L1
claim spends the nullifier exactly once and the bridge
releases 25 ANM of locked L1 funds. → CLAIMED
At every step the conservation invariant holds:
balances + open escrows + unclaimed burns == credited_total − claimed_on_l1,
and withdrawable value never exceeds ANM locked on L1.
Querying status
l2_getTransaction(txid)→{status, batchNumber, reason, receiptStatus, receivedMs}l2_getReceipt(txid)→ execution receipt onceBATCHEDl2_getBatch(n)/l2_getBatchData(n)→ header / DA blobl2_getProofStatus(n),l2_verifyBatch(n)→ proof + independent re-verification- Withdrawals:
l2_getWithdrawalProof(nullifier); deposits:l2_getDeposit(depositId)
This page mirrors a file in the animicaorg/all repository. If the repository and this page ever disagree, the repository is authoritative. For long-form explainers written for newcomers, see Learn.