Repository navigation
Payments 4/8: Real transaction status tracking and confirmation lifecycle #141
Description
Problem / Context
app/src/app/deposit/page.tsx currently fakes transaction confirmation with a timer:
function handleConfirm() {
setStep(4);
setTxStatus('pending');
// sdk.deposit({ tier: tier.label, amount }) would go here
setTimeout(() => setTxStatus('confirmed'), 2000);
}(lines 48-53) — there is no real polling of chain state anywhere. app/src/hooks/usePosition.ts calls callRpc('getLedgerEntries', ...) but then discards the result and always returns hardcoded zeros (lines 51-64: "Parse XDR result once SDK is available; return zeros until then."). app/src/services/rpc.ts implements exactly one method, getLatestLedger — nothing for sendTransaction, getTransaction, or simulateTransaction. There is currently no defined boundary anywhere between "the app believes a payment happened" and "the blockchain has actually confirmed it."
Objective
Build a real transaction-lifecycle state machine (submitted -> pending -> included -> finalized, or failed at any stage) driven by actual Soroban RPC responses, and make the app report true on-chain position state instead of a fixed zero.
Technical approach
- Extend the RPC layer (
app/src/services/rpc.ts, or move into the SDK from Payments 2/8: Implement real Soroban transaction pipeline in the TypeScript SDK #139 so both share it) withsendTransactionandgetTransaction; pollgetTransactionby hash with backoff until status resolves toSUCCESS,FAILED, or remainsNOT_FOUNDpast a reasonable propagation window. - Define one canonical status enum shared between the SDK and the app (e.g.
PaymentStatus: 'building' | 'simulating' | 'awaiting_signature' | 'submitted' | 'pending' | 'confirmed' | 'failed' | 'expired'), replacing the app's local ad hocTxStatus = 'pending' | 'confirmed' | 'failed'(deposit/page.tsxline 16). - Rewrite
usePositionto decode the real ledger-entry/contract-data response, or (preferably) callVaultRouter.position()viasimulateTransaction, so the dashboard reflects true on-chain principal/shares/lockUntilrather than a stub. - Wire the deposit page's step-4 view to this real status stream instead of the
setTimeoutmock. - Audit
app/src/hooks/useLastHarvest.tsanduseHarvestHistory.tsfor the same stubbed-data pattern and align them if they share it. - Decide and document a confirmation-depth/finality policy: how many ledgers past inclusion should the UI treat as "safe," versus merely "included."
Detailed scope
app/src/services/rpc.ts, app/src/hooks/usePosition.ts, app/src/app/deposit/page.tsx, sdks/typescript (a status-types/polling helper reusable by both SDK consumers and the app directly), and app/src/hooks/useLastHarvest.ts / useHarvestHistory.ts if they need the same treatment.
Important edge cases and failure scenarios
- A transaction can be included in a ledger while the contract call itself still reverts (Soroban distinguishes tx-level success from host-function/contract-level failure) — this must surface as a failure, not be reported as "confirmed."
getTransactionreturningNOT_FOUNDcan mean "still propagating, keep polling" or "genuinely expired/dropped" — the poller must distinguish these rather than treating everyNOT_FOUNDas terminal.- Race condition: a user submits a deposit and immediately navigates to the portfolio page before the vault's balance reflects the new principal — the UI must not render stale zero state as if the deposit failed.
- Polling/backoff must have a hard timeout that hands control to the failure-handling issue below rather than polling indefinitely.
Dependencies
Depends on #139 (real submission produces a real transaction hash to poll) and #140 (status must be keyed by the same idempotent intent identifier so the UI can recover this state correctly after a reload).
Acceptance criteria
- The deposit page shows real pending -> confirmed/failed transitions driven by actual testnet transaction results, not a timer.
usePositionreturns real principal/shares/lockUntilfor a funded testnet address, verified against direct contract-state reads.- One documented, single-source-of-truth status enum is used by both the SDK and the app — no divergent status types between them.
Definition of done
PR merged; manual verification against Soroban testnet shows a real deposit transitioning through the full status lifecycle in the UI; usePosition is unit-tested with mocked RPC responses for confirmed/failed/pending cases.
Activity
AbdulmujibOladayo commentedon Aug 19, 2026
Looked through this project and understood its current implementation, architecture, and overall stage of maturity.
grantfox-oss commentedon Aug 19, 2026
🦊 GrantFox — @AbdulmujibOladayo has been assigned to this issue as part of the Third Campaign campaign!
Next steps:
Closes #141)Good luck! Track your progress on GrantFox.
Payments 4/8: real transaction status tracking and confirmation lifec…
grantfox-oss commentedon Aug 22, 2026
🎉 This issue has been marked as completed on GrantFox as part of the Third Campaign campaign!
@AbdulmujibOladayo's PR #149 was approved and merged by @wumibals.
🏆 @AbdulmujibOladayo: You earned 40 FoxPoints for this contribution! Your current tier: Builder (1,425 total points). Track your full progress on GrantFox.
👏 Great work, @AbdulmujibOladayo! Keep contributing to LadderMine.