Skip to content

Conversation

@hmalik88
Copy link
Contributor

@hmalik88 hmalik88 commented Sep 18, 2025

Explanation

MultichainAccountService

  • Unified wallet creation into one flow (createMultichainAccountWallet) that handles import/restore/new vault.
  • Builds a single ServiceState index in one pass and passes state slices to wallets/groups (cuts repeated controller scans/calls).
  • Simplified init path and removed dead accountIdToContext mapping.

MultichainAccountWallet

  • init now consumes a pre-sliced wallet state (entropySource → groups → providerName → ids) instead of querying providers.
  • Emits clear events on group creation/updates; alignment orchestration uses provider state instead of full scans.

MultichainAccountGroup

  • init registers account IDs per provider and fills reverse maps; calls provider.addAccounts(ids) to keep providers in sync.
  • Added getAccountIds() for direct access to underlying IDs.
  • Improved partial‑failure reporting (aggregates provider errors by name).

BaseBip44AccountProvider

  • Added addAccounts(ids: string[]), enabling providers to track their own account ID lists.
  • getAccounts() paths rely on known IDs (plural lookups) rather than scanning the full controller list.

EvmAccountProvider

  • Switched from address‑based scans to ID‑based fetches (getAccount(s)) for create/discover (removes $O(n)$ scans).

Performance Analysis

n = total BIP-44 accounts in the AccountsController
p = number of providers (currently 4)
w = number of wallets (entropy sources)
g = total number of groups
e = number of created EVM accounts

When fully aligned $g = n / p$.
When accounts are not fully aligned then $g = max(f(p))$, where $f(p)$ is the number of accounts associated with a provider.

Consider two scenarios:

  1. State 1 -> State 2 transition, the user has unaligned groups after the transition.
  2. Already transitioned to State 2, the service is initialized after onboarding and every time the client is unlocked.

General formulas

For Scenario 2, the formulas are as follows:

Before this refactor, the number of loops can be represented $n * p * (1 + w + g)$, which with $p = 4$, becomes $n^2 + 4n(1 + w)$.

Before this refactor, the number of controller calls can be represented as $1 + w + g$, which with $p = 4$, becomes $1 + w + n/4$.

After this refactor, the number of loops can be represented by $n * p$, which with $p = 4$, becomes $4n$.

After this refactor, the number of calls is just $1$.

For Scenario 1, the formulas are entirely dependent on the breakdown of the number of accounts each provider has amongst the $n$ accounts, let's consider a scenario where Solana has $n/2$, Ethereum has $n/8$, Bitcoin has $n/4$ and Tron has $n/8$, the formulas would be as follows:

Before this refactor, the number of loops in the alignment process can be represented as $(p * g) + (n * e)$, which with $p=4$ and $g = n/2$, becomes $2n + 3n^2/8$. Therefore the number of loops for initialization + alignment in this scenario with $p = 4$ and $g = n/2$, becomes $(19/8)n^2 + (4w + 6)n$.

Before this refactor, the number of controller calls in the alignment process can be represented as $e$, which becomes $3n/8$. Therefore the number of controller calls for initialization + alignment in this scenario with $p = 4$, becomes $1 + w + 5n/8$.

After this refactor, the number of loops in the alignment process can be represented as $p * g$, which becomes $2n$. Therefore, the number of loops for initialization + alignment in this scenario with $p = 4$ and $g = n/2$, becomes $6n$.

After this refactor, the number of controller calls in the alignment process can be represented as $e$ which becomes $3n/8$. Therefore, the number of controller calls for initialization + alignment in this scenario with $p = 4$ and $g = n/2$, becomes $1 + 3n/8$.

In short, previous init performance for loops and controller calls was quadratic and linear, respectively. After, it is linear and constant.

Performance Charts

Below are charts that show performance (loops and controller calls) $n = 0$ -> $n = 256$ for Scenario 1 and 2 with $w = 2$, respectively:

MisalignedLoops MisalignedCalls AlignedLoops AlignedCalls

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, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Note

