Skip to content

feat: start tracking on AccountTreeController:initialized - #9892

Merged
salimtb merged 5 commits into
mainfrom
feat/assets-controller-account-tree-initialized
Aug 18, 2026
Merged

feat: start tracking on AccountTreeController:initialized#9892
salimtb merged 5 commits into
mainfrom
feat/assets-controller-account-tree-initialized

Conversation

@salimtb

@salimtbsalimtb commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Subscribe to AccountTreeController:initialized so asset tracking starts only after the tree is fully built, instead of on intermediate stateChange events during init. Hosts must now also delegate the new event through the AssetsController messenger.

Extension PR: MetaMask/metamask-extension#45579
Mobile PR: MetaMask/metamask-mobile#34866

Explanation

References

Checklist

  • 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
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Cursor Bugbot is generating a summary for commit 22007ec. Configure here.

Subscribe to AccountTreeController:initialized so asset tracking starts
only after the tree is fully built, instead of on intermediate
stateChange events during init. Hosts must now also delegate the new
event through the AssetsController messenger.
@salimtb

Copy link
Copy Markdown
ContributorAuthor

@metamaskbot publish-preview

@salimtb
salimtb marked this pull request as ready for review August 17, 2026 09:19
@salimtb
salimtb requested review from a team as code ownersAugust 17, 2026 09:19
Comment threadpackages/assets-controller/src/AssetsController.ts
@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@8.0.0-preview-22007ec
@metamask-previews/accounts-controller@39.1.0-preview-22007ec
@metamask-previews/address-book-controller@7.1.2-preview-22007ec
@metamask-previews/ai-controllers@0.8.0-preview-22007ec
@metamask-previews/analytics-controller@2.0.0-preview-22007ec
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-22007ec
@metamask-previews/announcement-controller@8.1.0-preview-22007ec
@metamask-previews/app-metadata-controller@2.0.1-preview-22007ec
@metamask-previews/approval-controller@9.0.2-preview-22007ec
@metamask-previews/assets-controller@13.1.4-preview-22007ec
@metamask-previews/assets-controllers@111.1.1-preview-22007ec
@metamask-previews/authenticated-user-storage@3.0.1-preview-22007ec
@metamask-previews/base-controller@9.1.0-preview-22007ec
@metamask-previews/base-data-service@0.1.3-preview-22007ec
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-22007ec
@metamask-previews/bridge-controller@79.2.0-preview-22007ec
@metamask-previews/bridge-status-controller@75.1.0-preview-22007ec
@metamask-previews/build-utils@3.0.4-preview-22007ec
@metamask-previews/chain-agnostic-permission@1.7.0-preview-22007ec
@metamask-previews/chomp-api-service@4.0.0-preview-22007ec
@metamask-previews/claims-controller@0.6.0-preview-22007ec
@metamask-previews/client-controller@1.0.1-preview-22007ec
@metamask-previews/client-utils@2.0.2-preview-22007ec
@metamask-previews/compliance-controller@2.1.0-preview-22007ec
@metamask-previews/composable-controller@12.0.1-preview-22007ec
@metamask-previews/config-registry-controller@2.0.1-preview-22007ec
@metamask-previews/connectivity-controller@0.3.0-preview-22007ec
@metamask-previews/controller-utils@12.3.0-preview-22007ec
@metamask-previews/core-backend@8.1.2-preview-22007ec
@metamask-previews/delegation-controller@3.0.2-preview-22007ec
@metamask-previews/earn-controller@12.2.5-preview-22007ec
@metamask-previews/eip-5792-middleware@3.0.5-preview-22007ec
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-22007ec
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-22007ec
@metamask-previews/ens-controller@19.1.6-preview-22007ec
@metamask-previews/eth-block-tracker@15.0.1-preview-22007ec
@metamask-previews/eth-json-rpc-middleware@24.0.0-preview-22007ec
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-22007ec
@metamask-previews/foundryup@1.0.1-preview-22007ec
@metamask-previews/gas-fee-controller@26.3.1-preview-22007ec
@metamask-previews/gator-permissions-controller@5.0.1-preview-22007ec
@metamask-previews/geolocation-controller@1.0.0-preview-22007ec
@metamask-previews/java-tron-up@1.0.0-preview-22007ec
@metamask-previews/json-rpc-engine@10.5.0-preview-22007ec
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-22007ec
@metamask-previews/keyring-controller@27.1.1-preview-22007ec
@metamask-previews/kyc-controller@0.0.0-preview-22007ec
@metamask-previews/local-node-utils@1.0.0-preview-22007ec
@metamask-previews/logging-controller@9.0.0-preview-22007ec
@metamask-previews/message-manager@14.1.2-preview-22007ec
@metamask-previews/messenger@2.0.0-preview-22007ec
@metamask-previews/messenger-cli@0.2.0-preview-22007ec
@metamask-previews/money-account-api-data-service@0.4.0-preview-22007ec
@metamask-previews/money-account-balance-service@2.4.1-preview-22007ec
@metamask-previews/money-account-controller@1.0.0-preview-22007ec
@metamask-previews/money-account-upgrade-controller@3.0.1-preview-22007ec
@metamask-previews/money-account-utils@1.1.0-preview-22007ec
@metamask-previews/multichain-account-service@13.0.2-preview-22007ec
@metamask-previews/multichain-api-middleware@4.0.2-preview-22007ec
@metamask-previews/multichain-network-controller@3.2.3-preview-22007ec
@metamask-previews/multichain-transactions-controller@7.1.2-preview-22007ec
@metamask-previews/name-controller@9.1.2-preview-22007ec
@metamask-previews/network-connection-banner-controller@0.1.2-preview-22007ec
@metamask-previews/network-controller@35.0.1-preview-22007ec
@metamask-previews/network-enablement-controller@6.0.3-preview-22007ec
@metamask-previews/notification-services-controller@26.0.1-preview-22007ec
@metamask-previews/passkey-controller@3.0.0-preview-22007ec
@metamask-previews/permission-controller@13.1.1-preview-22007ec
@metamask-previews/permission-log-controller@5.1.0-preview-22007ec
@metamask-previews/perps-controller@12.0.0-preview-22007ec
@metamask-previews/phishing-controller@17.3.1-preview-22007ec
@metamask-previews/platform-api-docs@0.0.0-preview-22007ec
@metamask-previews/polling-controller@16.0.9-preview-22007ec
@metamask-previews/preferences-controller@23.1.0-preview-22007ec
@metamask-previews/profile-metrics-controller@4.0.3-preview-22007ec
@metamask-previews/profile-sync-controller@29.0.0-preview-22007ec
@metamask-previews/ramps-controller@20.0.0-preview-22007ec
@metamask-previews/rate-limit-controller@7.0.1-preview-22007ec
@metamask-previews/react-data-query@0.2.2-preview-22007ec
@metamask-previews/remote-feature-flag-controller@5.0.0-preview-22007ec
@metamask-previews/sample-controllers@5.0.4-preview-22007ec
@metamask-previews/seedless-onboarding-controller@10.1.1-preview-22007ec
@metamask-previews/selected-network-controller@26.1.6-preview-22007ec
@metamask-previews/sentinel-api-service@1.0.0-preview-22007ec
@metamask-previews/shield-controller@6.0.0-preview-22007ec
@metamask-previews/signature-controller@39.2.9-preview-22007ec
@metamask-previews/smart-transactions-controller@25.1.0-preview-22007ec
@metamask-previews/snap-account-service@2.1.2-preview-22007ec
@metamask-previews/social-controllers@2.7.1-preview-22007ec
@metamask-previews/solana-test-validator-up@1.0.0-preview-22007ec
@metamask-previews/stellar-quickstart-up@0.0.0-preview-22007ec
@metamask-previews/storage-service@1.0.2-preview-22007ec
@metamask-previews/subscription-controller@7.0.0-preview-22007ec
@metamask-previews/transaction-controller@69.5.2-preview-22007ec
@metamask-previews/transaction-pay-controller@26.3.0-preview-22007ec
@metamask-previews/user-operation-controller@41.2.8-preview-22007ec
@metamask-previews/wallet@10.0.0-preview-22007ec
@metamask-previews/wallet-cli@0.0.0-preview-22007ec

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e0e5a85. Configure here.

