Repository navigation
Conversation
…d network fails keyring_getAccountBalances passes failOnNetworks for the networks of the requested assets. If a native or token-account fetch for one of them fails, SnapAssetsAdapter throws AccountAssetsFetchError instead of returning a partial or empty result, so the client flags the balance unavailable instead of keeping a stale one. Other callers keep the skip-and-report behaviour.
This was referenced Oct 26, 2026
fix(stellar): run account reads under the fetch policy and report stale balances as unavailable
#525
Closed
This branch has not been deployed
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.
@metamask/solana-wallet-snapExplanation
Bug
When the Solana RPC requests fail,
getAccountBalancesreturns a partial or empty result instead of an error, so the wallet keeps showing the last SOL amount as if it were current, with no "Balance unavailable".How to reproduce
release/13.54.0(acf000d) withyarn build:dev. For the "after" case, also build this branch and copy itsdist/bundle.js,snap.manifest.jsonandpreinstalled-snap.jsonintonode_modules/@metamask/solana-wallet-snap. To see the wallet-side "Balance unavailable" + Retry, add core PR 10 and extension PR 12.solana-mainnet.infura.iocan reuse the HTTP/2 connection already open to another*.infura.iohost (in our runs the Solana snap usually opened it first). The reply is an empty 404 without CORS headers, so the snap seesFailed to fetch. We reproduced this on a test machine where every*.infura.ioname resolved to the same IP. It depends on the network path and may not reproduce on every network. In our runs we moved the shared connection between hosts on purpose: we dropped the test browser's sockets while briefly blocking the other Infura hosts, which "heals" one chain and breaks the others.--disable-http2give no coalescing, and all four balances are correct.Expected: within seconds of the first failure, the Solana row shows "Balance unavailable" + Retry; it recovers on its own after the heal.
Actual (before this change): the row keeps the old amount (fixes-v2 A-v2: SOL failed from 10:30 onwards and never showed an error; that build had neither this PR nor core PR 11).
Without a browser:
Keyring.test.ts›getAccountBalances›fails instead of answering with a partial result when an RPC fetch for a requested network failed (v3).Summary
keyring_getAccountBalancesnow throws when a native-balance or token-account RPC fetch fails for one of the requested networks. Before, it returned the partial or empty result. The client then flags the SOL balance as unavailable instead of quietly keeping a stale balance.Why
In Chrome, HTTP/2 connection coalescing across
*.infura.iosends the solana snap's requests over a connection that belongs to another Infura host. Each reply is an empty 404 without CORS headers, which the snap sees asFailed to fetch.SnapAssetsAdapter.fetchAccountAssetsusedPromise.allSettled, reported each rejection to Sentry and dropped it from the result. SogetAccountBalances"succeeded" with whatever was left, and the wallet kept showing the last SOL amount with no error and no Retry.What changed
SnapAssetsAdapter.fetchAccountAssets(account, { failOnNetworks }):AccountAssetsFetchError(new, carriesfailures,cause= the first reason) if any failure is on a network infailOnNetworks.AssetsService.fetchAccountAssetsFromRpc(account, options)forwards the option.SolanaKeyring.getAccountBalancespassesfailOnNetworks= the networks of the requested assets, viagetNetworkFromToken.AssetsServiceandKeyring.Validation
Checked locally: the test files changed in this PR were run with the parent's source (tests from this branch, every other file from the parent), then on this branch.
Unit tests that fail before and pass after (parent:
80b21f6)SolanaKeyring › getAccountBalances ›fails instead of answering with a partial result when an RPC fetch for a requested network failed (v3),fetches live balances for the account and returns only the requested assetsSnapAssetsAdapter › fetchAccountAssets with failOnNetworks ›throws AccountAssetsFetchError when the native balance fetch of a required network fails,throws when token accounts of a required network cannot be fetchedAssetsService › fetchAccountAssetsFromRpc ›delegates the live fetch to the Snap adapter,forwards failOnNetworks to the Snap adapterFull run on this branch
jest --runInBandinpackages/solana-wallet-snap: 84 of 89 suites pass, 1043 tests pass, 11 fail. The 11 failures are in 5 env-dependent suites (getSolanaExplorerUrl, three confirmationrendertests andindex.test). They fail the same way on80b21f6locally: there is no.envand no snap bundle. All touched suites pass.tsc --noEmit(package): clean.Manual A/B
This snap change was not exercised in a browser. None of the local fixes builds (v1–v7) wired in a rebuilt solana snap: only bitcoin and stellar (and tron from v5) were rebuilt, and the solana snap was the npm package (B-v3 notes: solana 7.2.0 from npm). The SOL results in those runs come from the client side, core PR 11 (an empty balance map counts as a failure):
This PR fixes the same case at the source: the snap stops returning a partial or empty map. That also covers a partial result (some assets fetched, the native balance failed), which the client-side check cannot see. Before merging, repeat the A/B above with this snap wired in.
Risks
getAccountBalancescall fail. The client already handles that: it keeps the account flagged with a Retry and a backoff. That is intended, but SOL can briefly flip to unavailable on a single blip.getAccountBalancesopts in. Asset discovery and cron sync keep the lenient behaviour.fetchAccountAssetshas the new JSDoc block stacked above the old one. It is kept here so this branch matches the tested cumulative code exactly.Changelog (
packages/solana-wallet-snap/CHANGELOG.md, Unreleased → Fixed)keyring_getAccountBalancesnow fails when an RPC fetch for a requested network fails, instead of returning a partial or empty balance, so the wallet shows the balance as unavailable rather than stale (#527)References
Dependencies
retrySnapBalances) and PR 12 (extension UI) turn the thrown error into 'Balance unavailable' + Retry.snap.manifest.jsonshasum withyarn workspace @metamask/solana-wallet-snap build. It is not included here.Stale non-EVM balances series (Chrome HTTP/2 coalescing across
*.infura.io):Checklist