Major performance/architecture refactor across multichain account service.

  • Introduces centralized ServiceState built once, passed to MultichainAccountWallet.init and MultichainAccountGroup.init/update; removes per-provider scanning and legacy reverse maps
  • Unifies wallet creation via createMultichainAccountWallet supporting import/create/restore; adds removeMultichainAccountWallet action
  • Reworks alignment: groups call provider alignAccounts; improved partial‑failure aggregation/warnings; provider enable/disable respected via AccountProviderWrapper
  • BaseBip44AccountProvider now tracks account IDs internally (init, ID set), getAccounts/getAccount use controller ID lookups instead of global scans
  • EvmAccountProvider uses deterministic IDs (getUUIDFromAddressOfNormalAccount) and getAccount(s); similar ID tracking added to BTC/TRX/SOL providers
  • Extensive test updates and CHANGELOG marked BREAKING

Written by Cursor Bugbot for commit 91e7f90. This will update automatically on new commits. Configure here.

accountsList,
);
// we cast here because we know that the accounts are BIP-44 compatible
return internalAccounts as Bip44Account<KeyringAccount>[];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although the getAccounts's return type is (InternalAccount | undefined)[], we're sure to get back all the accounts we want since the accounts list will never be stale.

MultichainAccountWallet<Bip44Account<KeyringAccount>>
>;

readonly #accountIdToContext: Map<
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Decided to get rid of this mapping since it was only being used for handling the accountRemoved and accountAdded events, removing this gets rid of a large loop in the init function as well. If there's a particular need for this data at the client level, we can always add this back in.

Copy link
Contributor

Choose a reason for hiding this comment

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

@hmalik88
Copy link
Contributor Author

hmalik88 commented Jan 9, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-6a568504",
  "@metamask-previews/accounts-controller": "35.0.0-preview-6a568504",
  "@metamask-previews/address-book-controller": "7.0.1-preview-6a568504",
  "@metamask-previews/analytics-controller": "1.0.0-preview-6a568504",
  "@metamask-previews/announcement-controller": "8.0.0-preview-6a568504",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-6a568504",
  "@metamask-previews/approval-controller": "8.0.0-preview-6a568504",
  "@metamask-previews/assets-controllers": "95.0.0-preview-6a568504",
  "@metamask-previews/base-controller": "9.0.0-preview-6a568504",
  "@metamask-previews/bridge-controller": "64.4.0-preview-6a568504",
  "@metamask-previews/bridge-status-controller": "64.4.1-preview-6a568504",
  "@metamask-previews/build-utils": "3.0.4-preview-6a568504",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-6a568504",
  "@metamask-previews/claims-controller": "0.4.1-preview-6a568504",
  "@metamask-previews/composable-controller": "12.0.0-preview-6a568504",
  "@metamask-previews/controller-utils": "11.18.0-preview-6a568504",
  "@metamask-previews/core-backend": "5.0.0-preview-6a568504",
  "@metamask-previews/delegation-controller": "2.0.0-preview-6a568504",
  "@metamask-previews/earn-controller": "11.0.0-preview-6a568504",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-6a568504",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-6a568504",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-6a568504",
  "@metamask-previews/ens-controller": "19.0.0-preview-6a568504",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-6a568504",
  "@metamask-previews/eth-block-tracker": "15.0.0-preview-6a568504",
  "@metamask-previews/eth-json-rpc-middleware": "22.0.1-preview-6a568504",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-6a568504",
  "@metamask-previews/foundryup": "1.0.1-preview-6a568504",
  "@metamask-previews/gas-fee-controller": "26.0.0-preview-6a568504",
  "@metamask-previews/gator-permissions-controller": "0.8.0-preview-6a568504",
  "@metamask-previews/json-rpc-engine": "10.2.0-preview-6a568504",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-6a568504",
  "@metamask-previews/keyring-controller": "25.0.0-preview-6a568504",
  "@metamask-previews/logging-controller": "7.0.1-preview-6a568504",
  "@metamask-previews/message-manager": "14.1.0-preview-6a568504",
  "@metamask-previews/messenger": "0.3.0-preview-6a568504",
  "@metamask-previews/multichain-account-service": "5.0.0-preview-6a568504",
  "@metamask-previews/multichain-api-middleware": "1.2.5-preview-6a568504",
  "@metamask-previews/multichain-network-controller": "3.0.0-preview-6a568504",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-6a568504",
  "@metamask-previews/name-controller": "9.0.0-preview-6a568504",
  "@metamask-previews/network-controller": "27.2.0-preview-6a568504",
  "@metamask-previews/network-enablement-controller": "4.0.0-preview-6a568504",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-6a568504",
  "@metamask-previews/permission-controller": "12.2.0-preview-6a568504",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-6a568504",
  "@metamask-previews/phishing-controller": "16.1.0-preview-6a568504",
  "@metamask-previews/polling-controller": "16.0.0-preview-6a568504",
  "@metamask-previews/preferences-controller": "22.0.0-preview-6a568504",
  "@metamask-previews/profile-metrics-controller": "2.0.0-preview-6a568504",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-6a568504",
  "@metamask-previews/ramps-controller": "2.1.0-preview-6a568504",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-6a568504",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-6a568504",
  "@metamask-previews/sample-controllers": "4.0.0-preview-6a568504",
  "@metamask-previews/seedless-onboarding-controller": "7.1.0-preview-6a568504",
  "@metamask-previews/selected-network-controller": "26.0.0-preview-6a568504",
  "@metamask-previews/shield-controller": "4.1.0-preview-6a568504",
  "@metamask-previews/signature-controller": "38.0.0-preview-6a568504",
  "@metamask-previews/storage-service": "0.0.1-preview-6a568504",
  "@metamask-previews/subscription-controller": "5.4.0-preview-6a568504",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-6a568504",
  "@metamask-previews/transaction-controller": "62.8.0-preview-6a568504",
  "@metamask-previews/transaction-pay-controller": "10.6.0-preview-6a568504",
  "@metamask-previews/user-operation-controller": "41.0.0-preview-6a568504"
}

