#85 [Error Handling] connectAccount leaves wallet in partially-conn… - #117
Merged
Conversation
… partially-connected state on network failure FIXED
cybermax4200
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Problem:
useStellarWallet.connectAccount()calledconnect(key, type)BEFOREstellar.getBalance(key). If the balance fetch failed (network outage), the error wascaught and displayed, but the store had already flipped
isConnected: true— and thewallet store is persisted to MMKV, so the phantom connected state survived app restarts.
RootNavigatorgates the app onisConnected, so users landed in the main app with anull/zero balance and no indication their wallet wasn't actually usable.
Two compounding defects made it worse:
getBalance/getTokenBalanceswallowed ALL errors and returned'0', making anetwork outage indistinguishable from a legitimately unfunded (zero-balance) account.
saveInAppSecret()persisted the secret key to the vault before verification,orphaning secrets on failed connects.
Fix (fetch-then-connect + rollback, applied consistently to all wallet paths):
connectAccountnow verifies balances FIRST:beginConnect()→ fetch XLM + ECO + USDC →only on full success, save secret +
connect()with the verified balances.connect()in the store now accepts optionalinitialBalances, so the connected stateand balances land atomically — a connected wallet can never render an unknown balance.
connectFailed(message)rolls every walletfield back, records
connectError, and rethrows so the calling path surfaces the error.The user stays on Onboarding and sees a meaningful message instead of a zero-balance
main screen.
status: 'disconnected' | 'connecting' | 'connected'(intermediate"connecting" state until balances are verified) +
connectError, both excluded frompersistence via
partialize(a crash mid-connect can't restore a stuck state).getBalance/getTokenBalance:NotFoundError(unfunded account) still resolves'0'— a zero balance remains a valid connected state; infrastructure/network errors now
reject so the connect flow can detect them.
refreshBalance/refreshEcoBalance/refreshUsdcBalance) madereject-safe: a later network blip keeps the last known balance instead of rejecting
into fire-and-forget call sites.
OnboardingScreenalso surfaces the store-levelconnectError(survives hookremounts); self-clearing on retry/success/disconnect.
Closes #NN
Type of Change
Non-breaking:
connect(publicKey, walletType?)keeps its existing signature (the newinitialBalancesparam is optional); all 335 pre-existing tests pass unmodified.How Has This Been Tested?
26 new unit tests across two new suites (all passing):
src/__tests__/useStellarWallet.test.tsx(19):'0'balance (zero is NOT treated as failure)"connecting"state while the fetch is in flightconnectError+ hook error, verified forALL FOUR paths: in-app create, Freighter, Lobstr, import
src/__tests__/stellarBalance.test.ts(7):'0';network failure → rejects (for both
getBalanceandgetTokenBalance)Full suite: 42 suites / 361 tests passing (baseline 40 / 335). Manual on-device
testing not performed (no emulator available in this environment) — reviewers are
encouraged to smoke-test the offline-connect scenario on a device.
Checklist
src/: 0 errors; repo-wide lint identical to pre-fix baseline)Screenshots
N/A — no visual redesign. The only UI-visible change: on connect failure the user now
remains on the Onboarding screen and sees the error text (e.g. "Network request failed")
instead of being dropped into the main app with a zero/null balance.
Additional Context
Files changed (4 modified, 2 created; +215/−36):
src/hooks/useStellarWallet.tsconnectAccountrewritten: fetch-then-connect, rollback, atomic connect; reject-safe refreshers; secret saved only after verificationsrc/store/walletStore.tsstatus,connectError,beginConnect(),connectFailed();connect()accepts verifiedinitialBalances;partializekeeps transient fields out of MMKVsrc/services/stellar.tsgetBalance/getTokenBalancedistinguish unfunded accounts ('0') from network failures (reject)src/screens/OnboardingScreen.tsxconnectErrorsrc/__tests__/useStellarWallet.test.tsxsrc/__tests__/stellarBalance.test.tsDesign choice: implemented the issue's "Option A" (move
connect()after the balancefetch succeeds, with rollback) rather than "Option B" (error screen in RootNavigator),
because it satisfies the acceptance criteria at the root — a wallet that can't be verified
is simply never marked connected, so
RootNavigatorneeded zero changes. The store-levelconnectErrorfrom Option B is still recorded and surfaced on Onboarding for robustness.Out of scope (per issue): offline wallet state, cached balance.
Out of scope note: wallets already persisted in a half-connected state by older builds are
not migrated — the fix prevents new bad state.
Closes #85