Uh oh!
There was an error while loading. Please reload this page.
fix: never serve Accounts API balances from cache - #9867
Conversation
AccountsApiDataSource now invalidates cached balance queries before every fetch and fetches v5/v6 balances with staleTime: 0, gcTime: 0, so balances always hit the network in every flow (polling, forced refresh, websocket-triggered) and are never retained for reuse. Previously non-forced polls could reuse balances cached for up to 60s and forceUpdate: true only shrank the cache window to 100ms, so pull-to-refresh and post-transaction refreshes could show stale balances.
| // balance queries first (fetchQuery refetches invalidated queries | ||
| // regardless of freshness), then fetch with staleTime/gcTime 0 so the | ||
| // response is neither read back nor retained. | ||
| const fetchOptions = { staleTime: 0, gcTime: 0 }; |
There was a problem hiding this comment.
passing this to 0 now , we can also consider removing it
There was a problem hiding this comment.
Reverts this PR:
#9591
Worth double checking - I think instead of caching, we should attempt debouncing. But open to discuss
| // regardless of freshness), then fetch with staleTime/gcTime 0 so the | ||
| // response is neither read back nor retained. | ||
| const fetchOptions = { staleTime: 0, gcTime: 0 }; | ||
| await this.#apiClient.accounts.invalidateBalances(); |
There was a problem hiding this comment.
staleTime/gcTime only apply to this one call, old entries in the shared cache keep their 60s freshness. Invalidating first marks them stale for all consumers and forces an unconditional refetch, guaranteeing balances never come from cache.
There was a problem hiding this comment.
Yes, 100% agreed here
salimtb
commented
Aug 13, 2026
@metamaskbot publish-preview |
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
Align with MetaMask/core#9867: invalidate balance queries and fetch with staleTime/gcTime 0 on every AccountsApiDataSource fetch.
Align with MetaMask/core#9867: invalidate balance queries and fetch with staleTime/gcTime 0 on every AccountsApiDataSource fetch.
salimtb
commented
Aug 13, 2026
closing this one to do a revert instead: #9870 |
## Explanation `AccountsApiDataSource` polls for balances every 30s, but the underlying `ApiPlatformClient` caches responses for 60s (`STALE_TIMES.BALANCES`). Since the poll didn't pass `forceUpdate`, every other tick was served the cached response instead of hitting the network — balances only actually refreshed every ~60s. This passes `forceUpdate: true` on the poll's fetch call so it always bypasses the cache, same as other call sites (unlock, account switch, tx confirmation) already do. Kept the fix scoped to just the poll instead of removing caching everywhere, since #9591 added `forceUpdate` specifically to avoid bursts of duplicate calls when multiple triggers fire close together, and the unmerged #9867 ran into that regression by disabling the cache globally. ## References * Related to #9591 * Related to (closed) #9867 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Narrow cache-bypass on the 30s poll path only; does not change auth or other fetch sites. Slightly more Accounts API traffic on that interval, which is the intended behavior. > > **Overview** > Fixes `AccountsApiDataSource` polling serving stale balances on roughly every other tick. The 30s poll used `fetch()` without `forceUpdate`, so the 60s TanStack balances cache (`STALE_TIMES.BALANCES`) won. > > Recurring `pollFn` now passes `forceUpdate: true` so each tick hits the network (`staleTime: 0, gcTime: 0`), matching other authoritative refreshes. Bypass is scoped to the poll only so nearby unlock/switch/tx triggers still share the cache. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d162463. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
AccountsApiDataSource now invalidates cached balance queries before every fetch and fetches v5/v6 balances with staleTime: 0, gcTime: 0, so balances always hit the network in every flow (polling, forced refresh, websocket-triggered) and are never retained for reuse. Previously non-forced polls could reuse balances cached for up to 60s and forceUpdate: true only shrank the cache window to 100ms, so pull-to-refresh and post-transaction refreshes could show stale balances.
Extension PR: MetaMask/metamask-extension#45494
Mobile PR: MetaMask/metamask-mobile#34748
Explanation
References
Checklist