fix(wallet): resolve balance from the configured network's Horizon - #19
Merged
ibrahimmosouf-png merged 1 commit intoAug 23, 2026
Conversation
9 tasks
The wallet store fetched balances from a hardcoded Horizon testnet URL, so mainnet wallets always displayed a zero balance. Resolve the Horizon base URL from the configured network (NEXT_PUBLIC_STELLAR_NETWORK with a NEXT_PUBLIC_STELLAR_HORIZON_URL override) via a new resolveHorizonUrl() in lib/stellar/config.ts, and move the fetch into a pure lib/stellar/balance.ts module that distinguishes Horizon 404 (a valid zero balance for a new account) from real lookup failures, which now surface as a balanceError instead of a silently wrong zero. Balance refreshes on connect and every 15s; the duplicate hardcoded fetch in Header.tsx is removed and the wallet store owns the refresh lifecycle. Adds unit tests with mocked fetch covering native balance, no native asset, 404, network error, non-404 HTTP error, and mainnet vs testnet URL selection. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PR OrbitChainLabs#18).
Degentle12
force-pushed
the
fix/issue-8-wallet-balance-network
branch
from
August 23, 2026 16:39
0e7a676 to
5ad8e89
Compare
nasalehj
added a commit
to nasalehj/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The repository had no test infrastructure and no CI: package.json had no test script, no test files existed, and .github/workflows did not exist, so pure logic (Stellar formatting/validation/error mapping, the cache manager, the auth and UI stores, image uploads) had zero regression protection. Introduce Vitest as the runner (TS-native, zero-config unit tests, built-in jsdom for browser-API tests, and a path to React Testing Library for future component tests), add a test script, and write 76 unit tests across the seven modules the issue lists. Add a GitHub Actions workflow that runs type-check, lint, test, and build on push and pull request with no required secrets, and document npm test in the README. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PRs OrbitChainLabs#18 and OrbitChainLabs#19).
nasalehj
added a commit
to nasalehj/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The repository had no test infrastructure and no CI: package.json had no test script, no test files existed, and .github/workflows did not exist, so pure logic (Stellar formatting/validation/error mapping, the cache manager, the auth and UI stores, image uploads) had zero regression protection. Introduce Vitest as the runner (TS-native, zero-config unit tests, built-in jsdom for browser-API tests, and a path to React Testing Library for future component tests), add a test script, and write 76 unit tests across the seven modules the issue lists. Add a GitHub Actions workflow that runs type-check, lint, test, and build on push and pull request with no required secrets, and document npm test in the README. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PRs OrbitChainLabs#18 and OrbitChainLabs#19).
nasalehj
added a commit
to nasalehj/OrbitChain-Web
that referenced
this pull request
Aug 23, 2026
The repository had no test infrastructure and no CI: package.json had no test script, no test files existed, and .github/workflows did not exist, so pure logic (Stellar formatting/validation/error mapping, the cache manager, the auth and UI stores, image uploads) had zero regression protection. Introduce Vitest as the runner (TS-native, zero-config unit tests, built-in jsdom for browser-API tests, and a path to React Testing Library for future component tests), add a test script, and write 76 unit tests across the seven modules the issue lists. Add a GitHub Actions workflow that runs type-check, lint, test, and build on push and pull request with no required secrets, and document npm test in the README. Also fixes pre-existing type errors in adminStore, draftStore, and campaignDeployer that blocked the required type-check and build gates (identical to PRs OrbitChainLabs#18 and OrbitChainLabs#19).
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.
Summary
Closes #8
The wallet store fetched balances from a hardcoded Horizon testnet URL, so mainnet-configured wallets always showed a zero (or wrong-network) balance. The balance lookup now resolves the Horizon base URL from the configured network (
NEXT_PUBLIC_STELLAR_NETWORK, withNEXT_PUBLIC_STELLAR_HORIZON_URLas an override), distinguishes a Horizon 404 (a brand-new account — a valid zero) from real lookup failures, and keeps the balance fresh on connect and on a 15-second interval.The key design decision: the fetch logic lives in a new pure module (
lib/stellar/balance.ts) with no app imports, so the failure semantics and network selection are unit-testable with mockedfetchresponses; the store is a thin glue layer that owns the refresh lifecycle.Why
Before this change,
store/walletStore.tshithttps://horizon-testnet.stellar.org/accounts/<address>unconditionally, bypassing the repo's network-aware infrastructure (lib/stellar/config.tsHORIZON_URLS/getStellarConfig,lib/env.tsnetwork validation). Consequences:NEXT_PUBLIC_STELLAR_NETWORK=mainnet, balance lookups still went to testnet Horizon, returning 404 (rendered as'0.0000000') or an unrelated testnet balance.'0.0000000', so users couldn't tell an empty account from a lookup failure.components/Header.tsxduplicated the same hardcoded testnet fetch with its own 15s interval, so there were two competing implementations of the same bug.What was built
lib/stellar/balance.tsfetchNativeBalance(address, horizonUrl)andparseNativeBalance(data). Horizon 404 returnsZERO_BALANCEwith no error (new account); network failures and non-404 HTTP errors returnbalance: nullplus a human-readableerror. Trailing slashes on the base URL are tolerated. No app imports, so it runs under Node's test runner.lib/stellar/config.tsresolveHorizonUrl(): honorsNEXT_PUBLIC_STELLAR_HORIZON_URLwhen set, otherwise mapsNEXT_PUBLIC_STELLAR_NETWORK(testnet/mainnet/futurenet) to the module's well-knownHORIZON_URLS, where the env'smainnetmaps to thepublicnetwork.store/walletStore.tsconnectnow fetches the balance immediately and starts a 15s refresh interval;disconnectstops the timer; newrefreshBalance()action; newbalanceErrorstate field set on lookup failures (previous balance is preserved) instead of writing a fake zero.types/index.tscomponents/Header.tsxcomponents/WalletDropdown.tsxbalanceErroris set, instead of rendering a silently wrong0.00 XLM.tests/walletBalance.test.tsfetch: native balance, no native asset, 404, network error, non-404 HTTP error, trailing-slash URL handling, testnet URL resolution, mainnet URL resolution,NEXT_PUBLIC_STELLAR_HORIZON_URLoverride, end-to-end mainnet fetch, andparseNativeBalancefallback.package.json"test": "node --test \"tests/**/*.test.ts\""(Node's built-in runner, zero new dependencies).tsconfig.json"target": "ES2020"(was unset, defaulting to ES3) and"allowImportingTsExtensions": true(so tests can import.tsmodules under Node's native TypeScript support; safe withnoEmit).The tests exercise the exact failure semantics in the acceptance criteria: a 404 is a zero, a network error is an error, and the mainnet vs testnet URL selection is asserted end to end.
Integration changes outside
store/lib/server/adminStore.ts,lib/server/draftStore.ts,lib/server/campaignDeployer.ts— pre-existing type errors onmain(12 total) that madenpm run type-checkandnpm run buildfail before any wallet change. These are the same mechanical fixes as in PR fix(env): reconcile env schema with reads and fail fast at boot #18 (issue Environment contract is drifting: lib/env.ts schema omits variables read elsewhere and assertEnv is never invoked #9):noUncheckedIndexedAccessspread guards in the stores, and a port ofcampaignDeployer.tsfrom the removed@stellar/stellar-sdkv11SorobanRpc/scvalAPI to the v14rpc/nativeToScVal/xdrAPI (symbol map keys preserved). Included here only because the required quality gates cannot pass without them; identical content in both PRs merges cleanly regardless of order.tsconfig.json— target/extension settings above, required for the new tests and for the BigInt literals incampaignDeployer.ts.No other files were modified.
Acceptance criteria coverage
lib/stellar/balance.ts+resolveHorizonUrl()inlib/stellar/config.ts; verified bytests/walletBalance.test.ts— URL selection tests)NEXT_PUBLIC_STELLAR_NETWORKchanges the lookup target. (resolveHorizonUrl()mapsmainnet→https://horizon.stellar.org;tests/walletBalance.test.ts— "mainnet configuration resolves the mainnet Horizon URL" and "mainnet configuration fetches from the mainnet Horizon URL")fetchNativeBalance404 →ZERO_BALANCE/no error; network/HTTP errors →error+balanceErrorin the store, surfaced inWalletDropdown;tests/walletBalance.test.ts— "404 from Horizon…", "network failure…", "non-404 HTTP error…")store/walletStore.ts— immediate fetch onconnect+ 15s interval, stopped ondisconnect; reconnecting restarts both)tests/walletBalance.test.ts— 11 tests, all scenarios listed)Test plan
npm test— 11/11 passing (11 new tests)npm run type-check— no errorsnpm run lint— no warnings or errorsnpm run build— succeedsEnv vars / Notes
No new environment variables. Behavior of existing variables:
NEXT_PUBLIC_STELLAR_NETWORK— now controls the balance lookup target (testnet→https://horizon-testnet.stellar.org,mainnet→https://horizon.stellar.org,futurenet→https://horizon-futurenet.stellar.org).NEXT_PUBLIC_STELLAR_HORIZON_URL— optional override that wins over the network mapping when set.Notes:
Header.tsximplementation used, now owned by the store so it survives page navigation and stops cleanly on disconnect.balanceErroris set; the dropdown shows "Balance unavailable" rather than a fake zero.