Comment threadpackages/assets-controller/src/data-sources/AccountsApiDataSource.ts Outdated
this.#ensureNativeBalancesDefaultZero();
this.#ensureDefaultTrackedAssetsSeeded();
this.#subscribeAssets();
this.#subscribeAssets({ skipInitialFetch: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip poll after failed fetch

Medium Severity

On startup and tree-refresh failure paths, #subscribeAssets is still called with skipInitialFetch: true even though the forced getAssets did not succeed. Accounts API then skips its subscribe-time poll, so balances stay empty until the next interval instead of recovering immediately.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit e0e5a85. Configure here.

@salimtb

Copy link
Copy Markdown
ContributorAuthor

@metamaskbot publish-preview

@salimtb

Copy link
Copy Markdown
ContributorAuthor

@metamaskbot publish-preview

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@8.0.0-preview-57cd9b4fb
@metamask-previews/accounts-controller@39.1.0-preview-57cd9b4fb
@metamask-previews/address-book-controller@7.1.2-preview-57cd9b4fb
@metamask-previews/ai-controllers@0.8.0-preview-57cd9b4fb
@metamask-previews/analytics-controller@2.0.0-preview-57cd9b4fb
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-57cd9b4fb
@metamask-previews/announcement-controller@8.1.0-preview-57cd9b4fb
@metamask-previews/app-metadata-controller@2.0.1-preview-57cd9b4fb
@metamask-previews/approval-controller@9.0.2-preview-57cd9b4fb
@metamask-previews/assets-controller@13.1.4-preview-57cd9b4fb
@metamask-previews/assets-controllers@111.1.1-preview-57cd9b4fb
@metamask-previews/authenticated-user-storage@3.0.1-preview-57cd9b4fb
@metamask-previews/base-controller@9.1.0-preview-57cd9b4fb
@metamask-previews/base-data-service@0.1.3-preview-57cd9b4fb
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-57cd9b4fb
@metamask-previews/bridge-controller@79.2.0-preview-57cd9b4fb
@metamask-previews/bridge-status-controller@75.1.0-preview-57cd9b4fb
@metamask-previews/build-utils@3.0.4-preview-57cd9b4fb
@metamask-previews/chain-agnostic-permission@1.7.0-preview-57cd9b4fb
@metamask-previews/chomp-api-service@4.0.0-preview-57cd9b4fb
@metamask-previews/claims-controller@0.6.0-preview-57cd9b4fb
@metamask-previews/client-controller@1.0.1-preview-57cd9b4fb
@metamask-previews/client-utils@2.0.2-preview-57cd9b4fb
@metamask-previews/compliance-controller@2.1.0-preview-57cd9b4fb
@metamask-previews/composable-controller@12.0.1-preview-57cd9b4fb
@metamask-previews/config-registry-controller@2.0.1-preview-57cd9b4fb
@metamask-previews/connectivity-controller@0.3.0-preview-57cd9b4fb
@metamask-previews/controller-utils@12.3.0-preview-57cd9b4fb
@metamask-previews/core-backend@8.1.2-preview-57cd9b4fb
@metamask-previews/delegation-controller@3.0.2-preview-57cd9b4fb
@metamask-previews/earn-controller@12.2.5-preview-57cd9b4fb
@metamask-previews/eip-5792-middleware@3.0.5-preview-57cd9b4fb
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-57cd9b4fb
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-57cd9b4fb
@metamask-previews/ens-controller@19.1.6-preview-57cd9b4fb
@metamask-previews/eth-block-tracker@15.0.1-preview-57cd9b4fb
@metamask-previews/eth-json-rpc-middleware@24.0.0-preview-57cd9b4fb
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-57cd9b4fb
@metamask-previews/foundryup@1.0.1-preview-57cd9b4fb
@metamask-previews/gas-fee-controller@26.3.1-preview-57cd9b4fb
@metamask-previews/gator-permissions-controller@5.0.1-preview-57cd9b4fb
@metamask-previews/geolocation-controller@1.0.0-preview-57cd9b4fb
@metamask-previews/java-tron-up@1.0.0-preview-57cd9b4fb
@metamask-previews/json-rpc-engine@10.5.0-preview-57cd9b4fb
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-57cd9b4fb
@metamask-previews/keyring-controller@27.1.1-preview-57cd9b4fb
@metamask-previews/kyc-controller@0.0.0-preview-57cd9b4fb
@metamask-previews/local-node-utils@1.0.0-preview-57cd9b4fb
@metamask-previews/logging-controller@9.0.0-preview-57cd9b4fb
@metamask-previews/message-manager@14.1.2-preview-57cd9b4fb
@metamask-previews/messenger@2.0.0-preview-57cd9b4fb
@metamask-previews/messenger-cli@0.2.0-preview-57cd9b4fb
@metamask-previews/money-account-api-data-service@0.4.0-preview-57cd9b4fb
@metamask-previews/money-account-balance-service@2.4.1-preview-57cd9b4fb
@metamask-previews/money-account-controller@1.0.0-preview-57cd9b4fb
@metamask-previews/money-account-upgrade-controller@3.0.1-preview-57cd9b4fb
@metamask-previews/money-account-utils@1.1.0-preview-57cd9b4fb
@metamask-previews/multichain-account-service@13.0.2-preview-57cd9b4fb
@metamask-previews/multichain-api-middleware@4.0.2-preview-57cd9b4fb
@metamask-previews/multichain-network-controller@3.2.3-preview-57cd9b4fb
@metamask-previews/multichain-transactions-controller@7.1.2-preview-57cd9b4fb
@metamask-previews/name-controller@9.1.2-preview-57cd9b4fb
@metamask-previews/network-connection-banner-controller@0.1.2-preview-57cd9b4fb
@metamask-previews/network-controller@35.0.1-preview-57cd9b4fb
@metamask-previews/network-enablement-controller@6.0.3-preview-57cd9b4fb
@metamask-previews/notification-services-controller@26.0.1-preview-57cd9b4fb
@metamask-previews/passkey-controller@3.0.0-preview-57cd9b4fb
@metamask-previews/permission-controller@13.1.1-preview-57cd9b4fb
@metamask-previews/permission-log-controller@5.1.0-preview-57cd9b4fb
@metamask-previews/perps-controller@12.0.0-preview-57cd9b4fb
@metamask-previews/phishing-controller@17.3.1-preview-57cd9b4fb
@metamask-previews/platform-api-docs@0.0.0-preview-57cd9b4fb
@metamask-previews/polling-controller@16.0.9-preview-57cd9b4fb
@metamask-previews/preferences-controller@23.1.0-preview-57cd9b4fb
@metamask-previews/profile-metrics-controller@4.0.3-preview-57cd9b4fb
@metamask-previews/profile-sync-controller@29.0.0-preview-57cd9b4fb
@metamask-previews/ramps-controller@20.0.0-preview-57cd9b4fb
@metamask-previews/rate-limit-controller@7.0.1-preview-57cd9b4fb
@metamask-previews/react-data-query@0.2.2-preview-57cd9b4fb
@metamask-previews/remote-feature-flag-controller@5.0.0-preview-57cd9b4fb
@metamask-previews/sample-controllers@5.0.4-preview-57cd9b4fb
@metamask-previews/seedless-onboarding-controller@10.1.1-preview-57cd9b4fb
@metamask-previews/selected-network-controller@26.1.6-preview-57cd9b4fb
@metamask-previews/sentinel-api-service@1.0.0-preview-57cd9b4fb
@metamask-previews/shield-controller@6.0.0-preview-57cd9b4fb
@metamask-previews/signature-controller@39.2.9-preview-57cd9b4fb
@metamask-previews/smart-transactions-controller@25.1.0-preview-57cd9b4fb
@metamask-previews/snap-account-service@2.1.2-preview-57cd9b4fb
@metamask-previews/social-controllers@2.7.1-preview-57cd9b4fb
@metamask-previews/solana-test-validator-up@1.0.0-preview-57cd9b4fb
@metamask-previews/stellar-quickstart-up@0.0.0-preview-57cd9b4fb
@metamask-previews/storage-service@1.0.2-preview-57cd9b4fb
@metamask-previews/subscription-controller@7.0.0-preview-57cd9b4fb
@metamask-previews/transaction-controller@69.5.2-preview-57cd9b4fb
@metamask-previews/transaction-pay-controller@26.3.0-preview-57cd9b4fb
@metamask-previews/user-operation-controller@41.2.8-preview-57cd9b4fb
@metamask-previews/wallet@10.0.0-preview-57cd9b4fb
@metamask-previews/wallet-cli@0.0.0-preview-57cd9b4fb

