This document explains the Soroban RPC interaction patterns used by Stellar Goal Vault, particularly the simulate → sign → submit → reconcile flow used by the frontend and backend when creating on-chain pledges, claims, and refunds.
Why this doc exists
- The code in
backend/src/services/sorobanRpc.tsinteracts with a Soroban JSON-RPC node. That file contains logic to verify transaction results and map Soroban RPC responses to application-level errors. This doc explains the end-to-end flow, common failure modes, and how callers should react.
High-level flow
- Simulate (frontend): the wallet (e.g., Freighter) simulates a transaction against the Soroban contract to estimate fees, check preconditions, and detect early failures.
- Sign (user wallet): the user signs the simulated transaction using their wallet.
- Submit (wallet/network): the signed transaction is submitted to the Soroban RPC/node.
- Reconcile (backend): once the frontend receives a transaction hash, the backend
verifies on-chain success via
getTransactionbefore updating local state (reconcile).
sequenceDiagram
participant FE as Frontend (simulate)
participant Wallet as Wallet (Freighter)
participant RPC as Soroban RPC
participant BE as Backend (reconcile)
FE->>Wallet: request simulation
Wallet->>RPC: simulateTransaction
RPC-->>Wallet: simulation result (costs, futex, errors)
Wallet-->>FE: simulation-ok or simulation-error
FE->>Wallet: user signs transaction
Wallet->>RPC: submitTransaction (signed)
RPC-->>Wallet: txHash
Wallet-->>FE: txHash
FE->>BE: POST /pledges/reconcile { txHash }
BE->>RPC: getTransaction (txHash)
RPC-->>BE: status: NOT_FOUND | FAILED | SUCCESS
alt SUCCESS
BE-->>FE: reconcile success (update DB)
else FAILED
BE-->>FE: reconciliation failed (do not update DB)
else NOT_FOUND
BE-->>FE: tx pending (retry later)
end
Why simulation is required before signing
- Simulation provides deterministic feedback about whether the transaction would
succeed or fail if submitted. It helps:
- Avoid unnecessary user signatures for transactions that would fail (bad inputs, insufficient balance, contract preconditions).
- Estimate fees and resource usage so the frontend can show realistic UX.
- Catch predictable execution failures (e.g., contract-level validations) without broadcasting signed payloads.
Known RPC error codes and application mapping
SOROBAN_REFUND_NOT_CONFIGURED(503): MissingCONTRACT_IDor backend refund config.- Action: Fix backend env (set
CONTRACT_ID) or prevent the refund flow on frontend.
- Action: Fix backend env (set
SOROBAN_RPC_NOT_CONFIGURED(503): MissingSOROBAN_RPC_URL.- Action: Configure RPC URL in backend env.
SOROBAN_RPC_INVALID_RESPONSE(502): RPC returned empty/malformed body forgetTransaction.- Action: Treat as transient RPC failure; surface 502 to caller and consider retry with backoff.
SOROBAN_RPC_UNAVAILABLE(502): Network error or RPC unreachable.- Action: Retry with exponential backoff; surface error to client and optionally show maintenance notice.
SOROBAN_TX_PENDING(409): RPC reportsNOT_FOUND— transaction not yet included.- Action: Retry verification after a delay (e.g., 3–10 seconds), or use an event indexer when available.
SOROBAN_TX_FAILED(400): RPC reports transaction executed but failed.- Action: Do not reconcile local DB. Surface failure reason to the user (if available) and log details.
Guidance for callers (backend and frontend)
- Frontend: always simulate before asking the user to sign. If the simulation fails, show a clear error and do not continue.
- Wallet: after submitting a signed tx, return the
txHashto the frontend as soon as possible. - Backend: when reconciling via
verifyRefundTransaction(txHash):- Validate
configwithensureSorobanRefundConfig(). - Call
getTransactionand interpret statuses as above. - For
NOT_FOUND, treat as transient and retry; forFAILED, abort reconcile and report. - For
SUCCESS, use returned ledger metadata to mark local state and append an event to history.
- Validate
Testing and verification steps (how you can validate your assignment)
- Open the repository in your dev environment.
- Confirm
backend/src/services/sorobanRpc.tscontains expanded JSDoc and inline comments.- Search for
verifyRefundTransactionandensureSorobanRefundConfigto verify they have JSDoc@throwsentries for the mapped errors.
- Search for
- Run TypeScript compile / quick-check in the repo root:
npm run install:all
npm run build- Start the backend locally and ensure it starts without configuration errors (if
CONTRACT_IDis missing set a dummy value to exercise code paths):
cd backend
npm run dev:backend-
(Optional) If you have a Soroban RPC URL and a known transaction hash, call the backend reconcile endpoint that uses
verifyRefundTransactionor call the function directly from a small test harness. -
Lint or run tests (if available):
npm run lint
npm test- Manual code review: open backend/src/services/sorobanRpc.ts and confirm:
- Every function has a JSDoc explaining purpose, params, and
@throwsfor known error conditions. - Inline comments explain the JSON-RPC body and the mapping of RPC statuses to
AppErrorcodes.
- Every function has a JSDoc explaining purpose, params, and
If all of the above are present and the project builds, your assignment is complete.
Additional notes
- For production, consider adding an event indexer that listens for on-chain events instead of polling
getTransactionrepeatedly. This improves latency and reduces RPC load. - Surface granular RPC error messages (when safe) to help users understand
FAILEDcauses — e.g., contract-level errors returned in the RPC metadata.