Repository navigation
Conversation
This was referenced Oct 26, 2026
fix(stellar): run account reads under the fetch policy and report stale balances as unavailable
#525
Closed
rvelaz
force-pushed
the
fix/stale-balances-03-btc-scan-reliability
branch
from
October 28, 2026 10:27
d0acea0 to
679210f
Compare
The first full scan of a new account uses the discovery stop gap (5) instead of the standard gap (20). When that scan finds activity, extendDiscoveryScan checks only the addresses the standard gap adds (one Esplora history request each, through the network's shared fetch policy) and runs the gap-20 full scan only if one of them has history or the check fails, so coverage is unchanged. BitcoinAccount.hasSynchronized() tells a first scan (genesis checkpoint) from a rescan. In the A/B runs this cut the first-2-minute bitcoin requests from 257 to 137.
rvelaz
force-pushed
the
fix/stale-balances-04-btc-startup-cost
branch
from
October 28, 2026 10:30
1a8ef47 to
c872652
Compare
rvelaz
changed the base branch from
fix/stale-balances-03-btc-scan-reliability
to
main
October 28, 2026 10:30
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/bitcoin-wallet-snapExplanation
Bug
Each new BTC account gets a full gap-20 first scan (on
main, and also with PR 03). With the 5 accounts that onboarding creates, that is 257 bitcoin requests in the first 2 minutes, a burst that adds load on Infura and makes coalescing failures more likely. The unfixed build made about 40.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/bitcoin-wallet-snap. To see the wallet-side "Balance unavailable" + Retry, add core PR 10 and extension PR 12.bitcoin-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-http2), so that every request succeeds, and count the requests tobitcoin-mainnet.infura.io(and thescripthashlookups) in the first 2 minutes after onboarding.--disable-http2give no coalescing, and all four balances are correct.Expected: about 137 requests (116 scripthash), with all balances correct.
Actual (before this change): 257 requests (236 scripthash) on the fixes-v3 build (PRs 01–03, gap 20).
Without a browser:
AccountUseCases.test.ts›fullScan›completes the initial scan to the standard gap when the discovery gap finds activityand theEsploraClientAdapter›extendDiscoveryScantests (e.g.checks only the addresses the standard gap adds and skips the full scan when they are unused (run B-v4 account 0)).Summary
The first full scan of a new account uses the discovery stop gap (5) instead of the standard gap (20). When that scan finds activity, the snap checks only the addresses the standard gap would add, and runs the gap-20 scan only if one of them has history or the check fails. Bitcoin requests in the first 2 minutes after onboarding went from 257 to 137.
Why
In Chrome, HTTP/2 connection coalescing across
*.infura.iocan send the snap's Esplora request over a connection opened for another Infura host. The provider router answers with an empty 404 and no CORS headers, which shows up asFailed to fetch. Every extra request at startup is another chance to hit that, and it also loads the shared Infura Esplora endpoint. On a restored SRP, the account-tree sync creates account groups 1..4 before discovery runs. No discovery-scan reuse applies to them, so each got a gap-20 initial scan: about 42 requests for an empty account, where gap 5 needs about 10.What changed
use-cases/AccountUseCases.ts: the first full scan of an account (one that never synchronized) runs in'discovery'mode. If the result shows activity, it callschain.extendDiscoveryScan(account)instead of a full gap-20 rescan. Rescans keep the standard gap.entities/chain.ts,infra/EsploraClientAdapter.ts: newextendDiscoveryScan./scripthash/:hash/txsthrough the network's shared fetch policy (fetchWithPolicyfrom feat(bitcoin): use shared fetch policy helpers #530: retries, circuit breaker and the default per-attempt timeout).toScripthash()computes the Esplora script hash.entities/account.ts,infra/BdkAccountAdapter.ts,infra/StoredAccountAdapter.ts:Keychaintype,peekKeychainAddress,lastUsedIndexandlastRevealedIndex. The metadata-only stored view throws "unsupported". AlsohasSynchronized()(same code as PR 01).toScripthash, the gap check, the fallback to the full scan on history or failure), BdkAccountAdapter, StoredAccountAdapter.Validation
Run on a Cursor cloud VM against
mainatc2e8d19, with this PR's commit (c872652) applied withgit am:tsc --noEmit: pass.snap.manifest.jsonshasum mismatch (not regenerated yet).jestinpackages/bitcoin-wallet-snap: 27 suites, 697 tests, 0 failures.yarn workspace @metamask/bitcoin-wallet-snap run test(with coverage): pass.The earlier fail-before list (against PR 03) is no longer current: this PR is now based on
main.Manual A/B
A = Chrome with default HTTP/2 (reproduces the coalescing); B = Chrome with
--disable-http2(control, no coalescing). Every run used the same test wallet and a localyarn build:devof extension 13.54.0 (acf000d), with the snaps and assets-controller wired into node_modules by hand. "Unfixed 13.54.0" is that build with no fixes. The fixes-vN builds are cumulative: each one has every fix up to that pass, so an "after" number shows this PR together with the others in that build. Run names (A-v6, B-v7, ...) are our local test passes on those builds; times are CEST.All numbers are B runs: bitcoin requests / scripthash lookups in the first 2 minutes after onboarding.
The first BTC balance was correct at the first dump (≤ 9 s) in B-v4 to B-v7. The load is still above the unfixed build's ~40, because the old build did not run a full first scan per account. That is the trade-off for a reliable first balance.
Risks
Changelog (
packages/bitcoin-wallet-snap/CHANGELOG.md, Unreleased → Changed)References
Dependencies
main, on its own. It uses the shared fetch policy from feat(bitcoin): use shared fetch policy helpers #530. It does not need fix(bitcoin): report a never-synced balance as unavailable, not 0 #519, fix(bitcoin): guard Esplora reads with the shared fetch policy and timeouts #520 or fix(bitcoin): retry the first full scan and make Retry trigger a real scan #521.AccountUseCases.fullScan(the discovery-gap first scan moves into fix(bitcoin): retry the first full scan and make Retry trigger a real scan #521's#runFullScan). No conflict with PR 01.snap.manifest.jsonshasum.Stale non-EVM balances series (Chrome HTTP/2 coalescing across
*.infura.io):Checklist