Labels / Complexity: bug,dx · High — 13## Problem
store/walletStore.ts fetches balances from a hardcoded testnet endpoint regardless of the configured network:
// store/walletStore.ts
const res = await fetch(`https://horizon-testnet.stellar.org/accounts/${address}`);
The repo already has network-aware infrastructure that this bypasses: lib/stellar/config.ts defines HORIZON_URLS and getStellarConfig, and lib/env.ts validates NEXT_PUBLIC_STELLAR_NETWORK and NEXT_PUBLIC_STELLAR_HORIZON_URL.
- Mainnet wallets always show zero: with
NEXT_PUBLIC_STELLAR_NETWORK=mainnet (per .env.example), the balance lookup still hits testnet Horizon, which either returns a 404 (rendered as '0.0000000') or a testnet balance unrelated to the user's mainnet account.
- Balance never refreshes:
fetchBalance is called from connect without being awaited and never runs again, so a donation or funding event is not reflected until a full reconnect.
- Account-not-found is indistinguishable from zero: a 404 from Horizon is silently shown as a zero balance, so users cannot tell an empty account from a lookup failure.
Root cause
// store/walletStore.ts
const fetchBalance = async (address: string, set: any) => {
// hardcoded testnet URL ignores NEXT_PUBLIC_STELLAR_NETWORK / HORIZON_URLS
const res = await fetch(`https://horizon-testnet.stellar.org/accounts/${address}`);
Why this is architecturally hard
- The balance lookup must resolve the Horizon URL through
getStellarConfig() (or the env value) per network, which means the store cannot keep a literal URL and must import from lib/stellar/ without creating a client/server split.
- Account-not-found must be distinguishable from a zero balance, which requires parsing Horizon's 404 response rather than collapsing every failure into
'0.0000000'.
- The refresh lifecycle is a design decision: refresh on connect, on network change, and periodically, without fighting the fire-and-forget call in
connect or causing the global loading overlay in lib/api/interceptors.ts to flash on background polls.
- Native balance parsing must stay robust across Horizon response shapes (
asset_type === 'native'), which the current find already assumes but never tests.
Acceptance criteria
- A connected wallet's balance is fetched from the Horizon URL of the configured network, not a hardcoded URL.
- On mainnet configuration the balance reflects the mainnet account; switching
NEXT_PUBLIC_STELLAR_NETWORK changes the lookup target.
- A brand-new account shows zero with no error; a Horizon failure surfaces as an error rather than a silently wrong balance.
- Balance refreshes on reconnect and at least once after connect without user action.
- Tests cover the fetch with mocked responses: native balance, no native asset, 404, network error, and mainnet vs testnet URL selection.
Out of scope
Multi-asset balance display and trustline management; only the native balance lookup is in scope.
Getting started
- Read
store/walletStore.ts, lib/stellar/config.ts, lib/env.ts, and components/WalletDropdown.tsx for how the balance is displayed.
- Verify with
npm run type-check and npm run lint.
Good first files to read: store/walletStore.ts, lib/stellar/config.ts, lib/env.ts.
Labels / Complexity:
bug,dx· High — 13## Problemstore/walletStore.tsfetches balances from a hardcoded testnet endpoint regardless of the configured network:The repo already has network-aware infrastructure that this bypasses:
lib/stellar/config.tsdefinesHORIZON_URLSandgetStellarConfig, andlib/env.tsvalidatesNEXT_PUBLIC_STELLAR_NETWORKandNEXT_PUBLIC_STELLAR_HORIZON_URL.NEXT_PUBLIC_STELLAR_NETWORK=mainnet(per.env.example), the balance lookup still hits testnet Horizon, which either returns a 404 (rendered as'0.0000000') or a testnet balance unrelated to the user's mainnet account.fetchBalanceis called fromconnectwithout being awaited and never runs again, so a donation or funding event is not reflected until a full reconnect.Root cause
Why this is architecturally hard
getStellarConfig()(or the env value) per network, which means the store cannot keep a literal URL and must import fromlib/stellar/without creating a client/server split.'0.0000000'.connector causing the global loading overlay inlib/api/interceptors.tsto flash on background polls.asset_type === 'native'), which the currentfindalready assumes but never tests.Acceptance criteria
NEXT_PUBLIC_STELLAR_NETWORKchanges the lookup target.Out of scope
Multi-asset balance display and trustline management; only the native balance lookup is in scope.
Getting started
store/walletStore.ts,lib/stellar/config.ts,lib/env.ts, andcomponents/WalletDropdown.tsxfor how the balance is displayed.npm run type-checkandnpm run lint.Good first files to read:
store/walletStore.ts,lib/stellar/config.ts,lib/env.ts.