Commit c66ba32
authored
fix: improve assets controller snap accounts subscription and websocket (#8430)
## Explanation
### Current state
`AssetsController` had three related issues that prevented balances from
loading correctly in certain scenarios:
1. **Snap accounts not subscribed after startup**: On app launch,
`#start()` was called before `AccountTreeController.init()` had finished
building the account tree, so snap accounts weren't yet in the selected
account group. Because `#start()` is idempotent (it returns early when
subscriptions already exist), snap accounts were never picked up when
they became available, leaving Solana, Tron, and Bitcoin balances
missing.
2. **Custom assets filtered by spam heuristics**: `TokenDataSource`
applied the `MIN_TOKEN_OCCURRENCES` threshold (EVM ERC-20) and Blockaid
bulk scan (non-EVM fungible tokens) to all tokens uniformly. This meant
tokens manually imported by the user and stored in `customAssets` state
were incorrectly filtered out as potential spam.
3. **No polling fallback when WebSocket is disabled**:
`BackendWebsocketDataSource` and `AccountsApiDataSource` both call the
same `fetchV2SupportedNetworks` API to determine which chains they
support. Because `BackendWebsocketDataSource` has higher priority in the
chain-claiming loop, it would claim all supported chains at
initialization — even when the WebSocket never connected. This left
`AccountsApiDataSource` with no chains to poll, so users with the
WebSocket disabled or with a broken connection received no balance
updates.
### Solution
1. **Re-subscribe on AccountTree state change**: Added
`#handleAccountTreeStateChange()` which subscribes to
`AccountTreeController:stateChange`. When the account tree updates (e.g.
after snap accounts are hydrated), the method forces a full
re-subscription and re-fetch with the complete account list. Added
`AccountsController:getSelectedAccount` as a fallback in
`#getSelectedAccounts()` for cases where the account tree is not yet
initialized.
2. **Custom assets bypass spam filters**: `TokenDataSource` now reads
`customAssets` from state and builds a `customAssetIds` set. Tokens in
that set skip the `occurrences >= 3` EVM filter and are excluded from
the non-EVM Blockaid scan input, ensuring user-imported assets always
appear.
3. **Connection-aware chain claiming in BackendWebsocketDataSource**:
Added an `#isConnected` flag (initially `false`).
`#initializeActiveChains` and `#refreshActiveChains` now store supported
chains in `#supportedChains` but only call `updateActiveChains` when
connected. On disconnect, `activeChains` is cleared so the
chain-claiming loop re-assigns those chains to `AccountsApiDataSource`
for polling. On reconnect, chains are restored from `#supportedChains`
before pending WebSocket subscriptions are reprocessed.
## References
* Related to MetaMask/metamask-extension (assets multichain rollout)
## Checklist
- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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]
> **Medium Risk**
> Changes asset subscription/chain-claiming behavior and adds
onboarding-based RPC suppression, which can affect when and how balances
update across multiple chains and account types.
>
> **Overview**
> Fixes multi-chain balance tracking edge cases by making
`AssetsController` re-subscribe/re-fetch when the account tree later
gains accounts (e.g. snap accounts), using
`AccountsController:getSelectedAccount` as a startup fallback and
tracking `#lastKnownAccountIds` to avoid redundant work.
>
> Improves data-source coordination: `BackendWebsocketDataSource` now
only claims chains while connected, releases them on disconnect so
`AccountsApiDataSource` can poll as fallback, and reclaims on reconnect
(dropping stale pending subscriptions). `TokenDataSource` now exempts
user-imported `customAssets` from occurrence/Blockaid spam filtering.
>
> Adds an optional `isOnboarded` hook plumbed into `RpcDataSource` so
`fetch`/`subscribe` become no-ops before onboarding completes,
preventing premature on-chain RPC calls; tests, changelog, and
dependency graph are updated accordingly.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d5ac722. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent e8ff5b7 commit c66ba32
File tree
11 files changed
+351
-65
lines changed- packages/assets-controller
- src
- data-sources
11 files changed
+351
-65
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
213 | 213 | | |
214 | 214 | | |
215 | 215 | | |
| 216 | + | |
216 | 217 | | |
217 | 218 | | |
218 | 219 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
10 | 16 | | |
11 | 17 | | |
12 | 18 | | |
| |||
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
24 | 39 | | |
25 | 40 | | |
26 | 41 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| 60 | + | |
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
142 | 142 | | |
143 | 143 | | |
144 | 144 | | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
145 | 154 | | |
146 | 155 | | |
147 | 156 | | |
| |||
0 commit comments