Repository navigation
Conversation
…ker is open and dedupe failure reports Explicit keyring balance and asset requests (the wallet's Retry) bypass an open circuit breaker with one direct attempt per upstream request at most every 10 s, shared by concurrent callers. Background reads never bypass. Open-circuit short-circuits are no longer sent to trackError, and 'Failed to fetch account info' is reported once per failure streak.
This was referenced Oct 26, 2026
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/tron-wallet-snapExplanation
Bug
When TronGrid or the Tron provider has failed enough times to open the shared circuit breaker, the TRX row's Retry does nothing: the snap rejects the request locally, sends no request, and reports another error to Sentry on every click.
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/tron-wallet-snap. To see the wallet-side "Balance unavailable" + Retry, add core PR 10 and extension PR 12.tron-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: the first click sends tron requests at once; a click within 10 s reuses the result; a later click sends a new attempt. Sentry gets one 'Failed to fetch account info' per failure streak and none for the open circuit.
Actual (before this change): no tron request for clicks inside the breaker's open window, and one trackError per click.
Without a browser:
keyring.openCircuit.test.tsdrivesgetAccountBalances/getAccountAssetswith the breaker open.Summary
When TronGrid or the Tron provider has failed enough times to open the shared fetch-policy circuit breaker, the wallet's Retry button did nothing: the snap rejected the request locally and sent no request. This PR lets the wallet's own Retry requests (keyring
getAccountAssets/getAccountBalances) bypass an open breaker. Each upstream request gets at most one direct attempt every 10 s, and concurrent callers share it. Background and cron reads still respect the breaker. It also stops repeat failures from flooding Sentry (snap_trackError).Why
In Chrome, HTTP/2 connection coalescing across
*.infura.iocan send a snap's request over a connection opened for another Infura host. The reply is an empty 404 without CORS headers, so the snap seesFailed to fetch. Tron reads then fail in a burst and the circuit breaker opens. The wallet shows "Balance unavailable" + Retry, but:SnapDataSourcecallsgetAccountAssetsbeforegetAccountBalances, and both short-circuited withBrokenCircuitError, so Retry sent no request until the breaker cooled down (the EM A-v5 test: 0 requests for 5 clicks made 9–23 s after the last failure);snap_trackError('Failed to fetch account info'), and the open-circuit error itself was also tracked (A-v5: 34 + 1 for one outage).What changed
utils/openCircuitBypass.ts(new):OpenCircuitBypass. When a guarded read throwsBrokenCircuitErrorand the caller asked forbypassOpenCircuit, it makes one direct request with no retries, not counted by the policy. It is keyed by network + method + URL + body and limited to once every 10 s. Concurrent callers share the in-flight attempt, a result can be reused for up to 5 s, and a direct success resets the state.TrongridApiClient,TronHttpClient: accept the bypass option on account info, TRC20, account resources and reward reads.AccountsService,AssetsService,SnapAssetsAdapter,handlers/keyring/keyring.ts: keyringgetAccountAssets/getAccountBalancespass{ bypassOpenCircuit: true }. Cron and background paths do not.utils/errors.ts:shouldTrackdrops the open-circuit error, matched by message, because the keyring SDK rethrows it as a plainError.utils/failureStreakGate.ts(new): repeated sync failures such as 'Failed to fetch account info' are reported once per failure streak, keyed by message, with a reminder every 15 min. A success ends the streak.keyring.openCircuit.test.ts(new; tests 1 and 3 fail without the fix),openCircuitBypass.test.ts,failureStreakGate.test.ts, plus updates to the client, service and error tests.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)TronHttpClient › open circuit bypass (v5) › lets an explicit request reach the network once while the circuit is open, and the same test inTrongridApiClientKeyringHandler › getAccountBalances › fetches live balances for the account and returns the balance record;KeyringHandler › getAccountAssets › fetches live assets for all account scopes and returns their asset typesAssetsService › fetchAccountAssets › fetches live assets from the chain for all the account scopesAccountsService › synchronize (assets) › does not report accounts short-circuited by an open circuit (v5)keyring.openCircuit.test.ts,openCircuitBypass.test.tsandfailureStreakGate.test.tscannot run on the parent because their modules are new.Full run on this branch
jest --runInBandinpackages/tron-wallet-snap: 64 suites, 958 tests pass.tsc --noEmit(package): clean.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./v1/accounts,getaccountresource,getRewardwithin 0.1 s; +2 s reused; +13 s new requests. fixes-v7, A-v7: the same (request in the same second at 18 s)Risks
@metamask/snap-networks-utilschanges. It is covered by a test.Changelog (
packages/tron-wallet-snap/CHANGELOG.md, Unreleased → Fixed)References
Dependencies
main.getAccountAssets/getAccountBalances.snap.manifest.jsonshasum. It is not included because snap builds were out of scope for this split. Runyarn workspace @metamask/tron-wallet-snap buildand commit the manifest.Stale non-EVM balances series (Chrome HTTP/2 coalescing across
*.infura.io):Checklist