Transaction Lifecycle
Privacy Boost transactions don’t settle the instant you submit them. Settlement takes several seconds in the steady state and longer under congestion, so the SDK surfaces intermediate states you can drive UI and dependent actions from. This page explains what each state means and how to use preconfirmation — the intermediate state that lets you chain private actions before on-chain finality.The state machine
Every transaction carries a raw lifecyclestatus and a derived consumer-facing phase. Use phase for UI and business logic; use status only for debugging, audit, or history views.
| Status | Phase | Meaning |
|---|---|---|
pending | inFlight | Accepted by the SDK but not yet preconfirmed. |
preconfirmed (transfer) | spendable | The output note can be selected as input for the next private action. |
preconfirmed (shield, unshield) | inFlight | Progress only — no spendable output is produced. |
completed | final | Settled on-chain. Terminal success. |
failed | failed | Terminal failure — validation reject, revert, or unrecoverable reorg. |
blocked | inFlight | Held by compliance review. See complianceStatus for the verdict. |
phase is recomputed automatically whenever status or type changes — you don’t need to derive it yourself.
What preconfirmation guarantees
When a transfer reachespreconfirmed, its output note is spendable: getNotes, getBalance, and prepareTransfer will select it as input for a chained operation, and the input notes it consumed are locked from reuse.
It does not mean the transaction is on-chain. A reorg can still force a re-batch or transition the transaction to failed, in which case any chained spend against the preconfirmed output also unwinds. The practical rule: chain against spendable, but keep polling the originating request until it reaches final.
Shields and unshields also pass through preconfirmed, but neither produces a spendable output — treat their preconfirmed state as progress UI only.
Polling
The SDK polls in-flight transactions for you:pending— still in flight (pendingorpreconfirmed).resolved— newly reachedcompletedorfailedsince the previous poll.preconfirmed— newly transitioned intopreconfirmedsince the previous poll. Fires once per transaction.
useTransactions({ onPreconfirmed, onAllResolved }). See the React and TypeScript guides for usage.
Recommended UX patterns
- Show two distinct states, not one. “Submitted” (
inFlight) and “Ready to use” (transfer-onlyspendable) communicate progress meaningfully; collapsing them into “pending” hides the preconfirmation win. - Gate chained actions on
phase === 'spendable', not onstatus === 'completed'. The whole point of preconfirmation is to unblock the next action a few seconds earlier. - Mark preconfirmed transfers as not-yet-final in history. A dim row or “finalizing” badge keeps the distinction visible until polling reports
completed. - Treat shield
preconfirmedas progress only. Don’t let the user spend against a shielded amount until it reachescompleted. - Handle
blockedexplicitly viacomplianceStatusrather than treating it as a generic failure.
State transitions
phase is authoritative — UI should react to transitions rather than caching past states. Input notes consumed by a transaction become spendable again whenever it lands in failed.