refactor(multichain-account-service)!: replace withKeyring usage with withKeyringV2#8491
refactor(multichain-account-service)!: replace withKeyring usage with withKeyringV2#8491
withKeyring usage with withKeyringV2#8491Conversation
…t service types and base provider
withKeyring usage with withKeyringV2withKeyring usage with withKeyringV2
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 e442d90. Configure here.
| // Activity found — account stays. Return the address so we can | ||
| // look it up in the AccountsController after the callback persists. | ||
| return address; | ||
| }, |
There was a problem hiding this comment.
Discovery holds keyring mutex during network I/O
Medium Severity
The refactored discoverAccounts now performs the entire create → RPC check → delete flow inside a single withKeyringV2 callback. Since withKeyringV2 holds the KeyringController's exclusive mutex (#persistOrRollback → #withControllerLock), the RPC call to #getTransactionCount — which retries up to maxAttempts times with timeoutMs and backOffMs delays — now blocks all other keyring operations (signing, account creation, switching, etc.) for potentially several seconds. The prior implementation performed the RPC call outside withKeyring, acquiring the mutex only briefly for the peek and create steps.
Reviewed by Cursor Bugbot for commit e442d90. Configure here.
There was a problem hiding this comment.
The prior implementation peeked at addresses via keyring.root (V1 internal state) outside the lock. V2 keyrings don't expose HD internals, so we create the account inside the callback to discover the address, and delete it if inactive. Both must happen in the same callback so that #persistOrRollback sees zero net state change, as a result this holds the lock during the rpc call


Explanation
Changes
Base provider & types
AllowedActionsto useKeyringControllerWithKeyringV2ActionBaseBip44AccountProvider.withKeyringnow callsKeyringController:withKeyringV2withKeyringSelectorV2EvmAccountProvider
KeyringV2methods:getAccounts()returnsKeyringAccount[],createAccounts()replacesaddAccounts()bip44:derive-indexsinceHdKeyringV2does not supportbip44:derive-index-rangediscoverAccountsreplaces the V1keyring.rootpeek with a create → check → delete-if-no-activity pattern, preserving the same net behavior of not leaving unused accounts in theHD keyring
#getAddressFromGroupIndex(relied on V1 HD keyring internals)SnapAccountProvider
RestrictedSnapKeyringescape-hatch pattern that leaked keyring references outside thewithKeyringmutexwithSnapnow runs operations inside thewithKeyringV2scopecreateBip44Accountsalways useskeyring.createAccounts()directly, removing the V1/V2 branching based onbatchedconfigresyncAccountsuseskeyring.deleteAccount(id)instead ofkeyring.removeAccount(address)Snap subclasses (BTC, SOL, TRX)
createAccountV1overrides — V2 snaps handle chain-specific account creation internallyReferences
N/a
Checklist
Note
Medium Risk
Touches account creation/discovery paths and keyring mutex/persistence boundaries; regressions could lead to missed discovery results or unintended account/keyring state changes, though changes are scoped to provider/keyring integration.
Overview
BREAKING: Migrates multichain account providers’ keyring access from
KeyringController:withKeyringtoKeyringController:withKeyringV2, updating shared types/messenger delegation and the base providerwithKeyringhelper to useKeyringSelectorV2.Updates
EvmAccountProviderto use V2 keyring methods (getAccounts()returningKeyringAccount[],createAccounts(),deleteAccount()), changes range creation to loop per-index (since V2 HD keyrings don’t support range creation), and rewrites discovery to do create→RPC activity check→delete-if-inactive within a singlewithKeyringV2callback to ensure no net keyring state change when no activity is found.Keeps Snap-based providers on V1 keyring behavior for now by overriding
withKeyringto callKeyringController:withKeyring, while still accepting V2 selectors; tests are updated accordingly and the changelog documents the breaking API shift.Reviewed by Cursor Bugbot for commit 15f7986. Bugbot is set up for automated code reviews on this repo. Configure here.