Andamio Logo
Apps & Tooling/App

Transaction State Machine

How the Andamio app template tracks a transaction from button click to on-chain confirmation — fallback systems, recovery, and the full transaction type catalog.

The app template uses a two-layer state machine for every transaction. The frontend hook (useTransaction()) handles signing and submission; the gateway watcher (txWatcherStore) handles on-chain confirmation and DB sync. For the state transitions and the confirmed vs updated distinction, see:

This page covers what is specific to the app template implementation: the connection between the two machines, the fallback transport layer, page-load recovery, and the full transaction type catalog.

The two machines

Machine 1 — Frontend hook

Driven by useTransaction() in src/hooks/tx/use-transaction.ts.

Machine 2 — Gateway state machine

Five states, owned entirely by the gateway (andamio-api). txWatcherStore observes this machine via SSE — it does not run it.

txWatcherStore adds one local inference on top: if confirmed arrives but updated does not follow within 30s, the store treats the TX as stalled and calls handleTerminal(). This is a client-side derived condition — not a gateway state.

For state descriptions and the confirmed vs updated distinction see Monitoring State Changes — API Concepts and the app template README.

How the two machines connect

The handoff happens inside useTransaction() after wallet.submitTx() returns a txHash:

  1. registerTransaction() POSTs the txHash to the gateway (gateway registration)
  2. Gateway machine starts at pending
  3. An SSE stream opens — the gateway pushes state changes to the browser in real time
  4. Frontend machine enters success

From this point, the frontend machine is done. The gateway watcher runs at module level (outside React) so it survives page navigation and continues until it reaches a terminal state.

SSE and polling fallback

The gateway watcher prefers SSE (Server-Sent Events) for real-time updates. If SSE fails or goes silent, it falls back to polling, and if polling exhausts its budget, it tries a direct indexer lookup.

  • SSE — primary path; gateway streams state, state_change, and complete events
  • Polling — fallback at 6s intervals when SSE fails
  • Indexer fallback — last resort; queries a commitment endpoint directly to determine success or failure

Two transaction flows

Not all transactions behave the same after submission. There are two distinct flows based on whether the transaction requires a DB update:

Flow A — Pure on-chain transactions (requiresDBUpdate: false, requiresOnChainConfirmation: true)

Applies to: GLOBAL_GENERAL_ACCESS_TOKEN_MINT, GLOBAL_USER_ACCESS_TOKEN_CLAIM

The gateway tracks on-chain confirmation but has no DB writes to perform. The updated state is reached almost immediately after confirmed.

Flow B — DB-backed transactions (requiresDBUpdate: true)

Applies to all other 16 transaction types.

The gateway must confirm on-chain AND complete DB updates before reaching updated. This is the full lifecycle with the 30s confirmed-state timeout guard.

Recovery on page load

If gateway registration fails (network error, adblocker, proxy issue), the transaction hash is saved to localStorage via pendingTxRegistrations. On the next page load, runPendingTxRecovery() retries registration for each saved entry. Recovery requires a valid JWT, so it only runs after the user is authenticated.

Moments of Commitment

Five transaction types trigger a celebration UI on success in addition to the standard toast notification:

  • GLOBAL_GENERAL_ACCESS_TOKEN_MINT
  • INSTANCE_COURSE_CREATE
  • INSTANCE_PROJECT_CREATE
  • COURSE_STUDENT_CREDENTIAL_CLAIM
  • PROJECT_CONTRIBUTOR_CREDENTIAL_CLAIM

These are called "Moments of Commitment" in the codebase (tx-watcher-store.ts).

Transaction type catalog

All 18 types are tracked on-chain by the gateway. The meaningful distinction is whether a DB update follows confirmation.

Transaction typeRoleDB update after confirmation?
GLOBAL_GENERAL_ACCESS_TOKEN_MINTAny userNo
GLOBAL_USER_ACCESS_TOKEN_CLAIMAny user (V1 migration)No
INSTANCE_COURSE_CREATEInstance ownerYes
INSTANCE_PROJECT_CREATEInstance ownerYes
COURSE_OWNER_TEACHERS_MANAGECourse ownerYes
COURSE_TEACHER_MODULES_MANAGETeacherYes
COURSE_TEACHER_ASSIGNMENTS_ASSESSTeacherYes
COURSE_STUDENT_ASSIGNMENT_COMMITStudentYes
COURSE_STUDENT_ASSIGNMENT_UPDATEStudentYes
COURSE_STUDENT_CREDENTIAL_CLAIMStudentYes
PROJECT_OWNER_MANAGERS_MANAGEProject ownerYes
PROJECT_OWNER_BLACKLIST_MANAGEProject ownerYes
PROJECT_MANAGER_TASKS_MANAGEManagerYes
PROJECT_MANAGER_TASKS_ASSESSManagerYes
PROJECT_CONTRIBUTOR_TASK_COMMITContributorYes
PROJECT_CONTRIBUTOR_TASK_ACTIONContributorYes
PROJECT_CONTRIBUTOR_CREDENTIAL_CLAIMContributorYes
PROJECT_USER_TREASURY_ADD_FUNDSAny project userYes