@hmalik88
Copy link
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-fd4aea5a",
  "@metamask-previews/accounts-controller": "35.0.1-preview-fd4aea5a",
  "@metamask-previews/address-book-controller": "7.0.1-preview-fd4aea5a",
  "@metamask-previews/analytics-controller": "1.0.0-preview-fd4aea5a",
  "@metamask-previews/announcement-controller": "8.0.0-preview-fd4aea5a",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-fd4aea5a",
  "@metamask-previews/approval-controller": "8.0.0-preview-fd4aea5a",
  "@metamask-previews/assets-controller": "0.0.0-preview-fd4aea5a",
  "@metamask-previews/assets-controllers": "95.2.0-preview-fd4aea5a",
  "@metamask-previews/base-controller": "9.0.0-preview-fd4aea5a",
  "@metamask-previews/bridge-controller": "64.5.0-preview-fd4aea5a",
  "@metamask-previews/bridge-status-controller": "64.4.2-preview-fd4aea5a",
  "@metamask-previews/build-utils": "3.0.4-preview-fd4aea5a",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-fd4aea5a",
  "@metamask-previews/claims-controller": "0.4.1-preview-fd4aea5a",
  "@metamask-previews/composable-controller": "12.0.0-preview-fd4aea5a",
  "@metamask-previews/connectivity-controller": "0.0.0-preview-fd4aea5a",
  "@metamask-previews/controller-utils": "11.18.0-preview-fd4aea5a",
  "@metamask-previews/core-backend": "5.0.0-preview-fd4aea5a",
  "@metamask-previews/delegation-controller": "2.0.0-preview-fd4aea5a",
  "@metamask-previews/earn-controller": "11.0.0-preview-fd4aea5a",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-fd4aea5a",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-fd4aea5a",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-fd4aea5a",
  "@metamask-previews/ens-controller": "19.0.1-preview-fd4aea5a",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-fd4aea5a",
  "@metamask-previews/eth-block-tracker": "15.0.0-preview-fd4aea5a",
  "@metamask-previews/eth-json-rpc-middleware": "22.0.1-preview-fd4aea5a",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-fd4aea5a",
  "@metamask-previews/foundryup": "1.0.1-preview-fd4aea5a",
  "@metamask-previews/gas-fee-controller": "26.0.1-preview-fd4aea5a",
  "@metamask-previews/gator-permissions-controller": "0.8.0-preview-fd4aea5a",
  "@metamask-previews/json-rpc-engine": "10.2.0-preview-fd4aea5a",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-fd4aea5a",
  "@metamask-previews/keyring-controller": "25.0.0-preview-fd4aea5a",
  "@metamask-previews/logging-controller": "7.0.1-preview-fd4aea5a",
  "@metamask-previews/message-manager": "14.1.0-preview-fd4aea5a",
  "@metamask-previews/messenger": "0.3.0-preview-fd4aea5a",
  "@metamask-previews/multichain-account-service": "5.0.0-preview-fd4aea5a",
  "@metamask-previews/multichain-api-middleware": "1.2.5-preview-fd4aea5a",
  "@metamask-previews/multichain-network-controller": "3.0.1-preview-fd4aea5a",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-fd4aea5a",
  "@metamask-previews/name-controller": "9.0.0-preview-fd4aea5a",
  "@metamask-previews/network-controller": "28.0.0-preview-fd4aea5a",
  "@metamask-previews/network-enablement-controller": "4.0.0-preview-fd4aea5a",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-fd4aea5a",
  "@metamask-previews/permission-controller": "12.2.0-preview-fd4aea5a",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-fd4aea5a",
  "@metamask-previews/phishing-controller": "16.1.0-preview-fd4aea5a",
  "@metamask-previews/polling-controller": "16.0.1-preview-fd4aea5a",
  "@metamask-previews/preferences-controller": "22.0.0-preview-fd4aea5a",
  "@metamask-previews/profile-metrics-controller": "2.0.0-preview-fd4aea5a",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-fd4aea5a",
  "@metamask-previews/ramps-controller": "3.0.0-preview-fd4aea5a",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-fd4aea5a",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-fd4aea5a",
  "@metamask-previews/sample-controllers": "4.0.1-preview-fd4aea5a",
  "@metamask-previews/seedless-onboarding-controller": "7.1.0-preview-fd4aea5a",
  "@metamask-previews/selected-network-controller": "26.0.1-preview-fd4aea5a",
  "@metamask-previews/shield-controller": "4.1.0-preview-fd4aea5a",
  "@metamask-previews/signature-controller": "38.0.1-preview-fd4aea5a",
  "@metamask-previews/storage-service": "0.0.1-preview-fd4aea5a",
  "@metamask-previews/subscription-controller": "5.4.0-preview-fd4aea5a",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-fd4aea5a",
  "@metamask-previews/transaction-controller": "62.9.1-preview-fd4aea5a",
  "@metamask-previews/transaction-pay-controller": "11.0.1-preview-fd4aea5a",
  "@metamask-previews/user-operation-controller": "41.0.1-preview-fd4aea5a"
}

@hmalik88
Copy link
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-4a66b658",
  "@metamask-previews/accounts-controller": "35.0.2-preview-4a66b658",
  "@metamask-previews/address-book-controller": "7.0.1-preview-4a66b658",
  "@metamask-previews/analytics-controller": "1.0.0-preview-4a66b658",
  "@metamask-previews/announcement-controller": "8.0.0-preview-4a66b658",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-4a66b658",
  "@metamask-previews/approval-controller": "8.0.0-preview-4a66b658",
  "@metamask-previews/assets-controller": "0.0.0-preview-4a66b658",
  "@metamask-previews/assets-controllers": "95.3.0-preview-4a66b658",
  "@metamask-previews/base-controller": "9.0.0-preview-4a66b658",
  "@metamask-previews/bridge-controller": "64.5.1-preview-4a66b658",
  "@metamask-previews/bridge-status-controller": "64.4.3-preview-4a66b658",
  "@metamask-previews/build-utils": "3.0.4-preview-4a66b658",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-4a66b658",
  "@metamask-previews/claims-controller": "0.4.1-preview-4a66b658",
  "@metamask-previews/composable-controller": "12.0.0-preview-4a66b658",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-4a66b658",
  "@metamask-previews/controller-utils": "11.18.0-preview-4a66b658",
  "@metamask-previews/core-backend": "5.0.0-preview-4a66b658",
  "@metamask-previews/delegation-controller": "2.0.0-preview-4a66b658",
  "@metamask-previews/earn-controller": "11.1.0-preview-4a66b658",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-4a66b658",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-4a66b658",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-4a66b658",
  "@metamask-previews/ens-controller": "19.0.2-preview-4a66b658",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-4a66b658",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-4a66b658",
  "@metamask-previews/eth-json-rpc-middleware": "23.0.0-preview-4a66b658",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-4a66b658",
  "@metamask-previews/foundryup": "1.0.1-preview-4a66b658",
  "@metamask-previews/gas-fee-controller": "26.0.2-preview-4a66b658",
  "@metamask-previews/gator-permissions-controller": "1.0.0-preview-4a66b658",
  "@metamask-previews/json-rpc-engine": "10.2.1-preview-4a66b658",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-4a66b658",
  "@metamask-previews/keyring-controller": "25.0.0-preview-4a66b658",
  "@metamask-previews/logging-controller": "7.0.1-preview-4a66b658",
  "@metamask-previews/message-manager": "14.1.0-preview-4a66b658",
  "@metamask-previews/messenger": "0.3.0-preview-4a66b658",
  "@metamask-previews/multichain-account-service": "5.0.0-preview-4a66b658",
  "@metamask-previews/multichain-api-middleware": "1.2.6-preview-4a66b658",
  "@metamask-previews/multichain-network-controller": "3.0.2-preview-4a66b658",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-4a66b658",
  "@metamask-previews/name-controller": "9.0.0-preview-4a66b658",
  "@metamask-previews/network-controller": "29.0.0-preview-4a66b658",
  "@metamask-previews/network-enablement-controller": "4.1.0-preview-4a66b658",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-4a66b658",
  "@metamask-previews/permission-controller": "12.2.0-preview-4a66b658",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-4a66b658",
  "@metamask-previews/phishing-controller": "16.1.0-preview-4a66b658",
  "@metamask-previews/polling-controller": "16.0.2-preview-4a66b658",
  "@metamask-previews/preferences-controller": "22.0.0-preview-4a66b658",
  "@metamask-previews/profile-metrics-controller": "3.0.0-preview-4a66b658",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-4a66b658",
  "@metamask-previews/ramps-controller": "4.0.0-preview-4a66b658",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-4a66b658",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-4a66b658",
  "@metamask-previews/sample-controllers": "4.0.2-preview-4a66b658",
  "@metamask-previews/seedless-onboarding-controller": "7.1.0-preview-4a66b658",
  "@metamask-previews/selected-network-controller": "26.0.2-preview-4a66b658",
  "@metamask-previews/shield-controller": "5.0.0-preview-4a66b658",
  "@metamask-previews/signature-controller": "39.0.1-preview-4a66b658",
  "@metamask-previews/storage-service": "0.0.1-preview-4a66b658",
  "@metamask-previews/subscription-controller": "5.4.0-preview-4a66b658",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-4a66b658",
  "@metamask-previews/transaction-controller": "62.9.2-preview-4a66b658",
  "@metamask-previews/transaction-pay-controller": "11.0.2-preview-4a66b658",
  "@metamask-previews/user-operation-controller": "41.0.2-preview-4a66b658"
}

@hmalik88
Copy link
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-903db2da",
  "@metamask-previews/accounts-controller": "35.0.2-preview-903db2da",
  "@metamask-previews/address-book-controller": "7.0.1-preview-903db2da",
  "@metamask-previews/analytics-controller": "1.0.0-preview-903db2da",
  "@metamask-previews/announcement-controller": "8.0.0-preview-903db2da",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-903db2da",
  "@metamask-previews/approval-controller": "8.0.0-preview-903db2da",
  "@metamask-previews/assets-controller": "0.0.0-preview-903db2da",
  "@metamask-previews/assets-controllers": "95.3.0-preview-903db2da",
  "@metamask-previews/base-controller": "9.0.0-preview-903db2da",
  "@metamask-previews/bridge-controller": "64.6.1-preview-903db2da",
  "@metamask-previews/bridge-status-controller": "64.4.3-preview-903db2da",
  "@metamask-previews/build-utils": "3.0.4-preview-903db2da",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-903db2da",
  "@metamask-previews/claims-controller": "0.4.1-preview-903db2da",
  "@metamask-previews/composable-controller": "12.0.0-preview-903db2da",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-903db2da",
  "@metamask-previews/controller-utils": "11.18.0-preview-903db2da",
  "@metamask-previews/core-backend": "5.0.0-preview-903db2da",
  "@metamask-previews/delegation-controller": "2.0.0-preview-903db2da",
  "@metamask-previews/earn-controller": "11.1.0-preview-903db2da",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-903db2da",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-903db2da",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-903db2da",
  "@metamask-previews/ens-controller": "19.0.2-preview-903db2da",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-903db2da",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-903db2da",
  "@metamask-previews/eth-json-rpc-middleware": "23.0.0-preview-903db2da",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-903db2da",
  "@metamask-previews/foundryup": "1.0.1-preview-903db2da",
  "@metamask-previews/gas-fee-controller": "26.0.2-preview-903db2da",
  "@metamask-previews/gator-permissions-controller": "1.0.0-preview-903db2da",
  "@metamask-previews/json-rpc-engine": "10.2.1-preview-903db2da",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-903db2da",
  "@metamask-previews/keyring-controller": "25.0.0-preview-903db2da",
  "@metamask-previews/logging-controller": "7.0.1-preview-903db2da",
  "@metamask-previews/message-manager": "14.1.0-preview-903db2da",
  "@metamask-previews/messenger": "0.3.0-preview-903db2da",
  "@metamask-previews/multichain-account-service": "5.1.0-preview-903db2da",
  "@metamask-previews/multichain-api-middleware": "1.2.6-preview-903db2da",
  "@metamask-previews/multichain-network-controller": "3.0.2-preview-903db2da",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-903db2da",
  "@metamask-previews/name-controller": "9.0.0-preview-903db2da",
  "@metamask-previews/network-controller": "29.0.0-preview-903db2da",
  "@metamask-previews/network-enablement-controller": "4.1.0-preview-903db2da",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-903db2da",
  "@metamask-previews/permission-controller": "12.2.0-preview-903db2da",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-903db2da",
  "@metamask-previews/perps-controller": "0.0.0-preview-903db2da",
  "@metamask-previews/phishing-controller": "16.1.0-preview-903db2da",
  "@metamask-previews/polling-controller": "16.0.2-preview-903db2da",
  "@metamask-previews/preferences-controller": "22.0.0-preview-903db2da",
  "@metamask-previews/profile-metrics-controller": "3.0.0-preview-903db2da",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-903db2da",
  "@metamask-previews/ramps-controller": "4.0.0-preview-903db2da",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-903db2da",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-903db2da",
  "@metamask-previews/sample-controllers": "4.0.2-preview-903db2da",
  "@metamask-previews/seedless-onboarding-controller": "7.1.0-preview-903db2da",
  "@metamask-previews/selected-network-controller": "26.0.2-preview-903db2da",
  "@metamask-previews/shield-controller": "5.0.0-preview-903db2da",
  "@metamask-previews/signature-controller": "39.0.1-preview-903db2da",
  "@metamask-previews/storage-service": "0.0.1-preview-903db2da",
  "@metamask-previews/subscription-controller": "5.4.0-preview-903db2da",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-903db2da",
  "@metamask-previews/transaction-controller": "62.9.2-preview-903db2da",
  "@metamask-previews/transaction-pay-controller": "11.0.2-preview-903db2da",
  "@metamask-previews/user-operation-controller": "41.0.2-preview-903db2da"
}

Copy link

@cursor cursor bot left a comment

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 1 potential issue.

for (const account of accounts) {
this.accounts.add(account);
}
}
Copy link

Choose a reason for hiding this comment

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

Provider accounts set not cleared on re-initialization

Medium Severity

The init() method in BaseBip44AccountProvider only adds account IDs to the internal accounts Set without ever clearing it. When MultichainAccountService.init() is called multiple times (e.g., during app restart or unlock cycles while keeping the same service instance), providers accumulate stale account IDs from previous initializations. Since providers are created once in the constructor and reused, the accounts set grows unbounded.

This could cause getAccounts() to request accounts that no longer exist in the AccountsController, and the as unknown as Account[] type assertion masks any undefined entries in the returned array.

Additional Locations (1)

Fix in Cursor Fix in Web

@ccharly ccharly changed the title refactor(multichain-account-service): Improved performance across package classes and improved error messages perf(multichain-account-service)!: avoid excessive state syncing Jan 22, 2026
Copy link

@cursor cursor bot left a comment

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.

for (const account of accounts) {
this.accounts.add(account);
}
}
Copy link

Choose a reason for hiding this comment

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

Provider accounts set accumulates without clearing on reinit

Medium Severity

The init method in BaseBip44AccountProvider only adds account IDs to the internal accounts Set without clearing it first. When MultichainAccountService.init() is called multiple times (e.g., during wallet lock/unlock cycles), #wallets is cleared but providers are reused with the same instances. This causes account IDs from previous initializations to accumulate in the provider's Set. If an account were ever removed between init calls, the stale ID would remain, potentially causing getAccounts() to include undefined entries when those IDs are fetched from AccountsController.

