From 489f275e5eaf6c8d49e803f6d01f5fc8c4fb845d Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 16:51:09 +0200 Subject: [PATCH 1/9] chore(deps): bump keyring-api, keyring-snap-client, eth-snap-keyring and keyring-snap-sdk --- .../multichain-account-service/package.json | 6 +- packages/snap-account-service/package.json | 6 +- yarn.lock | 128 +++++++++++++++++- 3 files changed, 127 insertions(+), 13 deletions(-) diff --git a/packages/multichain-account-service/package.json b/packages/multichain-account-service/package.json index 33dcab211b..0514acc156 100644 --- a/packages/multichain-account-service/package.json +++ b/packages/multichain-account-service/package.json @@ -56,12 +56,12 @@ "@ethereumjs/util": "^9.1.0", "@metamask/accounts-controller": "^39.0.3", "@metamask/base-controller": "^9.1.0", - "@metamask/eth-snap-keyring": "^22.3.0", + "@metamask/eth-snap-keyring": "^22.4.0", "@metamask/key-tree": "^10.1.1", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/keyring-internal-api": "^11.0.1", - "@metamask/keyring-snap-client": "^9.0.2", + "@metamask/keyring-snap-client": "^9.1.0", "@metamask/keyring-utils": "^3.3.1", "@metamask/messenger": "^1.2.0", "@metamask/snap-account-service": "^1.0.0", diff --git a/packages/snap-account-service/package.json b/packages/snap-account-service/package.json index 9d513c49d8..4be1192a73 100644 --- a/packages/snap-account-service/package.json +++ b/packages/snap-account-service/package.json @@ -55,10 +55,10 @@ "dependencies": { "@metamask/account-api": "^1.0.4", "@metamask/account-tree-controller": "^7.5.3", - "@metamask/eth-snap-keyring": "^22.3.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/eth-snap-keyring": "^22.4.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", - "@metamask/keyring-snap-sdk": "^9.0.2", + "@metamask/keyring-snap-sdk": "^9.1.0", "@metamask/messenger": "^1.2.0", "@metamask/snaps-controllers": "^19.0.0", "@metamask/snaps-sdk": "^11.0.0", diff --git a/yarn.lock b/yarn.lock index 08009bff9f..214a40c709 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6823,6 +6823,32 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-snap-keyring@npm:^22.4.0": + version: 22.4.0 + resolution: "@metamask/eth-snap-keyring@npm:22.4.0" + dependencies: + "@ethereumjs/tx": "npm:^5.4.0" + "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/keyring-internal-api": "npm:^11.0.1" + "@metamask/keyring-internal-snap-client": "npm:^10.0.4" + "@metamask/keyring-sdk": "npm:^2.2.0" + "@metamask/keyring-snap-sdk": "npm:^9.1.0" + "@metamask/keyring-utils": "npm:^3.3.1" + "@metamask/messenger": "npm:^1.1.1" + "@metamask/snaps-controllers": "npm:^19.0.1" + "@metamask/snaps-sdk": "npm:^11.0.0" + "@metamask/snaps-utils": "npm:^12.2.1" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.11.0" + "@types/uuid": "npm:^9.0.8" + async-mutex: "npm:^0.5.0" + uuid: "npm:^9.0.1" + peerDependencies: + "@metamask/keyring-api": ^23.0.0 + checksum: 10/16cdf47083d5feed3346ae19a507ff491e4d9c53e15c943670b25441bdcfc71ff8d99f25c6bdb56035942e65fec77087582ee1fdf786cc2380de3144a5fa41b0 + languageName: node + linkType: hard + "@metamask/ethjs-contract@npm:^0.4.1": version: 0.4.1 resolution: "@metamask/ethjs-contract@npm:0.4.1" @@ -7173,6 +7199,18 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-api@npm:^23.4.0": + version: 23.4.0 + resolution: "@metamask/keyring-api@npm:23.4.0" + dependencies: + "@metamask/keyring-utils": "npm:^3.3.1" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.11.0" + bitcoin-address-validation: "npm:^2.2.3" + checksum: 10/2a8152faac3076e12254c74c2e92931a3ecb22b3772985b6e4967aa42be1aa1f294ef56476e3ef6f16c430ee35ab2de38159a2516af01d231bdee9974187ddbf + languageName: node + linkType: hard + "@metamask/keyring-controller@npm:^27.1.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" @@ -7237,6 +7275,19 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-internal-snap-client@npm:^10.0.4": + version: 10.0.4 + resolution: "@metamask/keyring-internal-snap-client@npm:10.0.4" + dependencies: + "@metamask/keyring-api": "npm:^23.4.0" + "@metamask/keyring-internal-api": "npm:^11.0.1" + "@metamask/keyring-snap-client": "npm:^9.1.0" + "@metamask/keyring-utils": "npm:^3.3.1" + "@metamask/messenger": "npm:^1.1.1" + checksum: 10/6718176b1e24ca575eb351252b5246804eb2030bcce2f48c03df63b43ad9da86019ec3cd957c70d08a7ee8624c27ab8355dcc3eaca6b3e97073599ee604e1697 + languageName: node + linkType: hard + "@metamask/keyring-sdk@npm:^2.0.2, @metamask/keyring-sdk@npm:^2.2.0": version: 2.2.0 resolution: "@metamask/keyring-sdk@npm:2.2.0" @@ -7271,6 +7322,22 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-snap-client@npm:^9.1.0": + version: 9.1.0 + resolution: "@metamask/keyring-snap-client@npm:9.1.0" + dependencies: + "@metamask/keyring-api": "npm:^23.4.0" + "@metamask/keyring-utils": "npm:^3.3.1" + "@metamask/superstruct": "npm:^3.1.0" + "@types/uuid": "npm:^9.0.8" + uuid: "npm:^9.0.1" + webextension-polyfill: "npm:^0.12.0" + peerDependencies: + "@metamask/providers": ^19.0.0 + checksum: 10/af1aff0318ee6380b2d3f01439df13cfcfe8892df13eb0aaf294eea50ddcfb739d4e10975493ff50a1e79fa413797457bb6eb7063ef108ea62b4a732b73a15e8 + languageName: node + linkType: hard + "@metamask/keyring-snap-sdk@npm:^9.0.2": version: 9.0.2 resolution: "@metamask/keyring-snap-sdk@npm:9.0.2" @@ -7287,6 +7354,22 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-snap-sdk@npm:^9.1.0": + version: 9.1.0 + resolution: "@metamask/keyring-snap-sdk@npm:9.1.0" + dependencies: + "@metamask/keyring-utils": "npm:^3.3.1" + "@metamask/snaps-sdk": "npm:^11.0.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.11.0" + webextension-polyfill: "npm:^0.12.0" + peerDependencies: + "@metamask/keyring-api": ^23.0.0 + "@metamask/providers": ^19.0.0 + checksum: 10/aefa41c793e7323eed6e508c636580d66f3ba0d593f633bb52d686d29e26d858d9a676b1377eeca92cb6092bbfa9a4a26a44474f0384104f65d109dd7cc03918 + languageName: node + linkType: hard + "@metamask/keyring-utils@npm:^3.2.0, @metamask/keyring-utils@npm:^3.3.1": version: 3.3.1 resolution: "@metamask/keyring-utils@npm:3.3.1" @@ -7520,12 +7603,12 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/eth-hd-keyring": "npm:^14.1.1" - "@metamask/eth-snap-keyring": "npm:^22.3.0" + "@metamask/eth-snap-keyring": "npm:^22.4.0" "@metamask/key-tree": "npm:^10.1.1" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" - "@metamask/keyring-snap-client": "npm:^9.0.2" + "@metamask/keyring-snap-client": "npm:^9.1.0" "@metamask/keyring-utils": "npm:^3.3.1" "@metamask/messenger": "npm:^1.2.0" "@metamask/providers": "npm:^22.1.0" @@ -8467,10 +8550,10 @@ __metadata: "@metamask/account-api": "npm:^1.0.4" "@metamask/account-tree-controller": "npm:^7.5.3" "@metamask/auto-changelog": "npm:^6.1.0" - "@metamask/eth-snap-keyring": "npm:^22.3.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/eth-snap-keyring": "npm:^22.4.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" - "@metamask/keyring-snap-sdk": "npm:^9.0.2" + "@metamask/keyring-snap-sdk": "npm:^9.1.0" "@metamask/keyring-utils": "npm:^3.3.1" "@metamask/messenger": "npm:^1.2.0" "@metamask/snaps-controllers": "npm:^19.0.0" @@ -8561,7 +8644,7 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-sdk@npm:^11.0.0, @metamask/snaps-sdk@npm:^11.1.0": +"@metamask/snaps-sdk@npm:^11.0.0, @metamask/snaps-sdk@npm:^11.1.0, @metamask/snaps-sdk@npm:^11.1.1": version: 11.1.1 resolution: "@metamask/snaps-sdk@npm:11.1.1" dependencies: @@ -8606,6 +8689,37 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-utils@npm:^12.2.1": + version: 12.2.1 + resolution: "@metamask/snaps-utils@npm:12.2.1" + dependencies: + "@babel/core": "npm:^7.23.2" + "@babel/types": "npm:^7.23.0" + "@metamask/key-tree": "npm:^10.1.1" + "@metamask/messenger": "npm:^1.2.0" + "@metamask/permission-controller": "npm:^13.1.1" + "@metamask/rpc-errors": "npm:^7.0.3" + "@metamask/slip44": "npm:^4.4.0" + "@metamask/snaps-registry": "npm:^4.0.0" + "@metamask/snaps-sdk": "npm:^11.1.1" + "@metamask/superstruct": "npm:^3.2.1" + "@metamask/utils": "npm:^11.11.0" + "@scure/base": "npm:^1.1.1" + chalk: "npm:^4.1.2" + cron-parser: "npm:^4.5.0" + fast-deep-equal: "npm:^3.1.3" + fast-json-stable-stringify: "npm:^2.1.0" + fast-xml-parser: "npm:^5.5.6" + luxon: "npm:^3.5.0" + marked: "npm:^12.0.1" + rfdc: "npm:^1.3.0" + semver: "npm:^7.5.4" + ses: "npm:^1.15.0" + validate-npm-package-name: "npm:^5.0.0" + checksum: 10/fda8caefa414d9755a2305b345b5bbbf30ee614215a49070449f314d5d906c645457cf0d7f4881a82f69d398aff5ad2fe9c648bbb2a5003c29c2d01e2f42f199 + languageName: node + linkType: hard + "@metamask/social-controllers@workspace:packages/social-controllers": version: 0.0.0-use.local resolution: "@metamask/social-controllers@workspace:packages/social-controllers" From c5c509c1a294c7fb0c491d96b89d5807a4cd4cc7 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 16:51:14 +0200 Subject: [PATCH 2/9] feat(snap-account-service): add getCapabilities action --- packages/snap-account-service/CHANGELOG.md | 8 +++++ .../SnapAccountService-method-action-types.ts | 18 ++++++++++ .../src/SnapAccountService.test.ts | 35 +++++++++++++++++++ .../src/SnapAccountService.ts | 23 ++++++++++++ packages/snap-account-service/src/index.ts | 1 + 5 files changed, 85 insertions(+) diff --git a/packages/snap-account-service/CHANGELOG.md b/packages/snap-account-service/CHANGELOG.md index 8566289c4f..6dec8269a4 100644 --- a/packages/snap-account-service/CHANGELOG.md +++ b/packages/snap-account-service/CHANGELOG.md @@ -7,10 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `getCapabilities` action to expose a Snap's keyring capabilities ([#0000](https://github.com/MetaMask/core/pull/0000)) + - Reads the capabilities the bridge keyring populated from the Snap's manifest, letting consumers decide whether to drive the Snap through the v1 or v2 keyring path. + ### Changed +- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) - Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) - Bump `@metamask/keyring-snap-sdk` from `^9.0.1` to `^9.0.2` ([#9249](https://github.com/MetaMask/core/pull/9249)) +- Bump `@metamask/keyring-snap-sdk` from `^9.0.2` to `^9.1.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) ## [1.0.0] diff --git a/packages/snap-account-service/src/SnapAccountService-method-action-types.ts b/packages/snap-account-service/src/SnapAccountService-method-action-types.ts index 8434c285eb..6d5ce20f0f 100644 --- a/packages/snap-account-service/src/SnapAccountService-method-action-types.ts +++ b/packages/snap-account-service/src/SnapAccountService-method-action-types.ts @@ -50,6 +50,23 @@ export type SnapAccountServiceEnsureMigratedAction = { handler: SnapAccountService['ensureMigrated']; }; +/** + * Returns the keyring capabilities declared by the given Snap. These are + * populated by the bridge keyring from the Snap's manifest, and describe + * which keyring features the Snap supports (scopes, BIP-44 options, etc.). + * + * Consumers use this to decide whether to drive the Snap through the v1 or + * v2 keyring path. Reading capabilities does not mutate state, so the + * lock-free keyring access is used. + * + * @param snapId - ID of the Snap. + * @returns The Snap's keyring capabilities. + */ +export type SnapAccountServiceGetCapabilitiesAction = { + type: `SnapAccountService:getCapabilities`; + handler: SnapAccountService['getCapabilities']; +}; + /** * Handle a message from a Snap. * @@ -69,4 +86,5 @@ export type SnapAccountServiceMethodActions = | SnapAccountServiceGetSnapsAction | SnapAccountServiceEnsureReadyAction | SnapAccountServiceEnsureMigratedAction + | SnapAccountServiceGetCapabilitiesAction | SnapAccountServiceHandleKeyringSnapMessageAction; diff --git a/packages/snap-account-service/src/SnapAccountService.test.ts b/packages/snap-account-service/src/SnapAccountService.test.ts index 4beae87549..cd60bc4bd2 100644 --- a/packages/snap-account-service/src/SnapAccountService.test.ts +++ b/packages/snap-account-service/src/SnapAccountService.test.ts @@ -9,6 +9,7 @@ import type { } from '@metamask/keyring-api'; import { KeyringEvent } from '@metamask/keyring-api'; import { KeyringType } from '@metamask/keyring-api/v2'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import { KeyringControllerError, KeyringControllerErrorMessage, @@ -334,6 +335,7 @@ function mockWithKeyringV2Unsafe( setSelectedAccounts?: jest.Mock; hasAccount?: (id: string) => boolean; handleKeyringSnapMessage?: jest.Mock; + capabilities?: KeyringCapabilities; } >, ): void { @@ -774,6 +776,39 @@ describe('SnapAccountService', () => { }); }); + describe('getCapabilities', () => { + it('returns the capabilities of the matching v2 Snap keyring', async () => { + const { service, mocks } = await setup(); + + const capabilities: KeyringCapabilities = { + scopes: ['solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'], + bip44: { + deriveIndex: true, + deriveIndexRange: true, + discover: true, + }, + }; + mockWithKeyringV2Unsafe(mocks, { + [MOCK_SNAP_ID]: { capabilities }, + }); + + expect(await service.getCapabilities(MOCK_SNAP_ID)).toStrictEqual( + capabilities, + ); + }); + + it('throws when there is no v2 keyring for the Snap', async () => { + const { service, mocks } = await setup(); + + // No keyring configured for the Snap → withKeyringV2Unsafe throws. + mockWithKeyringV2Unsafe(mocks, {}); + + await expect(service.getCapabilities(MOCK_SNAP_ID)).rejects.toThrow( + KeyringControllerErrorMessage.KeyringNotFound, + ); + }); + }); + describe('handleKeyringSnapMessage', () => { const MOCK_MESSAGE = { method: KeyringEvent.AccountUpdated, diff --git a/packages/snap-account-service/src/SnapAccountService.ts b/packages/snap-account-service/src/SnapAccountService.ts index d3e369ad23..6e086009c7 100644 --- a/packages/snap-account-service/src/SnapAccountService.ts +++ b/packages/snap-account-service/src/SnapAccountService.ts @@ -20,6 +20,7 @@ import { KeyringEvent, } from '@metamask/keyring-api'; import { KeyringType } from '@metamask/keyring-api/v2'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { KeyringControllerGetStateAction, KeyringControllerStateChangeEvent, @@ -55,6 +56,7 @@ import { projectLogger as log } from './logger'; import type { SnapAccountServiceEnsureReadyAction, SnapAccountServiceEnsureMigratedAction, + SnapAccountServiceGetCapabilitiesAction, SnapAccountServiceGetSnapsAction, SnapAccountServiceHandleKeyringSnapMessageAction, } from './SnapAccountService-method-action-types'; @@ -84,6 +86,7 @@ export const serviceName = 'SnapAccountService'; const MESSENGER_EXPOSED_METHODS = [ 'ensureMigrated', 'ensureReady', + 'getCapabilities', 'getSnaps', 'handleKeyringSnapMessage', ] as const; @@ -94,6 +97,7 @@ const MESSENGER_EXPOSED_METHODS = [ export type SnapAccountServiceActions = | SnapAccountServiceEnsureMigratedAction | SnapAccountServiceEnsureReadyAction + | SnapAccountServiceGetCapabilitiesAction | SnapAccountServiceGetSnapsAction | SnapAccountServiceHandleKeyringSnapMessageAction; @@ -562,6 +566,25 @@ export class SnapAccountService { ); } + /** + * Returns the keyring capabilities declared by the given Snap. These are + * populated by the bridge keyring from the Snap's manifest, and describe + * which keyring features the Snap supports (scopes, BIP-44 options, etc.). + * + * Consumers use this to decide whether to drive the Snap through the v1 or + * v2 keyring path. Reading capabilities does not mutate state, so the + * lock-free keyring access is used. + * + * @param snapId - ID of the Snap. + * @returns The Snap's keyring capabilities. + */ + async getCapabilities(snapId: SnapId): Promise { + return this.#withKeyringV2Unsafe( + snapId, + async (keyring) => keyring.capabilities, + ); + } + /** * Handle a message from a Snap. * diff --git a/packages/snap-account-service/src/index.ts b/packages/snap-account-service/src/index.ts index 6cda8e7a67..e7c34c72bc 100644 --- a/packages/snap-account-service/src/index.ts +++ b/packages/snap-account-service/src/index.ts @@ -12,6 +12,7 @@ export type { export type { SnapAccountServiceEnsureMigratedAction, SnapAccountServiceEnsureReadyAction, + SnapAccountServiceGetCapabilitiesAction, SnapAccountServiceGetSnapsAction, SnapAccountServiceHandleKeyringSnapMessageAction, } from './SnapAccountService-method-action-types'; From feb0197428126563275c5f265d06f6e31d72ae92 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 16:51:18 +0200 Subject: [PATCH 3/9] feat(multichain-account-service): select v1/v2 keyring client from snap capabilities --- .../multichain-account-service/CHANGELOG.md | 14 ++ .../src/providers/BtcAccountProvider.test.ts | 44 ++++- .../src/providers/BtcAccountProvider.ts | 75 +------ .../src/providers/SnapAccountProvider.test.ts | 14 +- .../src/providers/SnapAccountProvider.ts | 186 +++++++++++++++--- .../src/providers/SnapKeyringClient.test.ts | 87 ++++++++ .../src/providers/SnapKeyringClient.ts | 88 +++++++++ .../src/providers/SolAccountProvider.test.ts | 40 +++- .../src/providers/SolAccountProvider.ts | 70 +------ .../src/providers/TrxAccountProvider.test.ts | 40 +++- .../src/providers/TrxAccountProvider.ts | 72 +------ .../src/providers/XlmAccountProvider.test.ts | 37 +++- .../src/providers/XlmAccountProvider.ts | 72 +------ .../src/tests/messenger.ts | 1 + .../multichain-account-service/src/types.ts | 8 +- 15 files changed, 514 insertions(+), 334 deletions(-) create mode 100644 packages/multichain-account-service/src/providers/SnapKeyringClient.test.ts create mode 100644 packages/multichain-account-service/src/providers/SnapKeyringClient.ts diff --git a/packages/multichain-account-service/CHANGELOG.md b/packages/multichain-account-service/CHANGELOG.md index e48912185d..89d5d9de42 100644 --- a/packages/multichain-account-service/CHANGELOG.md +++ b/packages/multichain-account-service/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Capability-gated v1/v2 keyring client selection for Snap account providers ([#0000](https://github.com/MetaMask/core/pull/0000)) + - Providers resolve a Snap's capabilities via `SnapAccountService:getCapabilities` and route account creation and discovery through the v1 or v2 keyring client accordingly (a Snap that declares BIP-44 capabilities is treated as v2). + - Account discovery for v2 Snaps flows through `createAccounts({ bip44:discover })`; v1 Snaps keep using the `discoverAccounts` client method. + +### Changed + +- **BREAKING:** Remove `batched` from `SnapAccountProviderConfig['createAccounts']` ([#0000](https://github.com/MetaMask/core/pull/0000)) + - Whether accounts are created in a single batched `createAccounts` call is now derived from the Snap's `bip44.deriveIndexRange` capability instead of static config. +- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Bump `@metamask/keyring-snap-client` from `^9.0.2` to `^9.1.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) + ## [11.1.0] ### Added diff --git a/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts b/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts index bde6eba947..c4ecd4b842 100644 --- a/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts @@ -1,5 +1,10 @@ import { isBip44Account } from '@metamask/account-api'; -import { AccountCreationType, BtcAccountType } from '@metamask/keyring-api'; +import { + AccountCreationType, + BtcAccountType, + BtcScope, +} from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { SnapControllerState } from '@metamask/snaps-controllers'; @@ -35,6 +40,19 @@ function asConfig( ) as SnapAccountProviderConfig; } +/** + * v2 capabilities as declared by a fully v2-compliant Bitcoin Snap manifest. + * Drives the batched `createAccounts` flow and the v2 discovery path. + */ +const BTC_V2_CAPABILITIES: KeyringCapabilities = { + scopes: [BtcScope.Mainnet], + bip44: { + deriveIndex: true, + deriveIndexRange: true, + discover: true, + }, +}; + class MockBtcKeyring { readonly type = 'MockBtcKeyring'; @@ -147,16 +165,19 @@ class MockBtcAccountProvider extends BtcAccountProvider { * @param options.messenger - An optional messenger instance to use. Defaults to a new Messenger. * @param options.accounts - List of accounts to use. * @param options.config - Provider config. + * @param options.capabilities - The Snap keyring capabilities to expose via `SnapAccountService:getCapabilities`. * @returns An object containing the controller instance and the messenger. */ function setup({ messenger = getRootMessenger(), accounts = [], config, + capabilities = { scopes: [] }, }: { messenger?: RootMessenger; accounts?: InternalAccount[]; config?: SnapAccountProviderConfig; + capabilities?: KeyringCapabilities; } = {}): { provider: AccountProviderWrapper; messenger: RootMessenger; @@ -182,6 +203,11 @@ function setup({ () => ({ isReady: true }) as SnapControllerState, ); + messenger.registerActionHandler( + 'SnapAccountService:getCapabilities', + async () => capabilities, + ); + messenger.registerActionHandler( 'AccountsController:listMultichainAccounts', () => accounts, @@ -300,8 +326,8 @@ describe('BtcAccountProvider', () => { const accounts = [MOCK_BTC_P2WPKH_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - // Force v1 by not providing the config at all, so it relies on the default value. - config: asConfig({ createAccounts: { batched: undefined } }), + // No capabilities provided → defaults to an empty capability set, so + // the provider falls back to the v1 (non-batched) flow. }); await provider.createAccounts({ @@ -464,7 +490,7 @@ describe('BtcAccountProvider', () => { const accounts = [MOCK_BTC_P2WPKH_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: BTC_V2_CAPABILITIES, }); const newGroupIndex = accounts.length; // Group-index are 0-based. @@ -487,7 +513,7 @@ describe('BtcAccountProvider', () => { const accounts = [MOCK_BTC_P2WPKH_ACCOUNT_1]; const { provider } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: BTC_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -503,7 +529,7 @@ describe('BtcAccountProvider', () => { const accounts = [MOCK_BTC_P2WPKH_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: BTC_V2_CAPABILITIES, }); const from = 1; @@ -528,7 +554,7 @@ describe('BtcAccountProvider', () => { it('creates accounts with range starting from 0', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: BTC_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -545,7 +571,7 @@ describe('BtcAccountProvider', () => { it('creates a single account when range from equals to', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: BTC_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -566,7 +592,7 @@ describe('BtcAccountProvider', () => { it('throws if the account creation process takes too long', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: BTC_V2_CAPABILITIES, }); mocks.keyring.createAccounts.mockImplementation( diff --git a/packages/multichain-account-service/src/providers/BtcAccountProvider.ts b/packages/multichain-account-service/src/providers/BtcAccountProvider.ts index 5661dbaa30..90f514d539 100644 --- a/packages/multichain-account-service/src/providers/BtcAccountProvider.ts +++ b/packages/multichain-account-service/src/providers/BtcAccountProvider.ts @@ -1,24 +1,18 @@ import type { Bip44Account } from '@metamask/account-api'; import type { TraceCallback } from '@metamask/controller-utils'; import type { EntropySourceId, KeyringAccount } from '@metamask/keyring-api'; -import { - AccountCreationType, - BtcAccountType, - BtcScope, -} from '@metamask/keyring-api'; -import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; +import { BtcAccountType, BtcScope } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { SnapId } from '@metamask/snaps-sdk'; +import type { CaipChainId } from '@metamask/utils'; import { traceFallback } from '../analytics'; -import { TraceName } from '../analytics/traces'; import type { MultichainAccountServiceMessenger } from '../types'; import { SnapAccountProvider } from './SnapAccountProvider'; import type { RestrictedSnapKeyring, SnapAccountProviderConfig, } from './SnapAccountProvider'; -import { withRetry, withTimeout } from './utils'; export type BtcAccountProviderConfig = SnapAccountProviderConfig; @@ -27,7 +21,6 @@ export const BTC_ACCOUNT_PROVIDER_NAME = 'Bitcoin'; export const BTC_ACCOUNT_PROVIDER_DEFAULT_CONFIG: BtcAccountProviderConfig = { maxConcurrency: 3, createAccounts: { - batched: false, // For now, the Snap is not fully v2 compliant. timeoutMs: 3000, }, discovery: { @@ -46,13 +39,9 @@ export class BtcAccountProvider extends SnapAccountProvider { static BTC_SNAP_ID = 'npm:@metamask/bitcoin-wallet-snap' as SnapId; - readonly capabilities: KeyringCapabilities = { - scopes: [BtcScope.Mainnet, BtcScope.Testnet], - bip44: { - deriveIndex: true, - deriveIndexRange: true, - }, - }; + // TODO: Remove once the Snap is fully v2 — discovery is then driven by the + // Snap's own supported scopes via `createAccounts({ bip44:discover })`. + protected readonly v1DiscoveryScopes: CaipChainId[] = [BtcScope.Mainnet]; constructor( messenger: MultichainAccountServiceMessenger, @@ -87,58 +76,4 @@ export class BtcAccountProvider extends SnapAccountProvider { scope: BtcScope.Mainnet, }); } - - async discoverAccounts({ - entropySource, - groupIndex, - }: { - entropySource: EntropySourceId; - groupIndex: number; - }): Promise[]> { - return this.withSnap(async ({ client, keyring }) => { - return await super.trace( - { - name: TraceName.SnapDiscoverAccounts, - data: { - provider: this.getName(), - }, - }, - async () => { - if (!this.config.discovery.enabled) { - return []; - } - - const discoveredAccounts = await withRetry( - () => - withTimeout( - () => - client.discoverAccounts( - [BtcScope.Mainnet], - entropySource, - groupIndex, - ), - this.config.discovery.timeoutMs, - ), - { - maxAttempts: this.config.discovery.maxAttempts, - backOffMs: this.config.discovery.backOffMs, - }, - ); - - if ( - !Array.isArray(discoveredAccounts) || - discoveredAccounts.length === 0 - ) { - return []; - } - - return await this.createBip44Accounts(keyring, { - type: AccountCreationType.Bip44DeriveIndex, - entropySource, - groupIndex, - }); - }, - ); - }); - } } diff --git a/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts b/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts index 20b53f60b0..b05e0f37fc 100644 --- a/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts @@ -65,7 +65,7 @@ class MockSnapAccountProvider extends SnapAccountProvider { maxActiveCount: number; }; - readonly capabilities: KeyringCapabilities = { + capabilities: KeyringCapabilities = { scopes: [ SolScope.Devnet, SolScope.Testnet, @@ -77,6 +77,8 @@ class MockSnapAccountProvider extends SnapAccountProvider { }, }; + protected readonly v1DiscoveryScopes = [SolScope.Mainnet]; + constructor( snapId: SnapId, messenger: MultichainAccountServiceMessenger, @@ -153,7 +155,6 @@ class MockSnapAccountProvider extends SnapAccountProvider { const DEFAULT_TEST_CONFIG: SnapAccountProviderConfig = { createAccounts: { timeoutMs: 5000, - batched: false, }, discovery: { timeoutMs: 2000, @@ -168,11 +169,13 @@ const setup = ({ messenger = getRootMessenger(), accounts = [], keyring: keyringOverrides = {}, + capabilities = { scopes: [] }, }: { config?: DeepPartial; messenger?: RootMessenger; accounts?: InternalAccount[]; keyring?: { type?: string; snapId?: SnapId }; + capabilities?: KeyringCapabilities; } = {}) => { const mocks = { AccountsController: { @@ -194,6 +197,7 @@ const setup = ({ }, SnapAccountService: { ensureReady: jest.fn(), + getCapabilities: jest.fn(), }, }; @@ -210,6 +214,12 @@ const setup = ({ // Make the platform ready right away (having a resolved promise is enough). mocks.SnapAccountService.ensureReady.mockResolvedValue(undefined); + messenger.registerActionHandler( + 'SnapAccountService:getCapabilities', + mocks.SnapAccountService.getCapabilities, + ); + mocks.SnapAccountService.getCapabilities.mockResolvedValue(capabilities); + messenger.registerActionHandler( 'SnapController:handleRequest', mocks.SnapController.handleRequest, diff --git a/packages/multichain-account-service/src/providers/SnapAccountProvider.ts b/packages/multichain-account-service/src/providers/SnapAccountProvider.ts index e387c23372..9cca0ea798 100644 --- a/packages/multichain-account-service/src/providers/SnapAccountProvider.ts +++ b/packages/multichain-account-service/src/providers/SnapAccountProvider.ts @@ -10,15 +10,17 @@ import { import type { CreateAccountBip44DeriveIndexOptions, CreateAccountBip44DeriveIndexRangeOptions, + CreateAccountBip44DiscoverOptions, CreateAccountOptions, EntropySourceId, KeyringAccount, } from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; -import { KeyringClient } from '@metamask/keyring-snap-client'; import type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk'; import { HandlerType } from '@metamask/snaps-utils'; +import type { CaipChainId } from '@metamask/utils'; import { Semaphore } from 'async-mutex'; import { @@ -30,7 +32,9 @@ import { reportError } from '../errors'; import { projectLogger as log, WARNING_PREFIX } from '../logger'; import type { MultichainAccountServiceMessenger } from '../types'; import { BaseBip44AccountProvider } from './BaseBip44AccountProvider'; -import { withTimeout } from './utils'; +import { createSnapKeyringClient } from './SnapKeyringClient'; +import type { Sender, SnapKeyringClient } from './SnapKeyringClient'; +import { withRetry, withTimeout } from './utils'; export type RestrictedSnapKeyring = { createAccount: SnapKeyringV2['createAccount']; @@ -47,18 +51,12 @@ export type SnapAccountProviderConfig = { backOffMs: number; }; createAccounts: { - /** - * Whether to enable account batching with `createAccounts` method. If `true`, accounts will - * be created in batch. - * - * NOTE: The Snap has to implement this optional method for batching support. - * Defaults to `false`. - */ - batched: boolean; /** * Timeout for account creation operations. * - * NOTE: The value might have to be adapted in case batching is enabled! + * NOTE: Batching (and thus whether a single call may create multiple + * accounts) is driven by the Snap's declared capabilities, not this config. + * The value might have to be adapted when the Snap supports batching. */ timeoutMs: number; }; @@ -77,12 +75,34 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { protected readonly config: SnapAccountProviderConfig; - readonly #client: KeyringClient; + /** + * The Snap's keyring capabilities, sourced from `SnapAccountService` (which + * reads them from the Snap's manifest). Populated the first time the client + * is resolved; defaults to an empty capability set until then. + */ + capabilities: KeyringCapabilities = { scopes: [] }; + + /** + * Version-agnostic keyring client, resolved lazily once the Snap is ready and + * its capabilities are known — see {@link SnapAccountProvider.withSnap}. + */ + #client?: SnapKeyringClient; + + readonly #sender: Sender; readonly #queue?: Semaphore; readonly #trace: TraceCallback; + /** + * Scopes passed to the v1 `discoverAccounts` client method. Only used on the + * v1 discovery path. + * + * TODO: Remove once all Snaps are fully v2 — discovery is then driven by the + * Snap's own supported scopes via `createAccounts({ bip44:discover })`. + */ + protected abstract readonly v1DiscoveryScopes: CaipChainId[]; + constructor( snapId: SnapId, messenger: MultichainAccountServiceMessenger, @@ -93,7 +113,7 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { super(messenger); this.snapId = snapId; - this.#client = this.#getKeyringClientFromSnapId(snapId); + this.#sender = this.#createSender(snapId); const maxConcurrency = config.maxConcurrency ?? Infinity; this.config = { @@ -179,8 +199,8 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { }; } - #getKeyringClientFromSnapId(snapId: string): KeyringClient { - return new KeyringClient({ + #createSender(snapId: string): Sender { + return { send: async (request: JsonRpcRequest): Promise => { const response = await this.messenger.call( 'SnapController:handleRequest', @@ -193,18 +213,51 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { ); return response as Json; }, - }); + }; + } + + /** + * Whether the Snap supports the v2 keyring protocol, inferred from its + * declared capabilities (a v2-capable Snap declares BIP-44 capabilities). + * + * @returns `true` if the Snap is v2-capable. + */ + protected isV2(): boolean { + return Boolean(this.capabilities.bip44); + } + + /** + * Resolves the version-agnostic keyring client, fetching the Snap's + * capabilities from `SnapAccountService` on first use and caching both the + * capabilities and the resulting client. + * + * Callers must ensure the Snap is ready (via + * {@link SnapAccountProvider.ensureReady}) beforehand so that the + * capabilities are reliably populated — {@link SnapAccountProvider.withSnap} + * guarantees this ordering. + * + * @returns The resolved {@link SnapKeyringClient}. + */ + async #resolveClient(): Promise { + if (!this.#client) { + this.capabilities = await this.messenger.call( + 'SnapAccountService:getCapabilities', + this.snapId, + ); + this.#client = createSnapKeyringClient(this.#sender, this.isV2()); + } + return this.#client; } async resyncAccounts( accounts: Bip44Account[], ): Promise { - await this.withSnap(async ({ keyring }) => { + await this.withSnap(async ({ client, keyring }) => { const localSnapAccounts = accounts.filter( (account) => account.metadata.snap?.id === this.snapId, ); const snapAccounts = new Set( - (await this.#client.listAccounts()).map((account) => account.id), + (await client.listAccounts()).map((account) => account.id), ); // NOTE: This should never happen, but if it does, we recover by deleting the @@ -226,7 +279,7 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { if (!localAccountIds.has(snapAccountId)) { // This account exists in the Snap but not in MetaMask, delete it from // the Snap. - await this.#client.deleteAccount(snapAccountId); + await client.deleteAccount(snapAccountId); // Update the local Set so subsequent checks use the correct size snapAccounts.delete(snapAccountId); } @@ -304,14 +357,15 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { protected async withSnap( operation: (snap: { - client: KeyringClient; + client: SnapKeyringClient; keyring: RestrictedSnapKeyring; }) => Promise, ): Promise { await this.ensureReady(); + const client = await this.#resolveClient(); return await operation({ - client: this.#client, + client, keyring: await this.#getRestrictedSnapKeyring(), }); } @@ -335,13 +389,17 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { keyring: RestrictedSnapKeyring, options: | CreateAccountBip44DeriveIndexOptions - | CreateAccountBip44DeriveIndexRangeOptions, + | CreateAccountBip44DeriveIndexRangeOptions + | CreateAccountBip44DiscoverOptions, ): Promise[]> { return this.withMaxConcurrency(async () => { let groupIndexOffset = 0; let snapAccounts: KeyringAccount[] = []; - const batched = this.config.createAccounts.batched ?? false; + // Batching is driven by the Snap's declared capabilities: a Snap that + // supports BIP-44 index-range derivation is v2-compliant and uses the + // batched `createAccounts` flow. + const batched = Boolean(this.capabilities.bip44?.deriveIndexRange); const { entropySource } = options; const createAccountV1 = async ( @@ -365,7 +423,8 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { const createAccountsV2 = async ( optionsV2: | CreateAccountBip44DeriveIndexOptions - | CreateAccountBip44DeriveIndexRangeOptions, + | CreateAccountBip44DeriveIndexRangeOptions + | CreateAccountBip44DiscoverOptions, ): Promise => await withTimeout( () => @@ -467,10 +526,85 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { this.accounts.delete(id); } - abstract discoverAccounts(options: { + /** + * Discovers accounts for the given entropy source and group index. + * + * v2 Snaps drive discovery through `createAccounts({ bip44:discover })`: the + * Snap checks for on-chain activity (using its own supported scopes) and + * returns the created account(s), or nothing once discovery is exhausted. + * + * v1 Snaps use the client's `discoverAccounts` to detect activity on + * {@link v1DiscoveryScopes}, then create the account for the group index. + * + * @param options - The discovery options. + * @param options.entropySource - The entropy source to discover accounts for. + * @param options.groupIndex - The group index to discover accounts for. + * @returns The discovered (and created) accounts, or an empty array when + * there is nothing to discover at this group index. + */ + async discoverAccounts({ + entropySource, + groupIndex, + }: { entropySource: EntropySourceId; groupIndex: number; - }): Promise[]>; + }): Promise[]> { + return this.withSnap(async ({ client, keyring }) => + this.trace( + { + name: TraceName.SnapDiscoverAccounts, + data: { + provider: this.getName(), + }, + }, + async () => { + if (!this.config.discovery.enabled) { + return []; + } + + if (this.isV2()) { + // v2: the Snap detects on-chain activity and creates the account in + // a single `createAccounts({ bip44:discover })` call. An empty + // result means discovery is exhausted at this group index. + return this.createBip44Accounts(keyring, { + type: AccountCreationType.Bip44Discover, + entropySource, + groupIndex, + }); + } + + // v1: detect activity via the client, then create the account for + // this group index. + const discoveredAccounts = await withRetry( + () => + withTimeout( + () => + client.discoverAccounts( + this.v1DiscoveryScopes, + entropySource, + groupIndex, + ), + this.config.discovery.timeoutMs, + ), + { + maxAttempts: this.config.discovery.maxAttempts, + backOffMs: this.config.discovery.backOffMs, + }, + ); + + if (!discoveredAccounts.length) { + return []; + } + + return this.createBip44Accounts(keyring, { + type: AccountCreationType.Bip44DeriveIndex, + entropySource, + groupIndex, + }); + }, + ), + ); + } } export const isSnapAccountProvider = ( diff --git a/packages/multichain-account-service/src/providers/SnapKeyringClient.test.ts b/packages/multichain-account-service/src/providers/SnapKeyringClient.test.ts new file mode 100644 index 0000000000..04945255fc --- /dev/null +++ b/packages/multichain-account-service/src/providers/SnapKeyringClient.test.ts @@ -0,0 +1,87 @@ +import { SolScope } from '@metamask/keyring-api'; + +import { createSnapKeyringClient } from './SnapKeyringClient'; +import type { Sender } from './SnapKeyringClient'; + +/** + * Builds a mock {@link Sender} whose `send` resolves to the given response. + * + * @param response - The value the sender should resolve to. + * @returns The mock sender and its `send` jest mock. + */ +function makeSender(response: unknown = []): { + sender: Sender; + send: jest.Mock; +} { + const send = jest.fn().mockResolvedValue(response); + return { sender: { send } as unknown as Sender, send }; +} + +describe('createSnapKeyringClient', () => { + describe('v1 client', () => { + it('lists accounts', async () => { + const { sender, send } = makeSender([]); + const client = createSnapKeyringClient(sender, false); + + expect(await client.listAccounts()).toStrictEqual([]); + expect(send).toHaveBeenCalledWith( + expect.objectContaining({ method: 'keyring_listAccounts' }), + ); + }); + + it('deletes an account', async () => { + const { sender, send } = makeSender(null); + const client = createSnapKeyringClient(sender, false); + + await client.deleteAccount('account-id'); + expect(send).toHaveBeenCalledWith( + expect.objectContaining({ method: 'keyring_deleteAccount' }), + ); + }); + + it('discovers accounts', async () => { + const { sender, send } = makeSender([]); + const client = createSnapKeyringClient(sender, false); + + expect( + await client.discoverAccounts([SolScope.Mainnet], 'entropy', 0), + ).toStrictEqual([]); + expect(send).toHaveBeenCalledWith( + expect.objectContaining({ method: 'keyring_discoverAccounts' }), + ); + }); + }); + + describe('v2 client', () => { + it('lists accounts via getAccounts', async () => { + const { sender, send } = makeSender([]); + const client = createSnapKeyringClient(sender, true); + + expect(await client.listAccounts()).toStrictEqual([]); + expect(send).toHaveBeenCalledWith( + expect.objectContaining({ method: 'keyring_getAccounts' }), + ); + }); + + it('deletes an account', async () => { + const { sender, send } = makeSender(null); + const client = createSnapKeyringClient(sender, true); + + await client.deleteAccount('account-id'); + expect(send).toHaveBeenCalledWith( + expect.objectContaining({ method: 'keyring_deleteAccount' }), + ); + }); + + it('throws for discoverAccounts (unsupported on v2)', async () => { + const { sender } = makeSender(); + const client = createSnapKeyringClient(sender, true); + + await expect( + client.discoverAccounts([SolScope.Mainnet], 'entropy', 0), + ).rejects.toThrow( + 'discoverAccounts is not supported on the v2 keyring client', + ); + }); + }); +}); diff --git a/packages/multichain-account-service/src/providers/SnapKeyringClient.ts b/packages/multichain-account-service/src/providers/SnapKeyringClient.ts new file mode 100644 index 0000000000..3b1d7e9a08 --- /dev/null +++ b/packages/multichain-account-service/src/providers/SnapKeyringClient.ts @@ -0,0 +1,88 @@ +import type { EntropySourceId, KeyringAccount } from '@metamask/keyring-api'; +import { KeyringClient } from '@metamask/keyring-snap-client'; +import { KeyringClient as KeyringClientV2 } from '@metamask/keyring-snap-client/v2'; +import type { CaipChainId } from '@metamask/utils'; + +/** + * Transport used by both the v1 and v2 keyring clients. Matches the object the + * {@link KeyringClient} constructor expects. + */ +export type Sender = ConstructorParameters[0]; + +type DiscoveredAccount = Awaited< + ReturnType +>[number]; + +/** + * Thin abstraction over the v1 and v2 keyring RPC clients so that provider call + * sites (account re-sync and v1 discovery) don't have to branch on the keyring + * protocol version. The v1 and v2 clients expose different method names (e.g. + * v1 `listAccounts` vs v2 `getAccounts`), and v2 has no `discoverAccounts` — v2 + * discovery flows through `createAccounts({ bip44:discover })` on the bridge + * keyring instead. + */ +export type SnapKeyringClient = { + /** + * Lists the accounts the Snap currently holds. + */ + listAccounts(): Promise; + + /** + * Deletes an account by id from the Snap. + * + * @param id - The id of the account to delete. + */ + deleteAccount(id: string): Promise; + + /** + * Discovers accounts for the given entropy source and group index. + * + * Only supported on the v1 client; the v2 client throws, since v2 discovery + * goes through `createAccounts({ bip44:discover })`. + * + * @param scopes - The scopes to discover accounts on. + * @param entropySource - The entropy source to discover accounts for. + * @param groupIndex - The group index to discover accounts for. + */ + discoverAccounts( + scopes: CaipChainId[], + entropySource: EntropySourceId, + groupIndex: number, + ): Promise; +}; + +/** + * Builds a version-agnostic {@link SnapKeyringClient} backed by either the v1 + * or v2 keyring client, based on the Snap's declared capabilities. + * + * @param sender - The transport used to talk to the Snap. + * @param isV2 - Whether to back the client with the v2 keyring client. + * @returns A {@link SnapKeyringClient}. + */ +export function createSnapKeyringClient( + sender: Sender, + isV2: boolean, +): SnapKeyringClient { + if (isV2) { + const client = new KeyringClientV2(sender); + return { + listAccounts: async () => client.getAccounts(), + deleteAccount: async (id) => client.deleteAccount(id), + discoverAccounts: async (): Promise => { + // v2 discovery is driven by `createAccounts({ bip44:discover })` through + // the bridge keyring, so the client is never used for discovery here. + throw new Error( + 'discoverAccounts is not supported on the v2 keyring client', + ); + }, + }; + } + + const client = new KeyringClient(sender); + return { + listAccounts: async () => client.listAccounts(), + deleteAccount: async (id) => client.deleteAccount(id), + discoverAccounts: async (scopes, entropySource, groupIndex) => + client.discoverAccounts(scopes, entropySource, groupIndex), + }; +} diff --git a/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts b/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts index b2c424af5f..575177c560 100644 --- a/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts @@ -1,5 +1,6 @@ import { isBip44Account } from '@metamask/account-api'; -import { AccountCreationType } from '@metamask/keyring-api'; +import { AccountCreationType, SolScope } from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { SnapControllerState } from '@metamask/snaps-controllers'; @@ -34,6 +35,19 @@ function asConfig( ) as SnapAccountProviderConfig; } +/** + * v2 capabilities as declared by a fully v2-compliant Solana Snap manifest. + * Drives the batched `createAccounts` flow and the v2 discovery path. + */ +const SOL_V2_CAPABILITIES: KeyringCapabilities = { + scopes: [SolScope.Mainnet], + bip44: { + deriveIndex: true, + deriveIndexRange: true, + discover: true, + }, +}; + class MockSolanaKeyring { readonly type = 'MockSolanaKeyring'; @@ -129,16 +143,19 @@ class MockSolAccountProvider extends SolAccountProvider { * @param options.messenger - An optional messenger instance to use. Defaults to a new Messenger. * @param options.accounts - List of accounts to use. * @param options.config - Provider config. + * @param options.capabilities - The Snap keyring capabilities to expose via `SnapAccountService:getCapabilities`. * @returns An object containing the controller instance and the messenger. */ function setup({ messenger = getRootMessenger(), accounts = [], config, + capabilities = { scopes: [] }, }: { messenger?: RootMessenger; accounts?: InternalAccount[]; config?: SnapAccountProviderConfig; + capabilities?: KeyringCapabilities; } = {}): { provider: AccountProviderWrapper; messenger: RootMessenger; @@ -164,6 +181,11 @@ function setup({ () => ({ isReady: true }) as SnapControllerState, ); + messenger.registerActionHandler( + 'SnapAccountService:getCapabilities', + async () => capabilities, + ); + messenger.registerActionHandler( 'AccountsController:listMultichainAccounts', () => accounts, @@ -283,8 +305,8 @@ describe('SolAccountProvider', () => { const accounts = [MOCK_SOL_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - // Force v1 by not providing the config at all, so it relies on the default value. - config: asConfig({ createAccounts: { batched: undefined } }), + // No capabilities provided → defaults to an empty capability set, so + // the provider falls back to the v1 (non-batched) flow. }); await provider.createAccounts({ @@ -444,7 +466,7 @@ describe('SolAccountProvider', () => { const accounts = [MOCK_SOL_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: SOL_V2_CAPABILITIES, }); const newGroupIndex = accounts.length; // Group-index are 0-based. @@ -467,7 +489,7 @@ describe('SolAccountProvider', () => { const accounts = [MOCK_SOL_ACCOUNT_1]; const { provider } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: SOL_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -483,7 +505,7 @@ describe('SolAccountProvider', () => { const accounts = [MOCK_SOL_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: SOL_V2_CAPABILITIES, }); const from = 1; @@ -508,7 +530,7 @@ describe('SolAccountProvider', () => { it('creates accounts with range starting from 0', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: SOL_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -525,7 +547,7 @@ describe('SolAccountProvider', () => { it('creates a single account when range from equals to', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: SOL_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -546,7 +568,7 @@ describe('SolAccountProvider', () => { it('throws if the account creation process takes too long', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: SOL_V2_CAPABILITIES, }); mocks.keyring.createAccounts.mockImplementation( diff --git a/packages/multichain-account-service/src/providers/SolAccountProvider.ts b/packages/multichain-account-service/src/providers/SolAccountProvider.ts index c5f1af5977..a228ee38da 100644 --- a/packages/multichain-account-service/src/providers/SolAccountProvider.ts +++ b/packages/multichain-account-service/src/providers/SolAccountProvider.ts @@ -3,25 +3,22 @@ import type { Bip44Account } from '@metamask/account-api'; import type { TraceCallback } from '@metamask/controller-utils'; import type { EntropySourceId, KeyringAccount } from '@metamask/keyring-api'; import { - AccountCreationType, KeyringAccountEntropyTypeOption, SolAccountType, SolScope, } from '@metamask/keyring-api'; -import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { SnapId } from '@metamask/snaps-sdk'; +import type { CaipChainId } from '@metamask/utils'; import { traceFallback } from '../analytics'; -import { TraceName } from '../analytics/traces'; import type { MultichainAccountServiceMessenger } from '../types'; import { SnapAccountProvider } from './SnapAccountProvider'; import type { RestrictedSnapKeyring, SnapAccountProviderConfig, } from './SnapAccountProvider'; -import { withRetry, withTimeout } from './utils'; export type SolAccountProviderConfig = SnapAccountProviderConfig; @@ -36,7 +33,6 @@ export const SOL_ACCOUNT_PROVIDER_DEFAULT_CONFIG: SnapAccountProviderConfig = { backOffMs: 1000, }, createAccounts: { - batched: false, // For now, the Snap is not fully v2 compliant. timeoutMs: 3000, }, resyncAccounts: { @@ -49,13 +45,9 @@ export class SolAccountProvider extends SnapAccountProvider { static SOLANA_SNAP_ID = 'npm:@metamask/solana-wallet-snap' as SnapId; - readonly capabilities: KeyringCapabilities = { - scopes: [SolScope.Mainnet, SolScope.Devnet, SolScope.Testnet], - bip44: { - deriveIndex: true, - deriveIndexRange: true, - }, - }; + // TODO: Remove once the Snap is fully v2 — discovery is then driven by the + // Snap's own supported scopes via `createAccounts({ bip44:discover })`. + protected readonly v1DiscoveryScopes: CaipChainId[] = [SolScope.Mainnet]; constructor( messenger: MultichainAccountServiceMessenger, @@ -112,58 +104,4 @@ export class SolAccountProvider extends SnapAccountProvider { return account; } - - async discoverAccounts({ - entropySource, - groupIndex, - }: { - entropySource: EntropySourceId; - groupIndex: number; - }): Promise[]> { - return this.withSnap(async ({ client, keyring }) => { - return await super.trace( - { - name: TraceName.SnapDiscoverAccounts, - data: { - provider: this.getName(), - }, - }, - async () => { - if (!this.config.discovery.enabled) { - return []; - } - - const discoveredAccounts = await withRetry( - () => - withTimeout( - () => - client.discoverAccounts( - [SolScope.Mainnet], - entropySource, - groupIndex, - ), - this.config.discovery.timeoutMs, - ), - { - maxAttempts: this.config.discovery.maxAttempts, - backOffMs: this.config.discovery.backOffMs, - }, - ); - - if (!discoveredAccounts.length) { - return []; - } - - // NOTE: We know the Solana Snap only return 1 account per group index during discovery. Also, - // we do not use the returned `derivationPath` on purpose. Instead we just create the account - // for this group index and that's all. - return await this.createBip44Accounts(keyring, { - type: AccountCreationType.Bip44DeriveIndex, - entropySource, - groupIndex, - }); - }, - ); - }); - } } diff --git a/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts index d8b617ad1e..b967a07a81 100644 --- a/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts @@ -1,5 +1,6 @@ import { isBip44Account } from '@metamask/account-api'; -import { AccountCreationType } from '@metamask/keyring-api'; +import { AccountCreationType, TrxScope } from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { SnapControllerState } from '@metamask/snaps-controllers'; @@ -34,6 +35,19 @@ function asConfig( ) as SnapAccountProviderConfig; } +/** + * v2 capabilities as declared by a fully v2-compliant Tron Snap manifest. + * Drives the batched `createAccounts` flow and the v2 discovery path. + */ +const TRX_V2_CAPABILITIES: KeyringCapabilities = { + scopes: [TrxScope.Mainnet], + bip44: { + deriveIndex: true, + deriveIndexRange: true, + discover: true, + }, +}; + class MockTronKeyring { readonly type = 'MockTronKeyring'; @@ -118,16 +132,19 @@ class MockTrxAccountProvider extends TrxAccountProvider { * @param options.messenger - An optional messenger instance to use. Defaults to a new Messenger. * @param options.accounts - List of accounts to use. * @param options.config - Provider config. + * @param options.capabilities - The Snap keyring capabilities to expose via `SnapAccountService:getCapabilities`. * @returns An object containing the controller instance and the messenger. */ function setup({ messenger = getRootMessenger(), accounts = [], config, + capabilities = { scopes: [] }, }: { messenger?: RootMessenger; accounts?: InternalAccount[]; config?: SnapAccountProviderConfig; + capabilities?: KeyringCapabilities; } = {}): { provider: AccountProviderWrapper; messenger: RootMessenger; @@ -154,6 +171,11 @@ function setup({ () => ({ isReady: true }) as SnapControllerState, ); + messenger.registerActionHandler( + 'SnapAccountService:getCapabilities', + async () => capabilities, + ); + messenger.registerActionHandler( 'AccountsController:listMultichainAccounts', () => accounts, @@ -279,8 +301,8 @@ describe('TrxAccountProvider', () => { const accounts = [MOCK_TRX_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - // Force v1 by not providing the config at all, so it relies on the default value. - config: asConfig({ createAccounts: { batched: undefined } }), + // No capabilities provided → defaults to an empty capability set, so + // the provider falls back to the v1 (non-batched) flow. }); await provider.createAccounts({ @@ -445,7 +467,7 @@ describe('TrxAccountProvider', () => { const accounts = [MOCK_TRX_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: TRX_V2_CAPABILITIES, }); const newGroupIndex = accounts.length; // Group-index are 0-based. @@ -468,7 +490,7 @@ describe('TrxAccountProvider', () => { const accounts = [MOCK_TRX_ACCOUNT_1]; const { provider } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: TRX_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -484,7 +506,7 @@ describe('TrxAccountProvider', () => { const accounts = [MOCK_TRX_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: TRX_V2_CAPABILITIES, }); const from = 1; @@ -509,7 +531,7 @@ describe('TrxAccountProvider', () => { it('creates accounts with range starting from 0', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: TRX_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -526,7 +548,7 @@ describe('TrxAccountProvider', () => { it('creates a single account when range from equals to', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: TRX_V2_CAPABILITIES, }); const newAccounts = await provider.createAccounts({ @@ -547,7 +569,7 @@ describe('TrxAccountProvider', () => { it('throws if the account creation process takes too long', async () => { const { provider, mocks } = setup({ accounts: [], - config: asConfig({ createAccounts: { batched: true } }), + capabilities: TRX_V2_CAPABILITIES, }); mocks.keyring.createAccounts.mockImplementation( diff --git a/packages/multichain-account-service/src/providers/TrxAccountProvider.ts b/packages/multichain-account-service/src/providers/TrxAccountProvider.ts index 9c3ac48be5..07574a82d6 100644 --- a/packages/multichain-account-service/src/providers/TrxAccountProvider.ts +++ b/packages/multichain-account-service/src/providers/TrxAccountProvider.ts @@ -1,25 +1,19 @@ import type { Bip44Account } from '@metamask/account-api'; import type { TraceCallback } from '@metamask/controller-utils'; import type { EntropySourceId, KeyringAccount } from '@metamask/keyring-api'; -import { - AccountCreationType, - TrxAccountType, - TrxScope, -} from '@metamask/keyring-api'; -import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; +import { TrxAccountType, TrxScope } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { SnapId } from '@metamask/snaps-sdk'; +import type { CaipChainId } from '@metamask/utils'; import { traceFallback } from '../analytics'; -import { TraceName } from '../analytics/traces'; import type { MultichainAccountServiceMessenger } from '../types'; import { SnapAccountProvider } from './SnapAccountProvider'; import type { RestrictedSnapKeyring, SnapAccountProviderConfig, } from './SnapAccountProvider'; -import { withRetry, withTimeout } from './utils'; export type TrxAccountProviderConfig = SnapAccountProviderConfig; @@ -34,7 +28,6 @@ export const TRX_ACCOUNT_PROVIDER_DEFAULT_CONFIG: TrxAccountProviderConfig = { backOffMs: 1000, }, createAccounts: { - batched: false, // For now, the Snap is not fully v2 compliant. timeoutMs: 3000, }, resyncAccounts: { @@ -47,13 +40,9 @@ export class TrxAccountProvider extends SnapAccountProvider { static TRX_SNAP_ID = 'npm:@metamask/tron-wallet-snap' as SnapId; - readonly capabilities: KeyringCapabilities = { - scopes: [TrxScope.Mainnet, TrxScope.Shasta], - bip44: { - deriveIndex: true, - deriveIndexRange: true, - }, - }; + // TODO: Remove once the Snap is fully v2 — discovery is then driven by the + // Snap's own supported scopes via `createAccounts({ bip44:discover })`. + protected readonly v1DiscoveryScopes: CaipChainId[] = [TrxScope.Mainnet]; constructor( messenger: MultichainAccountServiceMessenger, @@ -88,55 +77,4 @@ export class TrxAccountProvider extends SnapAccountProvider { scope: TrxScope.Mainnet, }); } - - async discoverAccounts({ - entropySource, - groupIndex, - }: { - entropySource: EntropySourceId; - groupIndex: number; - }): Promise[]> { - return this.withSnap(async ({ client, keyring }) => { - return await super.trace( - { - name: TraceName.SnapDiscoverAccounts, - data: { - provider: this.getName(), - }, - }, - async () => { - if (!this.config.discovery.enabled) { - return []; - } - - const discoveredAccounts = await withRetry( - () => - withTimeout( - () => - client.discoverAccounts( - [TrxScope.Mainnet], - entropySource, - groupIndex, - ), - this.config.discovery.timeoutMs, - ), - { - maxAttempts: this.config.discovery.maxAttempts, - backOffMs: this.config.discovery.backOffMs, - }, - ); - - if (!discoveredAccounts.length) { - return []; - } - - return await this.createBip44Accounts(keyring, { - type: AccountCreationType.Bip44DeriveIndex, - entropySource, - groupIndex, - }); - }, - ); - }); - } } diff --git a/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts b/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts index 50223da501..c7dc3830f4 100644 --- a/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts @@ -1,6 +1,7 @@ import { isBip44Account } from '@metamask/account-api'; import type { SnapKeyring } from '@metamask/eth-snap-keyring'; -import { AccountCreationType } from '@metamask/keyring-api'; +import { AccountCreationType, XlmScope } from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { SnapControllerState } from '@metamask/snaps-controllers'; @@ -35,6 +36,19 @@ function asConfig( ) as SnapAccountProviderConfig; } +/** + * v2 capabilities as declared by a fully v2-compliant Stellar Snap manifest. + * Drives the batched `createAccounts` flow and the v2 discovery path. + */ +const XLM_V2_CAPABILITIES: KeyringCapabilities = { + scopes: [XlmScope.Pubnet], + bip44: { + deriveIndex: true, + deriveIndexRange: true, + discover: true, + }, +}; + class MockStellarKeyring { readonly type = 'MockStellarKeyring'; @@ -81,9 +95,9 @@ class MockStellarKeyring { .fn() .mockImplementation((options) => { const groupIndices = - options.type === 'bip44:derive-index' - ? [options.groupIndex] - : toGroupIndexRangeArray(options.range); + options.type === 'bip44:derive-index-range' + ? toGroupIndexRangeArray(options.range) + : [options.groupIndex]; return groupIndices.map((groupIndex) => { const found = this.accounts.find( @@ -119,10 +133,12 @@ function setup({ messenger = getRootMessenger(), accounts = [], config, + capabilities = { scopes: [] }, }: { messenger?: RootMessenger; accounts?: InternalAccount[]; config?: SnapAccountProviderConfig; + capabilities?: KeyringCapabilities; } = {}): { provider: AccountProviderWrapper; messenger: RootMessenger; @@ -149,6 +165,11 @@ function setup({ () => ({ isReady: true }) as SnapControllerState, ); + messenger.registerActionHandler( + 'SnapAccountService:getCapabilities', + async () => capabilities, + ); + messenger.registerActionHandler( 'AccountsController:listMultichainAccounts', () => accounts, @@ -247,6 +268,7 @@ describe('XlmAccountProvider', () => { it('returns existing account if it already exists at index', async () => { const { provider, mocks } = setup({ accounts: [MOCK_XLM_ACCOUNT_1], + capabilities: XLM_V2_CAPABILITIES, }); mocks.keyring.discoverAccounts.mockResolvedValue([ @@ -297,9 +319,10 @@ describe('XlmAccountProvider', () => { describe('v1', () => { it('uses createAccount when batching is disabled', async () => { const accounts = [MOCK_XLM_ACCOUNT_1]; + // No capabilities provided → empty capability set, so batching is + // disabled and the provider falls back to the v1 flow. const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: false } }), }); await provider.createAccounts({ @@ -318,7 +341,7 @@ describe('XlmAccountProvider', () => { const accounts = [MOCK_XLM_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: XLM_V2_CAPABILITIES, }); const newGroupIndex = accounts.length; @@ -341,7 +364,7 @@ describe('XlmAccountProvider', () => { const accounts = [MOCK_XLM_ACCOUNT_1]; const { provider, mocks } = setup({ accounts, - config: asConfig({ createAccounts: { batched: true } }), + capabilities: XLM_V2_CAPABILITIES, }); const from = 1; diff --git a/packages/multichain-account-service/src/providers/XlmAccountProvider.ts b/packages/multichain-account-service/src/providers/XlmAccountProvider.ts index 5d169cf1ad..516637858e 100644 --- a/packages/multichain-account-service/src/providers/XlmAccountProvider.ts +++ b/packages/multichain-account-service/src/providers/XlmAccountProvider.ts @@ -1,25 +1,19 @@ import type { Bip44Account } from '@metamask/account-api'; import type { TraceCallback } from '@metamask/controller-utils'; import type { EntropySourceId, KeyringAccount } from '@metamask/keyring-api'; -import { - AccountCreationType, - XlmAccountType, - XlmScope, -} from '@metamask/keyring-api'; -import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; +import { XlmAccountType, XlmScope } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { SnapId } from '@metamask/snaps-sdk'; +import type { CaipChainId } from '@metamask/utils'; import { traceFallback } from '../analytics'; -import { TraceName } from '../analytics/traces'; import type { MultichainAccountServiceMessenger } from '../types'; import { SnapAccountProvider } from './SnapAccountProvider'; import type { RestrictedSnapKeyring, SnapAccountProviderConfig, } from './SnapAccountProvider'; -import { withRetry, withTimeout } from './utils'; export type XlmAccountProviderConfig = SnapAccountProviderConfig; @@ -34,7 +28,6 @@ export const XLM_ACCOUNT_PROVIDER_DEFAULT_CONFIG: XlmAccountProviderConfig = { backOffMs: 1000, }, createAccounts: { - batched: true, timeoutMs: 10000, }, resyncAccounts: { @@ -47,13 +40,9 @@ export class XlmAccountProvider extends SnapAccountProvider { static XLM_SNAP_ID = 'npm:@metamask/stellar-wallet-snap' as SnapId; - readonly capabilities: KeyringCapabilities = { - scopes: [XlmScope.Pubnet, XlmScope.Testnet], - bip44: { - deriveIndex: true, - deriveIndexRange: true, - }, - }; + // TODO: Remove once the Snap is fully v2 — discovery is then driven by the + // Snap's own supported scopes via `createAccounts({ bip44:discover })`. + protected readonly v1DiscoveryScopes: CaipChainId[] = [XlmScope.Pubnet]; constructor( messenger: MultichainAccountServiceMessenger, @@ -88,55 +77,4 @@ export class XlmAccountProvider extends SnapAccountProvider { scope: XlmScope.Pubnet, }); } - - async discoverAccounts({ - entropySource, - groupIndex, - }: { - entropySource: EntropySourceId; - groupIndex: number; - }): Promise[]> { - return this.withSnap(async ({ client, keyring }) => { - return await super.trace( - { - name: TraceName.SnapDiscoverAccounts, - data: { - provider: this.getName(), - }, - }, - async () => { - if (!this.config.discovery.enabled) { - return []; - } - - const discoveredAccounts = await withRetry( - () => - withTimeout( - () => - client.discoverAccounts( - [XlmScope.Pubnet], - entropySource, - groupIndex, - ), - this.config.discovery.timeoutMs, - ), - { - maxAttempts: this.config.discovery.maxAttempts, - backOffMs: this.config.discovery.backOffMs, - }, - ); - - if (!discoveredAccounts.length) { - return []; - } - - return await this.createBip44Accounts(keyring, { - type: AccountCreationType.Bip44DeriveIndex, - entropySource, - groupIndex, - }); - }, - ); - }); - } } diff --git a/packages/multichain-account-service/src/tests/messenger.ts b/packages/multichain-account-service/src/tests/messenger.ts index 5c7cbca729..a50cdda77d 100644 --- a/packages/multichain-account-service/src/tests/messenger.ts +++ b/packages/multichain-account-service/src/tests/messenger.ts @@ -75,6 +75,7 @@ export function getMultichainAccountServiceMessenger( 'AccountsController:getAccounts', 'KeyringController:removeAccount', 'SnapAccountService:ensureReady', + 'SnapAccountService:getCapabilities', ...(extra?.actions ?? []), ], events: [ diff --git a/packages/multichain-account-service/src/types.ts b/packages/multichain-account-service/src/types.ts index b640f49757..5d86323ca8 100644 --- a/packages/multichain-account-service/src/types.ts +++ b/packages/multichain-account-service/src/types.ts @@ -29,7 +29,10 @@ import type { NetworkControllerFindNetworkClientIdByChainIdAction, NetworkControllerGetNetworkClientByIdAction, } from '@metamask/network-controller'; -import type { SnapAccountServiceEnsureReadyAction } from '@metamask/snap-account-service'; +import type { + SnapAccountServiceEnsureReadyAction, + SnapAccountServiceGetCapabilitiesAction, +} from '@metamask/snap-account-service'; import type { SnapControllerHandleRequestAction } from '@metamask/snaps-controllers'; import type { serviceName } from './MultichainAccountService'; @@ -86,7 +89,8 @@ type AllowedActions = | KeyringControllerCreateNewVaultAndRestoreAction | KeyringControllerRemoveAccountAction | SnapControllerHandleRequestAction - | SnapAccountServiceEnsureReadyAction; + | SnapAccountServiceEnsureReadyAction + | SnapAccountServiceGetCapabilitiesAction; /** * All events published by other modules that {@link MultichainAccountService} From 4076456e06777d57cca3b79236ea504c76e59ca4 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 17:09:05 +0200 Subject: [PATCH 4/9] fix(multichain-account-service): always batch v2 account creation and gate discovery on bip44.discover --- .../multichain-account-service/CHANGELOG.md | 4 ++-- .../src/providers/SnapAccountProvider.ts | 15 +++++++++++---- .../src/providers/SolAccountProvider.test.ts | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/multichain-account-service/CHANGELOG.md b/packages/multichain-account-service/CHANGELOG.md index e9809548aa..f0e0d73b65 100644 --- a/packages/multichain-account-service/CHANGELOG.md +++ b/packages/multichain-account-service/CHANGELOG.md @@ -11,12 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Capability-gated v1/v2 keyring client selection for Snap account providers ([#0000](https://github.com/MetaMask/core/pull/0000)) - Providers resolve a Snap's capabilities via `SnapAccountService:getCapabilities` and route account creation and discovery through the v1 or v2 keyring client accordingly (a Snap that declares BIP-44 capabilities is treated as v2). - - Account discovery for v2 Snaps flows through `createAccounts({ bip44:discover })`; v1 Snaps keep using the `discoverAccounts` client method. + - Account discovery for v2 Snaps that declare `bip44.discover` flows through `createAccounts({ bip44:discover })`; v1 Snaps keep using the `discoverAccounts` client method. ### Changed - **BREAKING:** Remove `batched` from `SnapAccountProviderConfig['createAccounts']` ([#0000](https://github.com/MetaMask/core/pull/0000)) - - Whether accounts are created in a single batched `createAccounts` call is now derived from the Snap's `bip44.deriveIndexRange` capability instead of static config. + - Batching is now derived from the Snap's capabilities (v2 Snaps that declare `bip44` use the `createAccounts` flow, since they expose no singular `createAccount`) instead of static config. - Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) - Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) - Bump `@metamask/keyring-snap-client` from `^9.0.2` to `^9.1.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) diff --git a/packages/multichain-account-service/src/providers/SnapAccountProvider.ts b/packages/multichain-account-service/src/providers/SnapAccountProvider.ts index 9cca0ea798..889ac80304 100644 --- a/packages/multichain-account-service/src/providers/SnapAccountProvider.ts +++ b/packages/multichain-account-service/src/providers/SnapAccountProvider.ts @@ -396,10 +396,10 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { let groupIndexOffset = 0; let snapAccounts: KeyringAccount[] = []; - // Batching is driven by the Snap's declared capabilities: a Snap that - // supports BIP-44 index-range derivation is v2-compliant and uses the - // batched `createAccounts` flow. - const batched = Boolean(this.capabilities.bip44?.deriveIndexRange); + // v2 Snaps expose `createAccounts` but no singular `createAccount`, so + // they must always go through the batched flow. v1 Snaps only have the + // singular `createAccount`. + const batched = this.isV2(); const { entropySource } = options; const createAccountV1 = async ( @@ -563,6 +563,13 @@ export abstract class SnapAccountProvider extends BaseBip44AccountProvider { } if (this.isV2()) { + // The v2 client has no `discoverAccounts`, so discovery is only + // possible when the Snap supports `bip44:discover`. Otherwise there + // is no way to discover and we report nothing. + if (!this.capabilities.bip44?.discover) { + return []; + } + // v2: the Snap detects on-chain activity and creates the account in // a single `createAccounts({ bip44:discover })` call. An empty // result means discovery is exhausted at this group index. diff --git a/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts b/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts index 575177c560..45b377cccc 100644 --- a/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts @@ -634,6 +634,25 @@ describe('SolAccountProvider', () => { expect(discovered).toStrictEqual([]); }); + it('returns no accounts when a v2 Snap does not support bip44:discover', async () => { + const { provider, mocks } = setup({ + accounts: [], + capabilities: { + scopes: [SolScope.Mainnet], + bip44: { deriveIndex: true, deriveIndexRange: true }, + }, + }); + + const discovered = await provider.discoverAccounts({ + entropySource: MOCK_HD_KEYRING_1.metadata.id, + groupIndex: 0, + }); + + expect(discovered).toStrictEqual([]); + expect(mocks.keyring.createAccounts).not.toHaveBeenCalled(); + expect(mocks.keyring.createAccount).not.toHaveBeenCalled(); + }); + it('does not run discovery if disabled', async () => { const { provider } = setup({ accounts: [MOCK_SOL_ACCOUNT_1], From 77841626d41f66e8ad8f8378dd5b50a4737b2c92 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 17:13:13 +0200 Subject: [PATCH 5/9] fix: correct @metamask/snaps-sdk lockfile checksum --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 86085a10d1..2fa8650624 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8685,7 +8685,7 @@ __metadata: "@metamask/superstruct": "npm:^3.2.1" "@metamask/utils": "npm:^11.11.0" luxon: "npm:^3.5.0" - checksum: 10/138c616584d537b9976ae48123090ab5731848d79d5d1f4e979c797dfdfe061329cbf18a5e84d8bd068fe36d5b9d169337f6d74efab0736f30c31ddf4088f70b + checksum: 10/8419625775b83045d98c4ed920002ad884471e2303f6650104c97457c238f095ff35030663ddf111ebb6ce8f4cd915f57b7b9a5597d00288093701dbb79e787c languageName: node linkType: hard From 90873eba3470675985d3ba5773cee77e498d05df Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 17:15:01 +0200 Subject: [PATCH 6/9] docs: set changelog PR references to #9377 --- packages/multichain-account-service/CHANGELOG.md | 10 +++++----- packages/snap-account-service/CHANGELOG.md | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/multichain-account-service/CHANGELOG.md b/packages/multichain-account-service/CHANGELOG.md index f0e0d73b65..bef58d928e 100644 --- a/packages/multichain-account-service/CHANGELOG.md +++ b/packages/multichain-account-service/CHANGELOG.md @@ -9,17 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Capability-gated v1/v2 keyring client selection for Snap account providers ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Capability-gated v1/v2 keyring client selection for Snap account providers ([#9377](https://github.com/MetaMask/core/pull/9377)) - Providers resolve a Snap's capabilities via `SnapAccountService:getCapabilities` and route account creation and discovery through the v1 or v2 keyring client accordingly (a Snap that declares BIP-44 capabilities is treated as v2). - Account discovery for v2 Snaps that declare `bip44.discover` flows through `createAccounts({ bip44:discover })`; v1 Snaps keep using the `discoverAccounts` client method. ### Changed -- **BREAKING:** Remove `batched` from `SnapAccountProviderConfig['createAccounts']` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- **BREAKING:** Remove `batched` from `SnapAccountProviderConfig['createAccounts']` ([#9377](https://github.com/MetaMask/core/pull/9377)) - Batching is now derived from the Snap's capabilities (v2 Snaps that declare `bip44` use the `createAccounts` flow, since they expose no singular `createAccount`) instead of static config. -- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) -- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) -- Bump `@metamask/keyring-snap-client` from `^9.0.2` to `^9.1.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-snap-client` from `^9.0.2` to `^9.1.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) - Adds `MultichainAccountGroup.isProviderAligned(provider)` to check alignment per provider ([#9269](https://github.com/MetaMask/core/pull/9269)) - Wallet alignment now only creates the missing `(provider, group index)` pairs instead of re-creating the whole range for every provider, avoiding redundant `createAccounts` calls (and their traces) ([#9269](https://github.com/MetaMask/core/pull/9269)) - Bump `@metamask/accounts-controller` from `^39.0.3` to `^39.0.4` ([#9349](https://github.com/MetaMask/core/pull/9349)) diff --git a/packages/snap-account-service/CHANGELOG.md b/packages/snap-account-service/CHANGELOG.md index 6dec8269a4..30674c5109 100644 --- a/packages/snap-account-service/CHANGELOG.md +++ b/packages/snap-account-service/CHANGELOG.md @@ -9,16 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `getCapabilities` action to expose a Snap's keyring capabilities ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Add `getCapabilities` action to expose a Snap's keyring capabilities ([#9377](https://github.com/MetaMask/core/pull/9377)) - Reads the capabilities the bridge keyring populated from the Snap's manifest, letting consumers decide whether to drive the Snap through the v1 or v2 keyring path. ### Changed -- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) - Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) -- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) - Bump `@metamask/keyring-snap-sdk` from `^9.0.1` to `^9.0.2` ([#9249](https://github.com/MetaMask/core/pull/9249)) -- Bump `@metamask/keyring-snap-sdk` from `^9.0.2` to `^9.1.0` ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Bump `@metamask/keyring-snap-sdk` from `^9.0.2` to `^9.1.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) ## [1.0.0] From 9da0c966876702c7ba1127da12ae61ed2682ee28 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 17:33:17 +0200 Subject: [PATCH 7/9] chore(deps): align keyring-api, eth-snap-keyring and keyring-snap-client ranges across the monorepo --- packages/account-tree-controller/package.json | 2 +- packages/accounts-controller/package.json | 4 +- packages/assets-controller/package.json | 4 +- packages/assets-controllers/package.json | 4 +- packages/bridge-controller/package.json | 2 +- packages/earn-controller/package.json | 2 +- packages/keyring-controller/package.json | 2 +- .../money-account-controller/package.json | 2 +- .../package.json | 2 +- .../package.json | 4 +- .../package.json | 2 +- packages/profile-sync-controller/package.json | 2 +- yarn.lock | 105 +++--------------- 13 files changed, 33 insertions(+), 104 deletions(-) diff --git a/packages/account-tree-controller/package.json b/packages/account-tree-controller/package.json index 2912ce27f6..1333f80343 100644 --- a/packages/account-tree-controller/package.json +++ b/packages/account-tree-controller/package.json @@ -55,7 +55,7 @@ "dependencies": { "@metamask/accounts-controller": "^39.0.4", "@metamask/base-controller": "^9.1.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^1.2.0", "@metamask/multichain-account-service": "^11.1.0", diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index ba8bf0161e..9def250f87 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -55,8 +55,8 @@ "dependencies": { "@ethereumjs/util": "^9.1.0", "@metamask/base-controller": "^9.1.0", - "@metamask/eth-snap-keyring": "^22.3.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/eth-snap-keyring": "^22.4.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/keyring-internal-api": "^11.0.1", "@metamask/keyring-sdk": "^2.2.0", diff --git a/packages/assets-controller/package.json b/packages/assets-controller/package.json index f929172520..548df07c89 100644 --- a/packages/assets-controller/package.json +++ b/packages/assets-controller/package.json @@ -63,10 +63,10 @@ "@metamask/client-controller": "^1.0.1", "@metamask/controller-utils": "^12.3.0", "@metamask/core-backend": "^6.5.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/keyring-internal-api": "^11.0.1", - "@metamask/keyring-snap-client": "^9.0.2", + "@metamask/keyring-snap-client": "^9.1.0", "@metamask/messenger": "^1.2.0", "@metamask/network-controller": "^34.0.0", "@metamask/network-enablement-controller": "^5.4.1", diff --git a/packages/assets-controllers/package.json b/packages/assets-controllers/package.json index 5947c7b5b3..03a568e7ba 100644 --- a/packages/assets-controllers/package.json +++ b/packages/assets-controllers/package.json @@ -68,7 +68,7 @@ "@metamask/controller-utils": "^12.3.0", "@metamask/core-backend": "^6.5.0", "@metamask/eth-query": "^4.0.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^1.2.0", "@metamask/metamask-eth-abis": "^3.1.1", @@ -106,7 +106,7 @@ "@metamask/auto-changelog": "^6.1.0", "@metamask/ethjs-provider-http": "^0.3.0", "@metamask/keyring-internal-api": "^11.0.1", - "@metamask/keyring-snap-client": "^9.0.2", + "@metamask/keyring-snap-client": "^9.1.0", "@metamask/providers": "^22.1.0", "@ts-bridge/cli": "^0.6.4", "@types/jest": "^29.5.14", diff --git a/packages/bridge-controller/package.json b/packages/bridge-controller/package.json index 64e39cdf86..f00b3b75cf 100644 --- a/packages/bridge-controller/package.json +++ b/packages/bridge-controller/package.json @@ -63,7 +63,7 @@ "@metamask/base-controller": "^9.1.0", "@metamask/controller-utils": "^12.3.0", "@metamask/gas-fee-controller": "^26.2.4", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/messenger": "^1.2.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/multichain-network-controller": "^3.2.1", diff --git a/packages/earn-controller/package.json b/packages/earn-controller/package.json index 72db79dde9..58464e71b1 100644 --- a/packages/earn-controller/package.json +++ b/packages/earn-controller/package.json @@ -58,7 +58,7 @@ "@metamask/account-tree-controller": "^7.5.3", "@metamask/base-controller": "^9.1.0", "@metamask/controller-utils": "^12.3.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/messenger": "^1.2.0", "@metamask/network-controller": "^34.0.0", "@metamask/stake-sdk": "^3.2.1", diff --git a/packages/keyring-controller/package.json b/packages/keyring-controller/package.json index 1eb3be2a6c..c01ec5345a 100644 --- a/packages/keyring-controller/package.json +++ b/packages/keyring-controller/package.json @@ -59,7 +59,7 @@ "@metamask/eth-hd-keyring": "^14.1.1", "@metamask/eth-sig-util": "^8.2.0", "@metamask/eth-simple-keyring": "^12.0.2", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-internal-api": "^11.0.1", "@metamask/messenger": "^1.2.0", "@metamask/utils": "^11.11.0", diff --git a/packages/money-account-controller/package.json b/packages/money-account-controller/package.json index b0cde52ce1..0b3a6d458b 100644 --- a/packages/money-account-controller/package.json +++ b/packages/money-account-controller/package.json @@ -56,7 +56,7 @@ "@metamask/accounts-controller": "^39.0.4", "@metamask/base-controller": "^9.1.0", "@metamask/eth-money-keyring": "^2.0.4", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^1.2.0", "async-mutex": "^0.5.0" diff --git a/packages/multichain-network-controller/package.json b/packages/multichain-network-controller/package.json index cc87134765..154a292a4e 100644 --- a/packages/multichain-network-controller/package.json +++ b/packages/multichain-network-controller/package.json @@ -56,7 +56,7 @@ "@metamask/accounts-controller": "^39.0.4", "@metamask/base-controller": "^9.1.0", "@metamask/controller-utils": "^12.3.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-internal-api": "^11.0.1", "@metamask/messenger": "^1.2.0", "@metamask/network-controller": "^34.0.0", diff --git a/packages/multichain-transactions-controller/package.json b/packages/multichain-transactions-controller/package.json index a5b038f122..44b0356982 100644 --- a/packages/multichain-transactions-controller/package.json +++ b/packages/multichain-transactions-controller/package.json @@ -55,9 +55,9 @@ "dependencies": { "@metamask/accounts-controller": "^39.0.4", "@metamask/base-controller": "^9.1.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-internal-api": "^11.0.1", - "@metamask/keyring-snap-client": "^9.0.2", + "@metamask/keyring-snap-client": "^9.1.0", "@metamask/messenger": "^1.2.0", "@metamask/polling-controller": "^16.0.8", "@metamask/snaps-controllers": "^19.0.0", diff --git a/packages/network-enablement-controller/package.json b/packages/network-enablement-controller/package.json index ba2b31e2dc..b309ccd892 100644 --- a/packages/network-enablement-controller/package.json +++ b/packages/network-enablement-controller/package.json @@ -55,7 +55,7 @@ "dependencies": { "@metamask/base-controller": "^9.1.0", "@metamask/controller-utils": "^12.3.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/messenger": "^1.2.0", "@metamask/multichain-network-controller": "^3.2.1", "@metamask/network-controller": "^34.0.0", diff --git a/packages/profile-sync-controller/package.json b/packages/profile-sync-controller/package.json index e0d8db4a00..d263033611 100644 --- a/packages/profile-sync-controller/package.json +++ b/packages/profile-sync-controller/package.json @@ -124,7 +124,7 @@ "@lavamoat/allow-scripts": "^3.0.4", "@lavamoat/preinstall-always-fail": "^2.1.0", "@metamask/auto-changelog": "^6.1.0", - "@metamask/keyring-api": "^23.3.0", + "@metamask/keyring-api": "^23.4.0", "@metamask/keyring-internal-api": "^11.0.1", "@metamask/providers": "^22.1.0", "@ts-bridge/cli": "^0.6.4", diff --git a/yarn.lock b/yarn.lock index 2fa8650624..68877557da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5427,7 +5427,7 @@ __metadata: "@metamask/accounts-controller": "npm:^39.0.4" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/multichain-account-service": "npm:^11.1.0" @@ -5464,8 +5464,8 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/eth-snap-keyring": "npm:^22.3.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/eth-snap-keyring": "npm:^22.4.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" "@metamask/keyring-sdk": "npm:^2.2.0" @@ -5675,10 +5675,10 @@ __metadata: "@metamask/client-controller": "npm:^1.0.1" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/core-backend": "npm:^6.5.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" - "@metamask/keyring-snap-client": "npm:^9.0.2" + "@metamask/keyring-snap-client": "npm:^9.1.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/network-enablement-controller": "npm:^5.4.1" @@ -5730,10 +5730,10 @@ __metadata: "@metamask/core-backend": "npm:^6.5.0" "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-provider-http": "npm:^0.3.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" - "@metamask/keyring-snap-client": "npm:^9.0.2" + "@metamask/keyring-snap-client": "npm:^9.1.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/multichain-account-service": "npm:^11.1.0" @@ -5943,7 +5943,7 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/eth-json-rpc-provider": "npm:^6.0.1" "@metamask/gas-fee-controller": "npm:^26.2.4" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/multichain-network-controller": "npm:^3.2.1" @@ -6443,7 +6443,7 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/stake-sdk": "npm:^3.2.1" @@ -6797,32 +6797,6 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-snap-keyring@npm:^22.3.0": - version: 22.3.0 - resolution: "@metamask/eth-snap-keyring@npm:22.3.0" - dependencies: - "@ethereumjs/tx": "npm:^5.4.0" - "@metamask/eth-sig-util": "npm:^8.2.0" - "@metamask/keyring-internal-api": "npm:^11.0.1" - "@metamask/keyring-internal-snap-client": "npm:^10.0.3" - "@metamask/keyring-sdk": "npm:^2.2.0" - "@metamask/keyring-snap-sdk": "npm:^9.0.2" - "@metamask/keyring-utils": "npm:^3.3.1" - "@metamask/messenger": "npm:^1.1.1" - "@metamask/snaps-controllers": "npm:^19.0.1" - "@metamask/snaps-sdk": "npm:^11.0.0" - "@metamask/snaps-utils": "npm:^12.1.3" - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^11.11.0" - "@types/uuid": "npm:^9.0.8" - async-mutex: "npm:^0.5.0" - uuid: "npm:^9.0.1" - peerDependencies: - "@metamask/keyring-api": ^23.0.0 - checksum: 10/d110a2cd97e0cd22eae7d5e87ea14ae09530d3d47204b4bc565ffd70f8af75133a2c3c15b0f51e49478b379ee40ea9980d3591c85a8e24c21e3768b8f1d8c1b8 - languageName: node - linkType: hard - "@metamask/eth-snap-keyring@npm:^22.4.0": version: 22.4.0 resolution: "@metamask/eth-snap-keyring@npm:22.4.0" @@ -7187,7 +7161,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@npm:^23.1.0, @metamask/keyring-api@npm:^23.2.0, @metamask/keyring-api@npm:^23.3.0": +"@metamask/keyring-api@npm:^23.1.0, @metamask/keyring-api@npm:^23.2.0": version: 23.3.0 resolution: "@metamask/keyring-api@npm:23.3.0" dependencies: @@ -7226,7 +7200,7 @@ __metadata: "@metamask/eth-hd-keyring": "npm:^14.1.1" "@metamask/eth-sig-util": "npm:^8.2.0" "@metamask/eth-simple-keyring": "npm:^12.0.2" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-internal-api": "npm:^11.0.1" "@metamask/keyring-utils": "npm:^3.3.1" "@metamask/messenger": "npm:^1.2.0" @@ -7262,19 +7236,6 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-internal-snap-client@npm:^10.0.3": - version: 10.0.3 - resolution: "@metamask/keyring-internal-snap-client@npm:10.0.3" - dependencies: - "@metamask/keyring-api": "npm:^23.1.0" - "@metamask/keyring-internal-api": "npm:^11.0.1" - "@metamask/keyring-snap-client": "npm:^9.0.2" - "@metamask/keyring-utils": "npm:^3.2.0" - "@metamask/messenger": "npm:^1.1.1" - checksum: 10/30c00a2f39125e7f99bd6513096f23fa243f30cd7ce4e77aa96f52c66fa99c077dfba0544d2693e16c283a2a5610f6b844eaa39130d1f760390d6ca1f53f0507 - languageName: node - linkType: hard - "@metamask/keyring-internal-snap-client@npm:^10.0.4": version: 10.0.4 resolution: "@metamask/keyring-internal-snap-client@npm:10.0.4" @@ -7306,22 +7267,6 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-snap-client@npm:^9.0.2": - version: 9.0.2 - resolution: "@metamask/keyring-snap-client@npm:9.0.2" - dependencies: - "@metamask/keyring-api": "npm:^23.1.0" - "@metamask/keyring-utils": "npm:^3.2.0" - "@metamask/superstruct": "npm:^3.1.0" - "@types/uuid": "npm:^9.0.8" - uuid: "npm:^9.0.1" - webextension-polyfill: "npm:^0.12.0" - peerDependencies: - "@metamask/providers": ^19.0.0 - checksum: 10/f4de52be334d941fffafb6458260875706a09315d68856577a66dd36b069de00260632cfcd0fe9b56528382512dee2c226f61430aafb619869f1b1711d3ffffd - languageName: node - linkType: hard - "@metamask/keyring-snap-client@npm:^9.1.0": version: 9.1.0 resolution: "@metamask/keyring-snap-client@npm:9.1.0" @@ -7338,22 +7283,6 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-snap-sdk@npm:^9.0.2": - version: 9.0.2 - resolution: "@metamask/keyring-snap-sdk@npm:9.0.2" - dependencies: - "@metamask/keyring-utils": "npm:^3.3.1" - "@metamask/snaps-sdk": "npm:^11.0.0" - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^11.11.0" - webextension-polyfill: "npm:^0.12.0" - peerDependencies: - "@metamask/keyring-api": ^23.0.0 - "@metamask/providers": ^19.0.0 - checksum: 10/a3fa4c306b058f484042e4460f8d19d509c4725cb49c40328dd905555bae33e07e29262891500acbcb845891af3721a562a36914286fc876f37668ce63f1bee1 - languageName: node - linkType: hard - "@metamask/keyring-snap-sdk@npm:^9.1.0": version: 9.1.0 resolution: "@metamask/keyring-snap-sdk@npm:9.1.0" @@ -7540,7 +7469,7 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/eth-money-keyring": "npm:^2.0.4" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-utils": "npm:^3.3.1" "@metamask/messenger": "npm:^1.2.0" @@ -7673,7 +7602,7 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" "@metamask/messenger": "npm:^1.2.0" @@ -7705,10 +7634,10 @@ __metadata: "@metamask/accounts-controller": "npm:^39.0.4" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" - "@metamask/keyring-snap-client": "npm:^9.0.2" + "@metamask/keyring-snap-client": "npm:^9.1.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/polling-controller": "npm:^16.0.8" "@metamask/snaps-controllers": "npm:^19.0.0" @@ -7808,7 +7737,7 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/messenger": "npm:^1.2.0" "@metamask/multichain-network-controller": "npm:^3.2.1" "@metamask/network-controller": "npm:^34.0.0" @@ -8196,7 +8125,7 @@ __metadata: "@metamask/address-book-controller": "npm:^7.1.2" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/keyring-api": "npm:^23.3.0" + "@metamask/keyring-api": "npm:^23.4.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" "@metamask/messenger": "npm:^1.2.0" From 342bf8a8386b83b0d0623b8ae1ae7bc5089bd020 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 17:42:59 +0200 Subject: [PATCH 8/9] chore: add dependency bump changelog entries for keyring package alignment --- packages/account-tree-controller/CHANGELOG.md | 2 +- packages/accounts-controller/CHANGELOG.md | 5 +++++ packages/assets-controller/CHANGELOG.md | 5 +++++ packages/assets-controllers/CHANGELOG.md | 4 ++++ packages/bridge-controller/CHANGELOG.md | 4 ++++ packages/earn-controller/CHANGELOG.md | 4 ++++ packages/keyring-controller/CHANGELOG.md | 2 +- packages/money-account-controller/CHANGELOG.md | 2 +- packages/multichain-network-controller/CHANGELOG.md | 4 ++++ packages/multichain-transactions-controller/CHANGELOG.md | 3 ++- packages/network-enablement-controller/CHANGELOG.md | 4 ++++ packages/snap-account-service/CHANGELOG.md | 6 ++---- 12 files changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index c60a1b49fb..9c6a895f58 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) +- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.4.0` ([#9249](https://github.com/MetaMask/core/pull/9249), [#9377](https://github.com/MetaMask/core/pull/9377)) - Bump `@metamask/multichain-account-service` from `^11.0.0` to `^11.1.0` ([#9264](https://github.com/MetaMask/core/pull/9264)) - Bump `@metamask/accounts-controller` from `^39.0.3` to `^39.0.4` ([#9349](https://github.com/MetaMask/core/pull/9349)) diff --git a/packages/accounts-controller/CHANGELOG.md b/packages/accounts-controller/CHANGELOG.md index 4d4cd09b94..c2454c09e8 100644 --- a/packages/accounts-controller/CHANGELOG.md +++ b/packages/accounts-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [39.0.4] ### Changed diff --git a/packages/assets-controller/CHANGELOG.md b/packages/assets-controller/CHANGELOG.md index a6108aa0b1..b673cd016d 100644 --- a/packages/assets-controller/CHANGELOG.md +++ b/packages/assets-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-snap-client` from `^9.0.2` to `^9.1.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [10.0.1] ### Changed diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index c800175a48..fa5f5ec4c8 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [109.3.0] ### Added diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index bdc17220ac..de4852df8b 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [77.3.2] ### Fixed diff --git a/packages/earn-controller/CHANGELOG.md b/packages/earn-controller/CHANGELOG.md index 4bae38c555..319df7f2c1 100644 --- a/packages/earn-controller/CHANGELOG.md +++ b/packages/earn-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [12.2.2] ### Changed diff --git a/packages/keyring-controller/CHANGELOG.md b/packages/keyring-controller/CHANGELOG.md index b538b1acad..36503299d8 100644 --- a/packages/keyring-controller/CHANGELOG.md +++ b/packages/keyring-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) +- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.4.0` ([#9249](https://github.com/MetaMask/core/pull/9249), [#9377](https://github.com/MetaMask/core/pull/9377)) ## [27.1.0] diff --git a/packages/money-account-controller/CHANGELOG.md b/packages/money-account-controller/CHANGELOG.md index 4ac68dbfc8..de30aeca33 100644 --- a/packages/money-account-controller/CHANGELOG.md +++ b/packages/money-account-controller/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074)) - Bump `@metamask/keyring-controller` from `^27.0.0` to `^27.1.0` ([#9129](https://github.com/MetaMask/core/pull/9129)) - Bump `@metamask/accounts-controller` from `^39.0.1` to `^39.0.4` ([#9218](https://github.com/MetaMask/core/pull/9218), [#9231](https://github.com/MetaMask/core/pull/9231), [#9349](https://github.com/MetaMask/core/pull/9349)) -- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) +- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.4.0` ([#9249](https://github.com/MetaMask/core/pull/9249), [#9377](https://github.com/MetaMask/core/pull/9377)) ## [0.3.3] diff --git a/packages/multichain-network-controller/CHANGELOG.md b/packages/multichain-network-controller/CHANGELOG.md index 42d8437491..1a0ddd539d 100644 --- a/packages/multichain-network-controller/CHANGELOG.md +++ b/packages/multichain-network-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [3.2.1] ### Changed diff --git a/packages/multichain-transactions-controller/CHANGELOG.md b/packages/multichain-transactions-controller/CHANGELOG.md index edfc30a405..e1f282ea24 100644 --- a/packages/multichain-transactions-controller/CHANGELOG.md +++ b/packages/multichain-transactions-controller/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074)) - Bump `@metamask/accounts-controller` from `^39.0.0` to `^39.0.4` ([#9058](https://github.com/MetaMask/core/pull/9058), [#9218](https://github.com/MetaMask/core/pull/9218), [#9231](https://github.com/MetaMask/core/pull/9231), [#9349](https://github.com/MetaMask/core/pull/9349)) - Bump `@metamask/polling-controller` from `^16.0.6` to `^16.0.8` ([#9218](https://github.com/MetaMask/core/pull/9218), [#9349](https://github.com/MetaMask/core/pull/9349)) -- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) +- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.4.0` ([#9249](https://github.com/MetaMask/core/pull/9249), [#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-snap-client` from `^9.0.2` to `^9.1.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) ## [7.1.1] diff --git a/packages/network-enablement-controller/CHANGELOG.md b/packages/network-enablement-controller/CHANGELOG.md index c7c56ecd81..3c37a7a66a 100644 --- a/packages/network-enablement-controller/CHANGELOG.md +++ b/packages/network-enablement-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) + ## [5.4.1] ### Added diff --git a/packages/snap-account-service/CHANGELOG.md b/packages/snap-account-service/CHANGELOG.md index 30674c5109..a9c333cc27 100644 --- a/packages/snap-account-service/CHANGELOG.md +++ b/packages/snap-account-service/CHANGELOG.md @@ -15,10 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Bump `@metamask/eth-snap-keyring` from `^22.3.0` to `^22.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) -- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.3.0` ([#9249](https://github.com/MetaMask/core/pull/9249)) -- Bump `@metamask/keyring-api` from `^23.3.0` to `^23.4.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) -- Bump `@metamask/keyring-snap-sdk` from `^9.0.1` to `^9.0.2` ([#9249](https://github.com/MetaMask/core/pull/9249)) -- Bump `@metamask/keyring-snap-sdk` from `^9.0.2` to `^9.1.0` ([#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-api` from `^23.1.0` to `^23.4.0` ([#9249](https://github.com/MetaMask/core/pull/9249), [#9377](https://github.com/MetaMask/core/pull/9377)) +- Bump `@metamask/keyring-snap-sdk` from `^9.0.1` to `^9.1.0` ([#9249](https://github.com/MetaMask/core/pull/9249), [#9377](https://github.com/MetaMask/core/pull/9377)) ## [1.0.0] From a1e525c77d3e7b78d204ce3392fa60ce010a5e06 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 2 Jul 2026 17:47:42 +0200 Subject: [PATCH 9/9] chore: dedupe yarn.lock --- yarn.lock | 47 ++--------------------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1eccd6e3b4..cd2add2a72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7178,19 +7178,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@npm:^23.1.0, @metamask/keyring-api@npm:^23.2.0": - version: 23.3.0 - resolution: "@metamask/keyring-api@npm:23.3.0" - dependencies: - "@metamask/keyring-utils": "npm:^3.3.1" - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^11.11.0" - bitcoin-address-validation: "npm:^2.2.3" - checksum: 10/c6ad3bf4294b09730d752d548b1cd0c1c622f357a12c7287af2b5889a01e99a4d20316cc9e96840d5c5619eee0f9305d4dfc03febd0bd136e72de95ce3fd0a1e - languageName: node - linkType: hard - -"@metamask/keyring-api@npm:^23.4.0": +"@metamask/keyring-api@npm:^23.1.0, @metamask/keyring-api@npm:^23.2.0, @metamask/keyring-api@npm:^23.4.0": version: 23.4.0 resolution: "@metamask/keyring-api@npm:23.4.0" dependencies: @@ -8635,38 +8623,7 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-utils@npm:^12.1.2, @metamask/snaps-utils@npm:^12.1.3, @metamask/snaps-utils@npm:^12.2.0": - version: 12.2.0 - resolution: "@metamask/snaps-utils@npm:12.2.0" - dependencies: - "@babel/core": "npm:^7.23.2" - "@babel/types": "npm:^7.23.0" - "@metamask/key-tree": "npm:^10.1.1" - "@metamask/messenger": "npm:^1.1.1" - "@metamask/permission-controller": "npm:^12.3.0" - "@metamask/rpc-errors": "npm:^7.0.3" - "@metamask/slip44": "npm:^4.4.0" - "@metamask/snaps-registry": "npm:^4.0.0" - "@metamask/snaps-sdk": "npm:^11.1.0" - "@metamask/superstruct": "npm:^3.2.1" - "@metamask/utils": "npm:^11.11.0" - "@scure/base": "npm:^1.1.1" - chalk: "npm:^4.1.2" - cron-parser: "npm:^4.5.0" - fast-deep-equal: "npm:^3.1.3" - fast-json-stable-stringify: "npm:^2.1.0" - fast-xml-parser: "npm:^5.5.6" - luxon: "npm:^3.5.0" - marked: "npm:^12.0.1" - rfdc: "npm:^1.3.0" - semver: "npm:^7.5.4" - ses: "npm:^1.15.0" - validate-npm-package-name: "npm:^5.0.0" - checksum: 10/0e7cb5a4deebad3dc98404486b7767be049e9f86173195e9b1ae577f197e67cdd392f1e05ebb146a199ffc3cd52013636637a4d1ac7f9fec164f3189be97df55 - languageName: node - linkType: hard - -"@metamask/snaps-utils@npm:^12.2.1": +"@metamask/snaps-utils@npm:^12.1.2, @metamask/snaps-utils@npm:^12.1.3, @metamask/snaps-utils@npm:^12.2.0, @metamask/snaps-utils@npm:^12.2.1": version: 12.2.1 resolution: "@metamask/snaps-utils@npm:12.2.1" dependencies: