fix: add BackendWebsocketDataSource tests for Arbitrum USDC balance update#9265
Open
salimtb wants to merge 5 commits into
Open
fix: add BackendWebsocketDataSource tests for Arbitrum USDC balance update#9265salimtb wants to merge 5 commits into
salimtb wants to merge 5 commits into
Conversation
f6eaa4f to
39a67eb
Compare
1cdc5c2 to
ca17234
Compare
Contributor
Author
|
@metamaskbot publish-preview |
Contributor
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
Prithpal-Sooriya
previously approved these changes
Jun 25, 2026
432b1cd to
4e4e438
Compare
Contributor
Author
|
@metamaskbot publish-preview |
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
Fixes stale or missing token balances when websocket live updates compete with Accounts API / RPC polling, and when websocket subscriptions are torn down and recreated during account switches or reconnects.
Manual verification (Arbitrum USDC):
eip155:42161/erc20:…) notification processed correctly after send/receiveExplanation
What was broken?
AssetsControllermerges balances from several sources: websocket push (BackendWebsocketDataSource), Accounts API polling (AccountsApiDataSource), RPC, Snap, etc. Several races caused the UI to show stale values or never recover after a transaction:staleTime), or polling could “win” right after a websocket update.subscriptionIdcould be dropped when the server-side subscription ID changed after reconnect; there was no per-channel callback fallback.forceUpdatestill stale —getAssets({ forceUpdate: true })did not bypass the Accounts API TanStack cache, and websocket freshness guards could block the forced fetch from applying (e.g. receiver switches to account 2 after a send but state still shows the pre-receive balance).What does this PR do?
AssetsControllersourceIdonDataResponse.BackendWebsocketDataSourcebalance push, marks thoseaccountId:assetIdentries fresh for 120s; passive polling sources (AccountsApiDataSource,RpcDataSource,SnapDataSource,StakedBalanceDataSource) cannot overwrite them during that window.getAssets({ forceUpdate: true })is taggedgetAssets:forceUpdate, clears freshness locks for applied balances, and is not subject to the websocket freshness filter — authoritative for account switch / manual refresh.forceUpdatefetch first, then subscribe, so API balances land before websocket recovery.#handleAccountGroupChanged).AccountsApiDataSourcerequest.forceUpdateis true, passes{ staleTime: 0, gcTime: 0 }tofetchV5MultiAccountBalancesso forced refreshes bypass the TanStack cache.BackendWebsocketDataSourcesubscriptionIdrouting is unreliable after reconnect.DataRequestwith balance updates.Non-obvious details
sourceIdis internal — added toDataResponsefor merge policy only; not a new public messenger action.forceUpdate.BackendWebSocketService:addChannelCallbackis not delegated in the client messenger, the primary websocket subscription path still works; callbacks are a fallback for reconnect routing mismatches.Scope
All changes are in
@metamask/assets-controlleronly. No dependency upgrades and no breaking public API changes.Test plan
yarn workspace @metamask/assets-controller test— new/updated unit tests passyarn workspace @metamask/assets-controller run changelog:validateforceUpdateshows correct balanceNew unit test coverage:
AssetsController: polling does not overwrite recent websocket balance;getAssetsforceUpdatewins over websocket freshnessAccountsApiDataSource:forceUpdatebypasses TanStack cacheBackendWebsocketDataSource: concurrent subscribe serialization, checksummed vs lowercase EVM addresses, channel-callback fallback, disconnect/reconnect chain reclaimReferences
Checklist
Note
Medium Risk
Changes how displayed balances are merged across websocket and polling paths and reorders fetch/subscribe on account changes; incorrect freshness or locking could show wrong balances until the next
forceUpdatefetch.Note
Medium Risk
Changes how displayed balances are merged across websocket and polling paths and reorders fetch/subscribe on account changes; incorrect freshness or locking could show wrong balances until the next forceUpdate fetch.
Overview
Fixes stale or flickering token balances when websocket pushes compete with Accounts API / RPC polling, and when subscriptions are recreated on account switch or reconnect.
AssetsControllertags applied updates with internalsourceIdonDataResponse. After websocket balance pushes, per-asset freshness locks block passive polling sources for 120s;getAssets({ forceUpdate: true })is taggedgetAssets:forceUpdate, skips that filter, and clears locks for applied balances. Account group and account-tree refreshes use a mutex and run force fetch before subscribe; account-tree updates with no overlapping account IDs are ignored.AccountsApiDataSourcepasses{ staleTime: 0, gcTime: 0 }to multi-account balance fetches whenforceUpdateis set.BackendWebsocketDataSourceserializes subscribe/unsubscribe, treats EVM addresses as unchanged when only checksumming differs, registers optional per-channel callbacks for reconnect routing, and passes the originalDataRequestwith balance updates.Reviewed by Cursor Bugbot for commit 773e4e8. Bugbot is set up for automated code reviews on this repo. Configure here.