Additional Locations (1)

Fix in Cursor Fix in Web

this.#providerToAccounts.set(provider, accounts);
for (const accountId of accounts) {
this.#accountToProvider.set(accountId, provider);
}
Copy link

Choose a reason for hiding this comment

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

Redundant map updates in group alignment method

Low Severity

The alignAccounts method updates the #providerToAccounts and #accountToProvider maps twice: first inline during the Promise.allSettled callback (lines 292-295), and then again via this.update(groupState) at line 331. The update method calls #setState which clears entries for each provider via #clearAccountToProviderState and re-sets the same values. This is redundant work that adds complexity without changing behavior.

Additional Locations (1)

Fix in Cursor Fix in Web

@ccharly
Copy link
Contributor

ccharly commented Jan 27, 2026

@metamaskbot publish-preview

@github-actions
Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.0.0-preview-766f7065",
  "@metamask-previews/accounts-controller": "35.0.2-preview-766f7065",
  "@metamask-previews/address-book-controller": "7.0.1-preview-766f7065",
  "@metamask-previews/ai-controllers": "0.0.0-preview-766f7065",
  "@metamask-previews/analytics-controller": "1.0.0-preview-766f7065",
  "@metamask-previews/announcement-controller": "8.0.0-preview-766f7065",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-766f7065",
  "@metamask-previews/approval-controller": "8.0.0-preview-766f7065",
  "@metamask-previews/assets-controller": "0.0.0-preview-766f7065",
  "@metamask-previews/assets-controllers": "96.0.0-preview-766f7065",
  "@metamask-previews/base-controller": "9.0.0-preview-766f7065",
  "@metamask-previews/bridge-controller": "64.8.1-preview-766f7065",
  "@metamask-previews/bridge-status-controller": "64.4.4-preview-766f7065",
  "@metamask-previews/build-utils": "3.0.4-preview-766f7065",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-766f7065",
  "@metamask-previews/claims-controller": "0.4.1-preview-766f7065",
  "@metamask-previews/composable-controller": "12.0.0-preview-766f7065",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-766f7065",
  "@metamask-previews/controller-utils": "11.18.0-preview-766f7065",
  "@metamask-previews/core-backend": "5.0.0-preview-766f7065",
  "@metamask-previews/delegation-controller": "2.0.0-preview-766f7065",
  "@metamask-previews/earn-controller": "11.1.0-preview-766f7065",
  "@metamask-previews/eip-5792-middleware": "2.1.0-preview-766f7065",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-766f7065",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-766f7065",
  "@metamask-previews/ens-controller": "19.0.2-preview-766f7065",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-766f7065",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-766f7065",
  "@metamask-previews/eth-json-rpc-middleware": "23.0.0-preview-766f7065",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-766f7065",
  "@metamask-previews/foundryup": "1.0.1-preview-766f7065",
  "@metamask-previews/gas-fee-controller": "26.0.2-preview-766f7065",
  "@metamask-previews/gator-permissions-controller": "1.1.0-preview-766f7065",
  "@metamask-previews/json-rpc-engine": "10.2.1-preview-766f7065",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-766f7065",
  "@metamask-previews/keyring-controller": "25.0.0-preview-766f7065",
  "@metamask-previews/logging-controller": "7.0.1-preview-766f7065",
  "@metamask-previews/message-manager": "14.1.0-preview-766f7065",
  "@metamask-previews/messenger": "0.3.0-preview-766f7065",
  "@metamask-previews/multichain-account-service": "5.1.0-preview-766f7065",
  "@metamask-previews/multichain-api-middleware": "1.2.6-preview-766f7065",
  "@metamask-previews/multichain-network-controller": "3.0.2-preview-766f7065",
  "@metamask-previews/multichain-transactions-controller": "7.0.0-preview-766f7065",
  "@metamask-previews/name-controller": "9.0.0-preview-766f7065",
  "@metamask-previews/network-controller": "29.0.0-preview-766f7065",
  "@metamask-previews/network-enablement-controller": "4.1.0-preview-766f7065",
  "@metamask-previews/notification-services-controller": "21.0.0-preview-766f7065",
  "@metamask-previews/permission-controller": "12.2.0-preview-766f7065",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-766f7065",
  "@metamask-previews/perps-controller": "0.0.0-preview-766f7065",
  "@metamask-previews/phishing-controller": "16.1.0-preview-766f7065",
  "@metamask-previews/polling-controller": "16.0.2-preview-766f7065",
  "@metamask-previews/preferences-controller": "22.0.0-preview-766f7065",
  "@metamask-previews/profile-metrics-controller": "3.0.0-preview-766f7065",
  "@metamask-previews/profile-sync-controller": "27.0.0-preview-766f7065",
  "@metamask-previews/ramps-controller": "4.1.0-preview-766f7065",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-766f7065",
  "@metamask-previews/remote-feature-flag-controller": "4.0.0-preview-766f7065",
  "@metamask-previews/sample-controllers": "4.0.2-preview-766f7065",
  "@metamask-previews/seedless-onboarding-controller": "7.1.0-preview-766f7065",
  "@metamask-previews/selected-network-controller": "26.0.2-preview-766f7065",
  "@metamask-previews/shield-controller": "5.0.0-preview-766f7065",
  "@metamask-previews/signature-controller": "39.0.1-preview-766f7065",
  "@metamask-previews/storage-service": "0.0.1-preview-766f7065",
  "@metamask-previews/subscription-controller": "5.4.0-preview-766f7065",
  "@metamask-previews/token-search-discovery-controller": "4.0.0-preview-766f7065",
  "@metamask-previews/transaction-controller": "62.9.2-preview-766f7065",
  "@metamask-previews/transaction-pay-controller": "11.1.0-preview-766f7065",
  "@metamask-previews/user-operation-controller": "41.0.2-preview-766f7065"
}