| AccountTreeControllerSelectedAccountGroupChangeEvent
| AccountTreeControllerStateChangeEvent
| AccountTreeControllerInitializedEvent
| AccountTreeControllerUninitializedEvent

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hosts that filter messenger events must now also allow :initialized / :uninitialized. These are the tree-ready / torn-down signals. Startup used to ride on :stateChange during AccountTreeController.init(), which fired many times and spammed the Accounts API.

* Whether `AccountTreeController` has finished `init()`. Unlock / UI-open
* alone must not start fetches — the tree can still be mid-build.
*/
#accountTreeInitialized = false;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New flag. Unlock and UI-open can happen before AccountTreeController.init() finishes; without it #updateActive() could start fetches on a half-built tree. Tracking now runs only when UI is open and unlocked and the tree is initialized.

'AccountTreeController:selectedAccountGroupChange',
(groupId) => {
this.#handleAccountGroupChanged(groupId).catch(console.error);
(groupId, previousGroupId) => {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thread previousGroupId through. ATC re-emits selectedAccountGroupChange on every init() even when the group didn't change, so we need the previous id to tell real user switches from init noise.

// Start tracking only after the account tree is fully built. Unlock can
// happen before `AccountTreeController.init()`, and `:stateChange` fires
// for intermediate mutations during that build.
this.messenger.subscribe('AccountTreeController:initialized', () => {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:initialized flips the flag and calls #updateActive() to start tracking once; :uninitialized clears it and stops tracking (wallet reset / clearState) so we don't keep polling stale accounts.

#updateActive(): void {
const shouldRun = this.#uiOpen && this.#keyringUnlocked;
const shouldRun =
this.#uiOpen && this.#keyringUnlocked && this.#accountTreeInitialized;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single start/stop gate now also requires #accountTreeInitialized. Unlock alone is no longer enough — this prevents the unlock-before-init Accounts API burst.

// Subscribe after seed so the price poll sees natives / defaults.
this.#subscribeAssets();
// Balances were just force-fetched — skip AccountsApi's subscribe-time poll.
this.#subscribeAssets({ skipInitialFetch: true });

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Group switch already force-fetched via getAssets, so skip AccountsApi's subscribe-time poll here too.

* is created. Used after the controller has already force-fetched balances
* for the same accounts/chains so we do not immediately hit the API again.
*/
skipInitialFetch?: boolean;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional subscribe flag so AssetsController can tell AccountsApi that balances for this scope are already fresh. Non-breaking — sources that don't read it behave as before.

const { request } = context;

// Price/metadata-only requests must not hit the Accounts API.
if (!request.dataTypes.includes('balance')) {

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip the Accounts API balance middleware for price-only refreshes (currency change, missing-price backfill, etc.). Balances aren't needed there, so pass through to the next middleware without the HTTP call.

// Interval above still polls on the normal cadence. This only skips the
// one-shot fetch at subscribe time when the controller already ran a
// force getAssets for the same scope (startup / group refresh).
if (!skipInitialFetch) {

@salimtbsalimtbAug 17, 2026

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setInterval is still armed , polling is not disabled. We only skip the immediate one-shot fetch when the controller already force-fetched the same accounts/chains; the next tick still runs on pollInterval.

// AssetsController
'AccountTreeController:selectedAccountGroupChange',
'AccountTreeController:stateChange',
'AccountTreeController:initialized',

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mirror the host messenger allowlist so the fixture can publish the new :initialized / :uninitialized lifecycle events.

@salimtb
salimtb added this pull request to the merge queueAug 18, 2026
Merged via the queue into main with commit 4bbeea8Aug 18, 2026
58 checks passed
@salimtb
salimtb deleted the feat/assets-controller-account-tree-initialized branch August 18, 2026 08:31
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@salimtb@juanmigdr