Skip to main content
Every state-changing blockchain operation in SocketFi goes through requestTransaction(), which launches a hosted approval screen where your user reviews and signs the action with their passkey. This example builds a complete token transfer flow — from a validated form through the full idle → loading → awaiting approval → confirmed / failed lifecycle — and shows you how to structure a service layer so your components stay clean.

Project structure

Transaction status lifecycle

Every transaction in your UI should move through these states explicitly. Keeping them in a single discriminated union makes rendering straightforward and prevents impossible states.

Step-by-step files

Full transaction lifecycle

When you call requestTransaction(), SocketFi automatically opens the hosted approval experience. Your user sees the contract address, method name, arguments, and fees before signing with their passkey.
If the user declines or an error occurs:

Error handling reference

The user closed the approval screen or tapped reject. This is normal user behaviour — don’t log it as an application error. Show a friendly message and allow retry.
The transaction was blocked by a wallet spending policy — for example, a per-transaction limit was exceeded. Explain the policy to the user and suggest an alternative amount or contact support.
The Soroban contract returned an error. Check that your contractId, method, and args are correct and that the contract’s state allows the operation (e.g. sufficient balance).
Transient connectivity issues. Implement retry logic with exponential back-off and surface a “Try again” prompt to the user.

Additional contract invocation examples

Production recommendations

Validate before submitting

Always validate recipient address format and amount before calling requestTransaction(). Contract validation is a safety net, not a substitute.

Disable during flight

Keep the submit button disabled while status is loading or awaiting_approval to prevent duplicate transactions.

Invalidate cached data

After a confirmed transaction, call queryClient.invalidateQueries() on balance and activity queries so your UI reflects the new chain state.

Store transaction hashes

Persist the transactionHash in your backend with a timestamp and status. It’s essential for transaction history, support, and analytics.
Never call requestTransaction() in response to a programmatic trigger without explicit user intent (e.g. a button click). Every transaction requires an intentional user action.