Copy link

@cursor cursor bot left a comment

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.

for (const account of accounts) {
this.accounts.add(account);
}
}
Copy link

Choose a reason for hiding this comment

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

Provider state not cleared on re-initialization

Medium Severity

The init method in BaseBip44AccountProvider only adds account IDs to the internal accounts Set without clearing it first. The service's init method calls this.#wallets.clear() before re-initializing, but providers are constructed once and reused. When init is called multiple times (e.g., on unlock), stale account IDs persist in the provider's Set. Since AccountsController:getAccounts returns (InternalAccount | undefined)[] for missing IDs, and getAccounts() casts this without filtering, the returned array may contain undefined values, causing potential runtime errors downstream.

Additional Locations (1)

Fix in Cursor Fix in Web

this.#providerToAccounts.set(provider, accounts);
for (const accountId of accounts) {
this.#accountToProvider.set(accountId, provider);
}
Copy link

Choose a reason for hiding this comment

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

Redundant state setting in alignAccounts method

Low Severity

In alignAccounts, the #providerToAccounts and #accountToProvider maps are set twice: first inside the Promise.allSettled callback (lines 292-295), then again via this.update(groupState) at line 331. The update method calls #setState which clears and re-sets the same data. The first setting inside the callback is redundant since update will overwrite it anyway.

Additional Locations (1)

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants