From 0781e515fdc2443be43fd0bc260cb85774b70a5e Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 14:47:38 +0800 Subject: [PATCH 01/14] feat: add PasskeyController to wallet initialization instances --- packages/wallet/package.json | 1 + .../src/initialization/instances/index.ts | 1 + .../passkey-controller/passkey-controller.ts | 26 +++++++++++++++++++ .../instances/passkey-controller/types.ts | 4 +++ packages/wallet/src/types.ts | 2 ++ packages/wallet/tsconfig.build.json | 1 + packages/wallet/tsconfig.json | 3 +++ yarn.lock | 3 ++- 8 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts create mode 100644 packages/wallet/src/initialization/instances/passkey-controller/types.ts diff --git a/packages/wallet/package.json b/packages/wallet/package.json index a31271b98a0..b4bdf30864d 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -65,6 +65,7 @@ "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^2.0.0", "@metamask/network-controller": "^34.0.0", + "@metamask/passkey-controller": "^2.1.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/storage-service": "^1.0.2", diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index a15db956032..6db88c600d2 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -4,6 +4,7 @@ export { approvalController } from './approval-controller/approval-controller'; export { connectivityController } from './connectivity-controller/connectivity-controller'; export { keyringController } from './keyring-controller/keyring-controller'; export { networkController } from './network-controller/network-controller'; +export { passkeyController } from './passkey-controller/passkey-controller'; export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; export { storageService } from './storage-service/storage-service'; export { transactionController } from './transaction-controller/transaction-controller'; diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts new file mode 100644 index 00000000000..a1b90b50156 --- /dev/null +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -0,0 +1,26 @@ +import { Messenger } from '@metamask/messenger'; +import { InitializationConfiguration } from '../../types'; +import { PasskeyController, PasskeyControllerMessenger } from '@metamask/passkey-controller'; + +const DEFAULT_PASSKEY_RP_NAME = 'MetaMask'; +const DEFAULT_PASSKEY_USER_NAME = 'MetaMask Wallet'; +const DEFAULT_PASSKEY_USER_DISPLAY_NAME = 'MetaMask Wallet'; + +export const passkeyController: InitializationConfiguration = { + name: 'PasskeyController', + init: ({ state, messenger, options }) => new PasskeyController({ + messenger, + state, + rpId: undefined, + rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, + expectedRPID: options.expectedRPID, + expectedOrigin: options.expectedOrigin, + userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, + userDisplayName: options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, + }), + getMessenger: (parent) => + new Messenger({ + namespace: 'PasskeyController', + parent, + }), +} diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts new file mode 100644 index 00000000000..f900ad2a79e --- /dev/null +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -0,0 +1,4 @@ +export type PasskeyControllerInstanceOptions = { + expectedRPID: string; + expectedOrigin: string; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 1f330e9bafd..6f531159fd5 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -13,6 +13,7 @@ import type { RemoteFeatureFlagControllerInstanceOptions } from './initializatio import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; +import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; export type WalletOptions = { messenger?: RootMessenger; @@ -29,6 +30,7 @@ export type InstanceSpecificOptions = { connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; networkController: NetworkControllerInstanceOptions; + passkeyController: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index cc15d40bc18..214a1cf6720 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -15,6 +15,7 @@ { "path": "../keyring-controller/tsconfig.build.json" }, { "path": "../messenger/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../passkey-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, { "path": "../storage-service/tsconfig.build.json" }, { "path": "../transaction-controller/tsconfig.build.json" } diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index ce310304ecc..e2a7b3ef81b 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -31,6 +31,9 @@ { "path": "../network-controller" }, + { + "path": "../passkey-controller" + }, { "path": "../remote-feature-flag-controller" }, diff --git a/yarn.lock b/yarn.lock index 91de6e9c442..29de7cca7a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7929,7 +7929,7 @@ __metadata: languageName: node linkType: hard -"@metamask/passkey-controller@workspace:packages/passkey-controller": +"@metamask/passkey-controller@npm:^2.1.0, @metamask/passkey-controller@workspace:packages/passkey-controller": version: 0.0.0-use.local resolution: "@metamask/passkey-controller@workspace:packages/passkey-controller" dependencies: @@ -9162,6 +9162,7 @@ __metadata: "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" + "@metamask/passkey-controller": "npm:^2.1.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/storage-service": "npm:^1.0.2" From d795cf64f89e6759a9fc88a9128b0fed5ab09b01 Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 15:08:34 +0800 Subject: [PATCH 02/14] feat: add SeedlessOnboardingController to wallet init --- packages/wallet/package.json | 1 + .../src/initialization/instances/index.ts | 1 + .../instances/passkey-controller/types.ts | 37 ++++++++++++++++++- .../seedless-onboarding-controller.ts | 26 +++++++++++++ .../seedless-onboarding-controller/types.ts | 13 +++++++ packages/wallet/src/types.ts | 2 + packages/wallet/tsconfig.build.json | 1 + packages/wallet/tsconfig.json | 3 ++ yarn.lock | 3 +- 9 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts create mode 100644 packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts diff --git a/packages/wallet/package.json b/packages/wallet/package.json index b4bdf30864d..7754effd742 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -68,6 +68,7 @@ "@metamask/passkey-controller": "^2.1.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", + "@metamask/seedless-onboarding-controller": "^10.0.3", "@metamask/storage-service": "^1.0.2", "@metamask/transaction-controller": "^69.0.0", "@metamask/utils": "^11.11.0" diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index 6db88c600d2..3f2be223691 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -6,5 +6,6 @@ export { keyringController } from './keyring-controller/keyring-controller'; export { networkController } from './network-controller/network-controller'; export { passkeyController } from './passkey-controller/passkey-controller'; export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; +export { seedlessOnboardingController } from './seedless-onboarding-controller/seedless-onboarding-controller'; export { storageService } from './storage-service/storage-service'; export { transactionController } from './transaction-controller/transaction-controller'; diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index f900ad2a79e..f74c75fedb0 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,4 +1,37 @@ export type PasskeyControllerInstanceOptions = { - expectedRPID: string; - expectedOrigin: string; + expectedRPID: string | string[]; + + /** + * Allowed value(s) for the WebAuthn client origin. + */ + expectedOrigin: string | string[]; + + /** + * Relying party ID(s) for verification (SHA-256 hash match in authenticator data). Pass a string or array of strings; an empty array skips RP ID + * allowlist checks in {@link verifyRegistrationResponse} / {@link verifyAuthenticationResponse}. + * + * @default undefined + */ + rpId?: string; + + /** + * Relying party name shown in the platform passkey UI. + * + * @default 'MetaMask' + */ + rpName?: string; + + /** + * Optional passkey user name; defaults to `rpName`. + * + * @default 'MetaMask Wallet' + */ + userName?: string; + + /** + * Optional display name; defaults to `rpName`. + * + * @default 'MetaMask Wallet' + */ + userDisplayName?: string; }; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts new file mode 100644 index 00000000000..03ceec4e2e1 --- /dev/null +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -0,0 +1,26 @@ +import { SeedlessOnboardingController, SeedlessOnboardingControllerMessenger } from '@metamask/seedless-onboarding-controller'; +import { InitializationConfiguration } from '../../types'; +import { encryptorFactory } from '../keyring-controller/encryptor'; +import { Messenger } from '@metamask/messenger'; +import { EncryptionKey } from '@metamask/browser-passworder'; +import { Encryptor } from '@metamask/keyring-controller'; + +export const seedlessOnboardingController: InitializationConfiguration< + SeedlessOnboardingController, + SeedlessOnboardingControllerMessenger +> = { + name: 'SeedlessOnboardingController', + init: ({ state, messenger, options }) => (new SeedlessOnboardingController({ + ...options, + state, + messenger, + encryptor: (options.encryptor ?? encryptorFactory(600_000)) as Encryptor< + EncryptionKey + >, + })), + getMessenger: (parent) => + new Messenger({ + namespace: 'SeedlessOnboardingController', + parent, + }), +}; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts new file mode 100644 index 00000000000..2ed619c43a5 --- /dev/null +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts @@ -0,0 +1,13 @@ +import type { SeedlessOnboardingControllerOptions } from '@metamask/seedless-onboarding-controller'; +import { GenericEncryptor } from '../keyring-controller/encryptor'; + +export type SeedlessOnboardingControllerInstanceOptions = Omit< + SeedlessOnboardingControllerOptions, + 'messenger' | 'state' | 'encryptor' +> & { + /** + * Encryptor used to protect the seedless onboarding vault. Defaults to a PBKDF2 encryptor + * configured with 600,000 iterations. + */ + encryptor?: GenericEncryptor; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 6f531159fd5..1bddde41ee8 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -14,6 +14,7 @@ import type { StorageServiceInstanceOptions } from './initialization/instances/s import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; +import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; export type WalletOptions = { messenger?: RootMessenger; @@ -32,6 +33,7 @@ export type InstanceSpecificOptions = { networkController: NetworkControllerInstanceOptions; passkeyController: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; + seedlessOnboardingController: SeedlessOnboardingControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; }; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index 214a1cf6720..d4caa50ec1c 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -17,6 +17,7 @@ { "path": "../network-controller/tsconfig.build.json" }, { "path": "../passkey-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, + { "path": "../seedless-onboarding-controller/tsconfig.build.json" }, { "path": "../storage-service/tsconfig.build.json" }, { "path": "../transaction-controller/tsconfig.build.json" } ], diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index e2a7b3ef81b..62f5137fc5b 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -37,6 +37,9 @@ { "path": "../remote-feature-flag-controller" }, + { + "path": "../seedless-onboarding-controller" + }, { "path": "../storage-service" }, diff --git a/yarn.lock b/yarn.lock index 29de7cca7a9..c04c5fcc476 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8434,7 +8434,7 @@ __metadata: languageName: node linkType: hard -"@metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": +"@metamask/seedless-onboarding-controller@npm:^10.0.3, @metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": version: 0.0.0-use.local resolution: "@metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller" dependencies: @@ -9165,6 +9165,7 @@ __metadata: "@metamask/passkey-controller": "npm:^2.1.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" + "@metamask/seedless-onboarding-controller": "npm:^10.0.3" "@metamask/storage-service": "npm:^1.0.2" "@metamask/transaction-controller": "npm:^69.0.0" "@metamask/utils": "npm:^11.11.0" From 3cbc1585ab154c6aede7fa697ce58a620267f6c1 Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 15:59:35 +0800 Subject: [PATCH 03/14] fix: fixed build and lint --- .../passkey-controller.test.ts | 154 +++++++++++++++ .../seedless-onboarding-controller.test.ts | 182 ++++++++++++++++++ packages/wallet/src/types.ts | 4 +- 3 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts create mode 100644 packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts new file mode 100644 index 00000000000..6f1614a3c54 --- /dev/null +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -0,0 +1,154 @@ +import { Messenger } from '@metamask/messenger'; +import { + PasskeyController, + getDefaultPasskeyControllerState, +} from '@metamask/passkey-controller'; +import type { PasskeyRecord } from '@metamask/passkey-controller'; + +import { defaultConfigurations } from '../../defaults'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults'; +import { passkeyController } from './passkey-controller'; + +const { PasskeyController: ActualPasskeyController } = jest.requireActual( + '@metamask/passkey-controller', +); + +jest.mock('@metamask/passkey-controller', () => ({ + ...jest.requireActual('@metamask/passkey-controller'), + PasskeyController: jest.fn(), +})); + +const REQUIRED_OPTIONS = { + expectedRPID: 'extension-id', + expectedOrigin: 'https://extension.origin', +}; + +/** + * Creates a root messenger for use in tests. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: 'Root' }); +} + +describe('passkeyController', () => { + beforeEach(() => { + jest.clearAllMocks(); + (PasskeyController as jest.Mock).mockImplementation((...args: unknown[]) => + new ActualPasskeyController(...args), + ); + }); + + it('is registered as a default initialization configuration', () => { + expect(Object.values(defaultConfigurations)).toContain(passkeyController); + }); + + it('initializes a PasskeyController with default state', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + const instance = passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(instance).toBeInstanceOf(ActualPasskeyController); + expect(instance.state).toStrictEqual(getDefaultPasskeyControllerState()); + }); + + it('forwards the provided state to the controller', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + const passkeyRecord: PasskeyRecord = { + credential: { + id: 'credential-id', + publicKey: 'public-key', + counter: 0, + transports: ['internal'], + aaguid: '00000000-0000-0000-0000-000000000000', + }, + encryptedVaultKey: { + ciphertext: 'YQ==', + iv: 'YWFhYWFhYWFhYQ==', + }, + keyDerivation: { method: 'userHandle' }, + }; + + const instance = passkeyController.init({ + state: { passkeyRecord }, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); + }); + + it('applies default rpName, userName, and userDisplayName when omitted', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(PasskeyController).toHaveBeenCalledWith( + expect.objectContaining({ + rpId: undefined, + rpName: 'MetaMask', + userName: 'MetaMask Wallet', + userDisplayName: 'MetaMask Wallet', + expectedRPID: REQUIRED_OPTIONS.expectedRPID, + expectedOrigin: REQUIRED_OPTIONS.expectedOrigin, + }), + ); + }); + + it('forwards custom passkey configuration options', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + passkeyController.init({ + state: undefined, + messenger, + options: { + expectedRPID: ['extension-id', 'other-id'], + expectedOrigin: ['https://a.example', 'https://b.example'], + rpId: 'rp-id', + rpName: 'Custom RP', + userName: 'custom-user', + userDisplayName: 'Custom Display Name', + }, + }); + + expect(PasskeyController).toHaveBeenCalledWith( + expect.objectContaining({ + rpId: undefined, + rpName: 'Custom RP', + userName: 'custom-user', + userDisplayName: 'Custom Display Name', + expectedRPID: ['extension-id', 'other-id'], + expectedOrigin: ['https://a.example', 'https://b.example'], + }), + ); + }); + + it('exposes its state through the root messenger', () => { + const rootMessenger = getRootMessenger(); + const messenger = passkeyController.getMessenger(rootMessenger); + + passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + expect(rootMessenger.call('PasskeyController:getState')).toStrictEqual( + getDefaultPasskeyControllerState(), + ); + }); +}); diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts new file mode 100644 index 00000000000..9fdfcace186 --- /dev/null +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts @@ -0,0 +1,182 @@ +import { Messenger } from '@metamask/messenger'; +import { + SecretType, + SeedlessOnboardingController, + getDefaultSeedlessOnboardingControllerState, +} from '@metamask/seedless-onboarding-controller'; + +import { defaultConfigurations } from '../../defaults'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults'; +import { encryptorFactory } from '../keyring-controller/encryptor'; +import * as encryptorModule from '../keyring-controller/encryptor'; +import { seedlessOnboardingController } from './seedless-onboarding-controller'; +import type { SeedlessOnboardingControllerInstanceOptions } from './types'; + +const { SeedlessOnboardingController: ActualSeedlessOnboardingController } = + jest.requireActual('@metamask/seedless-onboarding-controller'); + +jest.mock('@metamask/seedless-onboarding-controller', () => ({ + ...jest.requireActual('@metamask/seedless-onboarding-controller'), + SeedlessOnboardingController: jest.fn(), +})); + +/** + * Creates a root messenger for use in tests. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: 'Root' }); +} + +/** + * Creates the required seedless onboarding options for tests. + * + * @returns Seedless onboarding controller options. + */ +function getSeedlessOnboardingOptions(): Pick< + SeedlessOnboardingControllerInstanceOptions, + 'refreshJWTToken' | 'revokeRefreshToken' | 'renewRefreshToken' +> { + return { + refreshJWTToken: jest.fn(), + revokeRefreshToken: jest.fn(), + renewRefreshToken: jest.fn(), + }; +} + +describe('seedlessOnboardingController', () => { + beforeEach(() => { + jest.clearAllMocks(); + (SeedlessOnboardingController as jest.Mock).mockImplementation( + (...args: unknown[]) => new ActualSeedlessOnboardingController(...args), + ); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('is registered as a default initialization configuration', () => { + expect(Object.values(defaultConfigurations)).toContain( + seedlessOnboardingController, + ); + }); + + it('initializes a SeedlessOnboardingController with default state', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + + const instance = seedlessOnboardingController.init({ + state: undefined, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + expect(instance).toBeInstanceOf(ActualSeedlessOnboardingController); + expect(instance.state).toStrictEqual( + getDefaultSeedlessOnboardingControllerState(), + ); + }); + + it('forwards the provided state to the controller', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + + const instance = seedlessOnboardingController.init({ + state: { + migrationVersion: 1, + isSeedlessOnboardingUserAuthenticated: false, + socialBackupsMetadata: [{ hash: 'abc', type: SecretType.Mnemonic }], + }, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + expect(instance.state.migrationVersion).toBe(1); + expect(instance.state.socialBackupsMetadata).toStrictEqual([ + { hash: 'abc', type: SecretType.Mnemonic }, + ]); + }); + + it('forwards seedless onboarding options to the controller', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + const options = getSeedlessOnboardingOptions(); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options, + }); + + expect(SeedlessOnboardingController).toHaveBeenCalledWith( + expect.objectContaining({ + refreshJWTToken: options.refreshJWTToken, + revokeRefreshToken: options.revokeRefreshToken, + renewRefreshToken: options.renewRefreshToken, + }), + ); + }); + + it('applies the default encryptor when omitted', () => { + const defaultEncryptor = encryptorFactory(600_000); + const encryptorFactorySpy = jest + .spyOn(encryptorModule, 'encryptorFactory') + .mockReturnValue(defaultEncryptor); + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + const { encryptor } = (SeedlessOnboardingController as jest.Mock).mock + .calls[0][0] as { + encryptor: ReturnType; + }; + + expect(encryptorFactorySpy).toHaveBeenCalledWith(600_000); + expect(encryptor).toBe(defaultEncryptor); + }); + + it('uses the provided encryptor when supplied', () => { + const messenger = + seedlessOnboardingController.getMessenger(getRootMessenger()); + const encryptor = encryptorFactory(100_000); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options: { + ...getSeedlessOnboardingOptions(), + encryptor, + }, + }); + + expect(SeedlessOnboardingController).toHaveBeenCalledWith( + expect.objectContaining({ encryptor }), + ); + }); + + it('exposes its state through the root messenger', () => { + const rootMessenger = getRootMessenger(); + const messenger = seedlessOnboardingController.getMessenger(rootMessenger); + + seedlessOnboardingController.init({ + state: undefined, + messenger, + options: getSeedlessOnboardingOptions(), + }); + + expect( + rootMessenger.call('SeedlessOnboardingController:getState'), + ).toStrictEqual(getDefaultSeedlessOnboardingControllerState()); + }); +}); diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 1bddde41ee8..ec7485cea4d 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -31,9 +31,9 @@ export type InstanceSpecificOptions = { connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; networkController: NetworkControllerInstanceOptions; - passkeyController: PasskeyControllerInstanceOptions; + passkeyController?: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; - seedlessOnboardingController: SeedlessOnboardingControllerInstanceOptions; + seedlessOnboardingController?: SeedlessOnboardingControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; }; From abc2e4c542921ad676dbed796dc600b1a34632ef Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 16:14:47 +0800 Subject: [PATCH 04/14] chore: updated CHANGELOG --- README.md | 2 ++ eslint-suppressions.json | 5 --- packages/wallet/CHANGELOG.md | 4 +++ .../passkey-controller.test.ts | 4 +-- .../passkey-controller/passkey-controller.ts | 35 ++++++++++++------- .../seedless-onboarding-controller.ts | 28 ++++++++------- .../seedless-onboarding-controller/types.ts | 1 + packages/wallet/src/types.ts | 4 +-- 8 files changed, 49 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index fc19b98258a..e3bdf8dac9c 100644 --- a/README.md +++ b/README.md @@ -648,7 +648,9 @@ linkStyle default opacity:0.5 wallet --> keyring_controller; wallet --> messenger; wallet --> network_controller; + wallet --> passkey_controller; wallet --> remote_feature_flag_controller; + wallet --> seedless_onboarding_controller; wallet --> storage_service; wallet --> transaction_controller; wallet_cli --> base_controller; diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 79caefd17d3..46131ea3fee 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2240,11 +2240,6 @@ "count": 2 } }, - "packages/transaction-pay-controller/src/strategy/bridge/bridge-submit.ts": { - "no-restricted-syntax": { - "count": 1 - } - }, "packages/transaction-pay-controller/src/strategy/relay/hyperliquid-withdraw.ts": { "no-restricted-syntax": { "count": 1 diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 706c4ac482c..df287bf355b 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **BREAKING** Wire `SeedlessOnboardingController` and `PasskeyController` into the default wallet initialization ([#9533](https://github.com/MetaMask/core/pull/9533)) + ## [7.0.1] ### Changed diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 6f1614a3c54..19d010d22d9 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -39,8 +39,8 @@ function getRootMessenger(): RootMessenger { describe('passkeyController', () => { beforeEach(() => { jest.clearAllMocks(); - (PasskeyController as jest.Mock).mockImplementation((...args: unknown[]) => - new ActualPasskeyController(...args), + (PasskeyController as jest.Mock).mockImplementation( + (...args: unknown[]) => new ActualPasskeyController(...args), ); }); diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index a1b90b50156..edc81458c1b 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -1,26 +1,35 @@ import { Messenger } from '@metamask/messenger'; +import { + PasskeyController, + PasskeyControllerMessenger, +} from '@metamask/passkey-controller'; + import { InitializationConfiguration } from '../../types'; -import { PasskeyController, PasskeyControllerMessenger } from '@metamask/passkey-controller'; const DEFAULT_PASSKEY_RP_NAME = 'MetaMask'; const DEFAULT_PASSKEY_USER_NAME = 'MetaMask Wallet'; const DEFAULT_PASSKEY_USER_DISPLAY_NAME = 'MetaMask Wallet'; -export const passkeyController: InitializationConfiguration = { +export const passkeyController: InitializationConfiguration< + PasskeyController, + PasskeyControllerMessenger +> = { name: 'PasskeyController', - init: ({ state, messenger, options }) => new PasskeyController({ - messenger, - state, - rpId: undefined, - rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, - expectedRPID: options.expectedRPID, - expectedOrigin: options.expectedOrigin, - userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, - userDisplayName: options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, - }), + init: ({ state, messenger, options }) => + new PasskeyController({ + messenger, + state, + rpId: options.rpId ?? undefined, + rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, + expectedRPID: options.expectedRPID, + expectedOrigin: options.expectedOrigin, + userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, + userDisplayName: + options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, + }), getMessenger: (parent) => new Messenger({ namespace: 'PasskeyController', parent, }), -} +}; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts index 03ceec4e2e1..03bd03d3778 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -1,23 +1,27 @@ -import { SeedlessOnboardingController, SeedlessOnboardingControllerMessenger } from '@metamask/seedless-onboarding-controller'; -import { InitializationConfiguration } from '../../types'; -import { encryptorFactory } from '../keyring-controller/encryptor'; -import { Messenger } from '@metamask/messenger'; import { EncryptionKey } from '@metamask/browser-passworder'; import { Encryptor } from '@metamask/keyring-controller'; +import { Messenger } from '@metamask/messenger'; +import { + SeedlessOnboardingController, + SeedlessOnboardingControllerMessenger, +} from '@metamask/seedless-onboarding-controller'; + +import { InitializationConfiguration } from '../../types'; +import { encryptorFactory } from '../keyring-controller/encryptor'; export const seedlessOnboardingController: InitializationConfiguration< SeedlessOnboardingController, SeedlessOnboardingControllerMessenger > = { name: 'SeedlessOnboardingController', - init: ({ state, messenger, options }) => (new SeedlessOnboardingController({ - ...options, - state, - messenger, - encryptor: (options.encryptor ?? encryptorFactory(600_000)) as Encryptor< - EncryptionKey - >, - })), + init: ({ state, messenger, options }) => + new SeedlessOnboardingController({ + ...options, + state, + messenger, + encryptor: (options.encryptor ?? + encryptorFactory(600_000)) as Encryptor, + }), getMessenger: (parent) => new Messenger({ namespace: 'SeedlessOnboardingController', diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts index 2ed619c43a5..8bbf862698c 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts @@ -1,4 +1,5 @@ import type { SeedlessOnboardingControllerOptions } from '@metamask/seedless-onboarding-controller'; + import { GenericEncryptor } from '../keyring-controller/encryptor'; export type SeedlessOnboardingControllerInstanceOptions = Omit< diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index ec7485cea4d..910ea28c171 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -9,12 +9,12 @@ import type { ApprovalControllerInstanceOptions } from './initialization/instanc import type { ConnectivityControllerInstanceOptions } from './initialization/instances/connectivity-controller/types'; import type { KeyringControllerInstanceOptions } from './initialization/instances/keyring-controller/types'; import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; +import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; +import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; import type { InitializationConfiguration } from './initialization/types'; -import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; -import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; export type WalletOptions = { messenger?: RootMessenger; From b867f452d0415402ddbc47c10e557172f35c2401 Mon Sep 17 00:00:00 2001 From: lwin Date: Thu, 16 Jul 2026 16:20:25 +0800 Subject: [PATCH 05/14] chore: updaed CODE_OWNERS --- .github/CODEOWNERS | 2 ++ .../instances/passkey-controller/passkey-controller.test.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 49f39f1ce08..2c0825f7132 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -150,6 +150,8 @@ /packages/wallet/src/initialization/instances/remote-feature-flag-controller/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform /packages/wallet/src/initialization/instances/storage-service/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform /packages/wallet/src/initialization/instances/transaction-controller/ @MetaMask/confirmations +/packages/wallet/src/initialization/instances/passkey-controller @MetaMask/web3auth +/packages/wallet/src/initialization/instances/seedless-onboarding-controller @MetaMask/web3auth ## Package Release related /packages/account-tree-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 19d010d22d9..f8293342a0d 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -127,7 +127,7 @@ describe('passkeyController', () => { expect(PasskeyController).toHaveBeenCalledWith( expect.objectContaining({ - rpId: undefined, + rpId: 'rp-id', rpName: 'Custom RP', userName: 'custom-user', userDisplayName: 'Custom Display Name', From 901744a99caaa0de4cb74b95a6a2422fc915ec15 Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 10:10:31 +0800 Subject: [PATCH 06/14] chore: reuse constructor options for PasskeyControllerInstanceOptions --- .../passkey-controller.test.ts | 1 + .../passkey-controller/passkey-controller.ts | 2 +- .../instances/passkey-controller/types.ts | 41 +++---------------- 3 files changed, 7 insertions(+), 37 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index f8293342a0d..727275e0230 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -25,6 +25,7 @@ jest.mock('@metamask/passkey-controller', () => ({ const REQUIRED_OPTIONS = { expectedRPID: 'extension-id', expectedOrigin: 'https://extension.origin', + rpName: 'MetaMask', }; /** diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index edc81458c1b..e5918668554 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -19,7 +19,7 @@ export const passkeyController: InitializationConfiguration< new PasskeyController({ messenger, state, - rpId: options.rpId ?? undefined, + rpId: options.rpId, rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, expectedRPID: options.expectedRPID, expectedOrigin: options.expectedOrigin, diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index f74c75fedb0..e6b09e74257 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,37 +1,6 @@ -export type PasskeyControllerInstanceOptions = { - expectedRPID: string | string[]; +import { PasskeyController } from "@metamask/passkey-controller"; - /** - * Allowed value(s) for the WebAuthn client origin. - */ - expectedOrigin: string | string[]; - - /** - * Relying party ID(s) for verification (SHA-256 hash match in authenticator data). Pass a string or array of strings; an empty array skips RP ID - * allowlist checks in {@link verifyRegistrationResponse} / {@link verifyAuthenticationResponse}. - * - * @default undefined - */ - rpId?: string; - - /** - * Relying party name shown in the platform passkey UI. - * - * @default 'MetaMask' - */ - rpName?: string; - - /** - * Optional passkey user name; defaults to `rpName`. - * - * @default 'MetaMask Wallet' - */ - userName?: string; - - /** - * Optional display name; defaults to `rpName`. - * - * @default 'MetaMask Wallet' - */ - userDisplayName?: string; -}; +export type PasskeyControllerInstanceOptions = Omit< + ConstructorParameters[0], + 'messenger' | 'state' +>; From c94fc018b0822ac71422f7af31b1ded8895ad99e Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 10:32:20 +0800 Subject: [PATCH 07/14] fix: fixed lint --- .../src/initialization/instances/passkey-controller/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index e6b09e74257..b25bcf1c27a 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,4 +1,4 @@ -import { PasskeyController } from "@metamask/passkey-controller"; +import { PasskeyController } from '@metamask/passkey-controller'; export type PasskeyControllerInstanceOptions = Omit< ConstructorParameters[0], From ded1aa14c6b643949678f79f5890f8433325380e Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 11:29:25 +0800 Subject: [PATCH 08/14] feat: updated passkey-controller-init --- .../passkey-controller/passkey-controller.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index e5918668554..bf1836fa5c0 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -6,10 +6,6 @@ import { import { InitializationConfiguration } from '../../types'; -const DEFAULT_PASSKEY_RP_NAME = 'MetaMask'; -const DEFAULT_PASSKEY_USER_NAME = 'MetaMask Wallet'; -const DEFAULT_PASSKEY_USER_DISPLAY_NAME = 'MetaMask Wallet'; - export const passkeyController: InitializationConfiguration< PasskeyController, PasskeyControllerMessenger @@ -17,15 +13,9 @@ export const passkeyController: InitializationConfiguration< name: 'PasskeyController', init: ({ state, messenger, options }) => new PasskeyController({ + ...options, messenger, state, - rpId: options.rpId, - rpName: options.rpName ?? DEFAULT_PASSKEY_RP_NAME, - expectedRPID: options.expectedRPID, - expectedOrigin: options.expectedOrigin, - userName: options.userName ?? DEFAULT_PASSKEY_USER_NAME, - userDisplayName: - options.userDisplayName ?? DEFAULT_PASSKEY_USER_DISPLAY_NAME, }), getMessenger: (parent) => new Messenger({ From 70079ce87c9bc5ffd441ff4dbf8782d29e1fe134 Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 12:15:27 +0800 Subject: [PATCH 09/14] fix: fixed tests --- .../passkey-controller/passkey-controller.test.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 727275e0230..ac81fc353f6 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -89,7 +89,7 @@ describe('passkeyController', () => { expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); }); - it('applies default rpName, userName, and userDisplayName when omitted', () => { + it('forwards options as-is without injecting userName or userDisplayName defaults', () => { const messenger = passkeyController.getMessenger(getRootMessenger()); passkeyController.init({ @@ -100,14 +100,18 @@ describe('passkeyController', () => { expect(PasskeyController).toHaveBeenCalledWith( expect.objectContaining({ - rpId: undefined, rpName: 'MetaMask', - userName: 'MetaMask Wallet', - userDisplayName: 'MetaMask Wallet', expectedRPID: REQUIRED_OPTIONS.expectedRPID, expectedOrigin: REQUIRED_OPTIONS.expectedOrigin, }), ); + + const [passedOptions] = (PasskeyController as jest.Mock).mock.calls[0] as [ + Record, + ]; + expect(passedOptions).not.toHaveProperty('userName'); + expect(passedOptions).not.toHaveProperty('userDisplayName'); + expect(passedOptions.rpId).toBeUndefined(); }); it('forwards custom passkey configuration options', () => { From c60b97981104cb8659cd4b7984db53309c88c094 Mon Sep 17 00:00:00 2001 From: lwin Date: Fri, 17 Jul 2026 12:15:49 +0800 Subject: [PATCH 10/14] fix: fixed tests --- .../passkey-controller.test.ts | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index ac81fc353f6..7606b942618 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -89,31 +89,6 @@ describe('passkeyController', () => { expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); }); - it('forwards options as-is without injecting userName or userDisplayName defaults', () => { - const messenger = passkeyController.getMessenger(getRootMessenger()); - - passkeyController.init({ - state: undefined, - messenger, - options: REQUIRED_OPTIONS, - }); - - expect(PasskeyController).toHaveBeenCalledWith( - expect.objectContaining({ - rpName: 'MetaMask', - expectedRPID: REQUIRED_OPTIONS.expectedRPID, - expectedOrigin: REQUIRED_OPTIONS.expectedOrigin, - }), - ); - - const [passedOptions] = (PasskeyController as jest.Mock).mock.calls[0] as [ - Record, - ]; - expect(passedOptions).not.toHaveProperty('userName'); - expect(passedOptions).not.toHaveProperty('userDisplayName'); - expect(passedOptions.rpId).toBeUndefined(); - }); - it('forwards custom passkey configuration options', () => { const messenger = passkeyController.getMessenger(getRootMessenger()); From 12bb36714fc73e3fd3c323fce4c1923808571b64 Mon Sep 17 00:00:00 2001 From: lwin Date: Tue, 21 Jul 2026 02:55:56 +0800 Subject: [PATCH 11/14] fix: fixed CODEOWNERS --- .github/CODEOWNERS | 4 ++-- codeowners.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 472b58dc838..f7d8e29ec70 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -151,11 +151,11 @@ /packages/wallet/src/initialization/instances/approval-controller/ @MetaMask/confirmations /packages/wallet/src/initialization/instances/connectivity-controller/ @MetaMask/core-platform /packages/wallet/src/initialization/instances/keyring-controller/ @MetaMask/accounts-engineers @MetaMask/core-platform +/packages/wallet/src/initialization/instances/passkey-controller/ @MetaMask/web3auth /packages/wallet/src/initialization/instances/remote-feature-flag-controller/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform +/packages/wallet/src/initialization/instances/seedless-onboarding-controller/ @MetaMask/web3auth /packages/wallet/src/initialization/instances/storage-service/ @MetaMask/extension-platform @MetaMask/mobile-platform @MetaMask/core-platform /packages/wallet/src/initialization/instances/transaction-controller/ @MetaMask/confirmations -/packages/wallet/src/initialization/instances/passkey-controller/ @MetaMask/web3auth -/packages/wallet/src/initialization/instances/seedless-onboarding-controller/ @MetaMask/web3auth ## Package Release related /packages/account-tree-controller/package.json @MetaMask/accounts-engineers @MetaMask/core-platform diff --git a/codeowners.ts b/codeowners.ts index 7020d46d5df..ce77d6b6877 100644 --- a/codeowners.ts +++ b/codeowners.ts @@ -248,6 +248,7 @@ const PACKAGES: Record = { }, 'passkey-controller': { teams: ['@MetaMask/web3auth'], + initializationPath: 'passkey-controller', }, 'permission-controller': { teams: ['@MetaMask/core-platform'], @@ -298,6 +299,7 @@ const PACKAGES: Record = { }, 'seedless-onboarding-controller': { teams: ['@MetaMask/web3auth'], + initializationPath: 'seedless-onboarding-controller', }, 'selected-network-controller': { teams: ['@MetaMask/core-platform'], From 5566024a173cf175a535288c19cdbaaf0dc7269b Mon Sep 17 00:00:00 2001 From: lwin Date: Tue, 21 Jul 2026 11:23:50 +0800 Subject: [PATCH 12/14] chore: test updates --- .../passkey-controller.test.ts | 70 +++++++------- .../seedless-onboarding-controller.test.ts | 92 ++----------------- .../seedless-onboarding-controller.ts | 5 - .../seedless-onboarding-controller/types.ts | 12 +-- 4 files changed, 46 insertions(+), 133 deletions(-) diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 7606b942618..5ef4080f5ea 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -5,7 +5,6 @@ import { } from '@metamask/passkey-controller'; import type { PasskeyRecord } from '@metamask/passkey-controller'; -import { defaultConfigurations } from '../../defaults'; import type { DefaultActions, DefaultEvents, @@ -13,15 +12,6 @@ import type { } from '../../defaults'; import { passkeyController } from './passkey-controller'; -const { PasskeyController: ActualPasskeyController } = jest.requireActual( - '@metamask/passkey-controller', -); - -jest.mock('@metamask/passkey-controller', () => ({ - ...jest.requireActual('@metamask/passkey-controller'), - PasskeyController: jest.fn(), -})); - const REQUIRED_OPTIONS = { expectedRPID: 'extension-id', expectedOrigin: 'https://extension.origin', @@ -38,17 +28,6 @@ function getRootMessenger(): RootMessenger { } describe('passkeyController', () => { - beforeEach(() => { - jest.clearAllMocks(); - (PasskeyController as jest.Mock).mockImplementation( - (...args: unknown[]) => new ActualPasskeyController(...args), - ); - }); - - it('is registered as a default initialization configuration', () => { - expect(Object.values(defaultConfigurations)).toContain(passkeyController); - }); - it('initializes a PasskeyController with default state', () => { const messenger = passkeyController.getMessenger(getRootMessenger()); @@ -58,7 +37,7 @@ describe('passkeyController', () => { options: REQUIRED_OPTIONS, }); - expect(instance).toBeInstanceOf(ActualPasskeyController); + expect(instance).toBeInstanceOf(PasskeyController); expect(instance.state).toStrictEqual(getDefaultPasskeyControllerState()); }); @@ -89,10 +68,30 @@ describe('passkeyController', () => { expect(instance.state.passkeyRecord).toStrictEqual(passkeyRecord); }); - it('forwards custom passkey configuration options', () => { + it('defaults userName and userDisplayName to rpName when omitted', () => { const messenger = passkeyController.getMessenger(getRootMessenger()); - passkeyController.init({ + const instance = passkeyController.init({ + state: undefined, + messenger, + options: REQUIRED_OPTIONS, + }); + + const options = instance.generateRegistrationOptions({ + prfAvailable: false, + }); + + expect(options.user).toStrictEqual({ + id: expect.any(String), + name: 'MetaMask', + displayName: 'MetaMask', + }); + }); + + it('uses custom passkey configuration options', () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + + const instance = passkeyController.init({ state: undefined, messenger, options: { @@ -105,16 +104,19 @@ describe('passkeyController', () => { }, }); - expect(PasskeyController).toHaveBeenCalledWith( - expect.objectContaining({ - rpId: 'rp-id', - rpName: 'Custom RP', - userName: 'custom-user', - userDisplayName: 'Custom Display Name', - expectedRPID: ['extension-id', 'other-id'], - expectedOrigin: ['https://a.example', 'https://b.example'], - }), - ); + const options = instance.generateRegistrationOptions({ + prfAvailable: false, + }); + + expect(options.rp).toStrictEqual({ + name: 'Custom RP', + id: 'rp-id', + }); + expect(options.user).toStrictEqual({ + id: expect.any(String), + name: 'custom-user', + displayName: 'Custom Display Name', + }); }); it('exposes its state through the root messenger', () => { diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts index 9fdfcace186..198d127ff92 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts @@ -5,25 +5,15 @@ import { getDefaultSeedlessOnboardingControllerState, } from '@metamask/seedless-onboarding-controller'; -import { defaultConfigurations } from '../../defaults'; import type { DefaultActions, DefaultEvents, RootMessenger, } from '../../defaults'; import { encryptorFactory } from '../keyring-controller/encryptor'; -import * as encryptorModule from '../keyring-controller/encryptor'; import { seedlessOnboardingController } from './seedless-onboarding-controller'; import type { SeedlessOnboardingControllerInstanceOptions } from './types'; -const { SeedlessOnboardingController: ActualSeedlessOnboardingController } = - jest.requireActual('@metamask/seedless-onboarding-controller'); - -jest.mock('@metamask/seedless-onboarding-controller', () => ({ - ...jest.requireActual('@metamask/seedless-onboarding-controller'), - SeedlessOnboardingController: jest.fn(), -})); - /** * Creates a root messenger for use in tests. * @@ -38,11 +28,11 @@ function getRootMessenger(): RootMessenger { * * @returns Seedless onboarding controller options. */ -function getSeedlessOnboardingOptions(): Pick< - SeedlessOnboardingControllerInstanceOptions, - 'refreshJWTToken' | 'revokeRefreshToken' | 'renewRefreshToken' -> { +function getSeedlessOnboardingOptions(): SeedlessOnboardingControllerInstanceOptions { return { + encryptor: encryptorFactory( + 600_000, + ) as SeedlessOnboardingControllerInstanceOptions['encryptor'], refreshJWTToken: jest.fn(), revokeRefreshToken: jest.fn(), renewRefreshToken: jest.fn(), @@ -50,23 +40,6 @@ function getSeedlessOnboardingOptions(): Pick< } describe('seedlessOnboardingController', () => { - beforeEach(() => { - jest.clearAllMocks(); - (SeedlessOnboardingController as jest.Mock).mockImplementation( - (...args: unknown[]) => new ActualSeedlessOnboardingController(...args), - ); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('is registered as a default initialization configuration', () => { - expect(Object.values(defaultConfigurations)).toContain( - seedlessOnboardingController, - ); - }); - it('initializes a SeedlessOnboardingController with default state', () => { const messenger = seedlessOnboardingController.getMessenger(getRootMessenger()); @@ -77,7 +50,7 @@ describe('seedlessOnboardingController', () => { options: getSeedlessOnboardingOptions(), }); - expect(instance).toBeInstanceOf(ActualSeedlessOnboardingController); + expect(instance).toBeInstanceOf(SeedlessOnboardingController); expect(instance.state).toStrictEqual( getDefaultSeedlessOnboardingControllerState(), ); @@ -103,66 +76,17 @@ describe('seedlessOnboardingController', () => { ]); }); - it('forwards seedless onboarding options to the controller', () => { - const messenger = - seedlessOnboardingController.getMessenger(getRootMessenger()); - const options = getSeedlessOnboardingOptions(); - - seedlessOnboardingController.init({ - state: undefined, - messenger, - options, - }); - - expect(SeedlessOnboardingController).toHaveBeenCalledWith( - expect.objectContaining({ - refreshJWTToken: options.refreshJWTToken, - revokeRefreshToken: options.revokeRefreshToken, - renewRefreshToken: options.renewRefreshToken, - }), - ); - }); - - it('applies the default encryptor when omitted', () => { - const defaultEncryptor = encryptorFactory(600_000); - const encryptorFactorySpy = jest - .spyOn(encryptorModule, 'encryptorFactory') - .mockReturnValue(defaultEncryptor); + it('reports unauthenticated when initialized with default state', async () => { const messenger = seedlessOnboardingController.getMessenger(getRootMessenger()); - seedlessOnboardingController.init({ + const instance = seedlessOnboardingController.init({ state: undefined, messenger, options: getSeedlessOnboardingOptions(), }); - const { encryptor } = (SeedlessOnboardingController as jest.Mock).mock - .calls[0][0] as { - encryptor: ReturnType; - }; - - expect(encryptorFactorySpy).toHaveBeenCalledWith(600_000); - expect(encryptor).toBe(defaultEncryptor); - }); - - it('uses the provided encryptor when supplied', () => { - const messenger = - seedlessOnboardingController.getMessenger(getRootMessenger()); - const encryptor = encryptorFactory(100_000); - - seedlessOnboardingController.init({ - state: undefined, - messenger, - options: { - ...getSeedlessOnboardingOptions(), - encryptor, - }, - }); - - expect(SeedlessOnboardingController).toHaveBeenCalledWith( - expect.objectContaining({ encryptor }), - ); + expect(await instance.getIsUserAuthenticated()).toBe(false); }); it('exposes its state through the root messenger', () => { diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts index 03bd03d3778..d49b3c184ba 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -1,5 +1,3 @@ -import { EncryptionKey } from '@metamask/browser-passworder'; -import { Encryptor } from '@metamask/keyring-controller'; import { Messenger } from '@metamask/messenger'; import { SeedlessOnboardingController, @@ -7,7 +5,6 @@ import { } from '@metamask/seedless-onboarding-controller'; import { InitializationConfiguration } from '../../types'; -import { encryptorFactory } from '../keyring-controller/encryptor'; export const seedlessOnboardingController: InitializationConfiguration< SeedlessOnboardingController, @@ -19,8 +16,6 @@ export const seedlessOnboardingController: InitializationConfiguration< ...options, state, messenger, - encryptor: (options.encryptor ?? - encryptorFactory(600_000)) as Encryptor, }), getMessenger: (parent) => new Messenger({ diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts index 8bbf862698c..3e978ac682d 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/types.ts @@ -1,14 +1,6 @@ import type { SeedlessOnboardingControllerOptions } from '@metamask/seedless-onboarding-controller'; -import { GenericEncryptor } from '../keyring-controller/encryptor'; - export type SeedlessOnboardingControllerInstanceOptions = Omit< SeedlessOnboardingControllerOptions, - 'messenger' | 'state' | 'encryptor' -> & { - /** - * Encryptor used to protect the seedless onboarding vault. Defaults to a PBKDF2 encryptor - * configured with 600,000 iterations. - */ - encryptor?: GenericEncryptor; -}; + 'messenger' | 'state' +>; From 30b4935852d14779f13329656db7dfd6216e6563 Mon Sep 17 00:00:00 2001 From: lwin Date: Tue, 21 Jul 2026 23:55:48 +0800 Subject: [PATCH 13/14] feat: update passkey-controller to v3 --- packages/wallet/package.json | 2 +- .../src/initialization/instances/index.ts | 16 +----- .../passkey-controller.test.ts | 49 +++++++++++++++++-- .../passkey-controller/passkey-controller.ts | 34 ++++++++++--- .../instances/passkey-controller/types.ts | 4 +- .../seedless-onboarding-controller.test.ts | 8 +-- .../seedless-onboarding-controller.ts | 2 +- packages/wallet/src/types.ts | 20 ++------ yarn.lock | 4 +- 9 files changed, 89 insertions(+), 50 deletions(-) diff --git a/packages/wallet/package.json b/packages/wallet/package.json index a457b13b95b..b56f1b253f5 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -65,7 +65,7 @@ "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^2.0.0", "@metamask/network-controller": "^34.0.0", - "@metamask/passkey-controller": "^2.1.0", + "@metamask/passkey-controller": "^3.0.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/seedless-onboarding-controller": "^10.0.3", diff --git a/packages/wallet/src/initialization/instances/index.ts b/packages/wallet/src/initialization/instances/index.ts index 247cb66e95c..858e36e9769 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -1,23 +1,11 @@ -<<<<<<< HEAD -export { accountsController } from './accounts-controller/accounts-controller'; -export { addressBookController } from './address-book-controller/address-book-controller'; -export { approvalController } from './approval-controller/approval-controller'; -export { connectivityController } from './connectivity-controller/connectivity-controller'; -export { keyringController } from './keyring-controller/keyring-controller'; -export { networkController } from './network-controller/network-controller'; -export { passkeyController } from './passkey-controller/passkey-controller'; -export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; -export { seedlessOnboardingController } from './seedless-onboarding-controller/seedless-onboarding-controller'; -export { storageService } from './storage-service/storage-service'; -export { transactionController } from './transaction-controller/transaction-controller'; -======= export { accountsController } from './accounts-controller/accounts-controller.js'; export { addressBookController } from './address-book-controller/address-book-controller.js'; export { approvalController } from './approval-controller/approval-controller.js'; export { connectivityController } from './connectivity-controller/connectivity-controller.js'; export { keyringController } from './keyring-controller/keyring-controller.js'; export { networkController } from './network-controller/network-controller.js'; +export { passkeyController } from './passkey-controller/passkey-controller.js'; export { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller.js'; +export { seedlessOnboardingController } from './seedless-onboarding-controller/seedless-onboarding-controller.js'; export { storageService } from './storage-service/storage-service.js'; export { transactionController } from './transaction-controller/transaction-controller.js'; ->>>>>>> origin/main diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts index 5ef4080f5ea..7c31e0b3e70 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.test.ts @@ -1,21 +1,29 @@ import { Messenger } from '@metamask/messenger'; import { PasskeyController, + PasskeyControllerErrorCode, getDefaultPasskeyControllerState, } from '@metamask/passkey-controller'; -import type { PasskeyRecord } from '@metamask/passkey-controller'; +import type { + PasskeyAuthenticationResponse, + PasskeyRecord, + PasskeyRegistrationResponse, +} from '@metamask/passkey-controller'; +import { defaultConfigurations } from '../../defaults.js'; import type { DefaultActions, DefaultEvents, RootMessenger, -} from '../../defaults'; -import { passkeyController } from './passkey-controller'; +} from '../../defaults.js'; +import { passkeyController } from './passkey-controller.js'; +import type { PasskeyControllerInstanceOptions } from './types.js'; -const REQUIRED_OPTIONS = { +const REQUIRED_OPTIONS: PasskeyControllerInstanceOptions = { expectedRPID: 'extension-id', expectedOrigin: 'https://extension.origin', rpName: 'MetaMask', + getIsOnboardingCompleted: () => false, }; /** @@ -28,6 +36,13 @@ function getRootMessenger(): RootMessenger { } describe('passkeyController', () => { + it('is registered as a default initialization configuration', () => { + // Proves the controller is part of the default ensemble that `initialize()` + // wires, without constructing a `Wallet` (which keeps this PR independent of + // the constructor-options shape). + expect(Object.values(defaultConfigurations)).toContain(passkeyController); + }); + it('initializes a PasskeyController with default state', () => { const messenger = passkeyController.getMessenger(getRootMessenger()); @@ -95,6 +110,7 @@ describe('passkeyController', () => { state: undefined, messenger, options: { + ...REQUIRED_OPTIONS, expectedRPID: ['extension-id', 'other-id'], expectedOrigin: ['https://a.example', 'https://b.example'], rpId: 'rp-id', @@ -119,6 +135,31 @@ describe('passkeyController', () => { }); }); + it('uses the provided getIsOnboardingCompleted callback', async () => { + const messenger = passkeyController.getMessenger(getRootMessenger()); + const getIsOnboardingCompleted = jest.fn().mockReturnValue(true); + + const instance = passkeyController.init({ + state: undefined, + messenger, + options: { + ...REQUIRED_OPTIONS, + getIsOnboardingCompleted, + }, + }); + + await expect( + instance.protectVaultKeyWithPasskey({ + registrationResponse: {} as PasskeyRegistrationResponse, + authenticationResponse: {} as PasskeyAuthenticationResponse, + }), + ).rejects.toMatchObject({ + code: PasskeyControllerErrorCode.EnrollmentPasswordRequired, + }); + + expect(getIsOnboardingCompleted).toHaveBeenCalled(); + }); + it('exposes its state through the root messenger', () => { const rootMessenger = getRootMessenger(); const messenger = passkeyController.getMessenger(rootMessenger); diff --git a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts index bf1836fa5c0..7e40e7ce590 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/passkey-controller.ts @@ -4,7 +4,12 @@ import { PasskeyControllerMessenger, } from '@metamask/passkey-controller'; -import { InitializationConfiguration } from '../../types'; +import type { + DefaultActions, + DefaultEvents, + RootMessenger, +} from '../../defaults.js'; +import { InitializationConfiguration } from '../../types.js'; export const passkeyController: InitializationConfiguration< PasskeyController, @@ -17,9 +22,26 @@ export const passkeyController: InitializationConfiguration< messenger, state, }), - getMessenger: (parent) => - new Messenger({ - namespace: 'PasskeyController', - parent, - }), + getMessenger: (parent: RootMessenger) => { + const passkeyControllerMessenger: PasskeyControllerMessenger = + new Messenger({ + namespace: 'PasskeyController', + parent, + }); + + parent.delegate({ + messenger: passkeyControllerMessenger, + actions: [ + 'KeyringController:verifyPassword', + 'KeyringController:exportEncryptionKey', + 'KeyringController:submitEncryptionKey', + 'KeyringController:changePassword', + 'KeyringController:exportSeedPhrase', + 'KeyringController:exportAccount', + ], + events: [], + }); + + return passkeyControllerMessenger; + }, }; diff --git a/packages/wallet/src/initialization/instances/passkey-controller/types.ts b/packages/wallet/src/initialization/instances/passkey-controller/types.ts index b25bcf1c27a..e450e056d04 100644 --- a/packages/wallet/src/initialization/instances/passkey-controller/types.ts +++ b/packages/wallet/src/initialization/instances/passkey-controller/types.ts @@ -1,6 +1,6 @@ -import { PasskeyController } from '@metamask/passkey-controller'; +import type { PasskeyControllerOptions } from '@metamask/passkey-controller'; export type PasskeyControllerInstanceOptions = Omit< - ConstructorParameters[0], + PasskeyControllerOptions, 'messenger' | 'state' >; diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts index 198d127ff92..8d208eb649e 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.test.ts @@ -9,10 +9,10 @@ import type { DefaultActions, DefaultEvents, RootMessenger, -} from '../../defaults'; -import { encryptorFactory } from '../keyring-controller/encryptor'; -import { seedlessOnboardingController } from './seedless-onboarding-controller'; -import type { SeedlessOnboardingControllerInstanceOptions } from './types'; +} from '../../defaults.js'; +import { encryptorFactory } from '../keyring-controller/encryptor.js'; +import { seedlessOnboardingController } from './seedless-onboarding-controller.js'; +import type { SeedlessOnboardingControllerInstanceOptions } from './types.js'; /** * Creates a root messenger for use in tests. diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts index d49b3c184ba..b842b6ac02d 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -4,7 +4,7 @@ import { SeedlessOnboardingControllerMessenger, } from '@metamask/seedless-onboarding-controller'; -import { InitializationConfiguration } from '../../types'; +import { InitializationConfiguration } from '../../types.js'; export const seedlessOnboardingController: InitializationConfiguration< SeedlessOnboardingController, diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index caf1d634599..46bb9c5993c 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -4,29 +4,17 @@ import type { DefaultActions, DefaultEvents, RootMessenger, -<<<<<<< HEAD -} from './initialization/defaults'; -import type { ApprovalControllerInstanceOptions } from './initialization/instances/approval-controller/types'; -import type { ConnectivityControllerInstanceOptions } from './initialization/instances/connectivity-controller/types'; -import type { KeyringControllerInstanceOptions } from './initialization/instances/keyring-controller/types'; -import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; -import { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types'; -import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; -import { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types'; -import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; -import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types'; -import type { InitializationConfiguration } from './initialization/types'; -======= } from './initialization/defaults.js'; import type { ApprovalControllerInstanceOptions } from './initialization/instances/approval-controller/types.js'; import type { ConnectivityControllerInstanceOptions } from './initialization/instances/connectivity-controller/types.js'; import type { KeyringControllerInstanceOptions } from './initialization/instances/keyring-controller/types.js'; import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types.js'; +import type { PasskeyControllerInstanceOptions } from './initialization/instances/passkey-controller/types.js'; import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types.js'; +import type { SeedlessOnboardingControllerInstanceOptions } from './initialization/instances/seedless-onboarding-controller/types.js'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types.js'; import type { TransactionControllerInstanceOptions } from './initialization/instances/transaction-controller/types.js'; import type { InitializationConfiguration } from './initialization/types.js'; ->>>>>>> origin/main export type WalletOptions = { messenger?: RootMessenger; @@ -43,9 +31,9 @@ export type InstanceSpecificOptions = { connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; networkController: NetworkControllerInstanceOptions; - passkeyController?: PasskeyControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; - seedlessOnboardingController?: SeedlessOnboardingControllerInstanceOptions; storageService: StorageServiceInstanceOptions; transactionController?: TransactionControllerInstanceOptions; + passkeyController?: PasskeyControllerInstanceOptions; + seedlessOnboardingController?: SeedlessOnboardingControllerInstanceOptions; }; diff --git a/yarn.lock b/yarn.lock index 45d5cc749f6..8fc98d15567 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7932,7 +7932,7 @@ __metadata: languageName: node linkType: hard -"@metamask/passkey-controller@npm:^2.1.0, @metamask/passkey-controller@workspace:packages/passkey-controller": +"@metamask/passkey-controller@npm:^3.0.0, @metamask/passkey-controller@workspace:packages/passkey-controller": version: 0.0.0-use.local resolution: "@metamask/passkey-controller@workspace:packages/passkey-controller" dependencies: @@ -9173,7 +9173,7 @@ __metadata: "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" - "@metamask/passkey-controller": "npm:^2.1.0" + "@metamask/passkey-controller": "npm:^3.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/seedless-onboarding-controller": "npm:^10.0.3" From b5b7ac61bd3b580f4631901099d6a6841f690813 Mon Sep 17 00:00:00 2001 From: lwin Date: Wed, 22 Jul 2026 00:44:53 +0800 Subject: [PATCH 14/14] fix: fixed default encryptor for seedless-onboarding-controller --- .../seedless-onboarding-controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts index b842b6ac02d..1f07d40ffc6 100644 --- a/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts +++ b/packages/wallet/src/initialization/instances/seedless-onboarding-controller/seedless-onboarding-controller.ts @@ -5,6 +5,7 @@ import { } from '@metamask/seedless-onboarding-controller'; import { InitializationConfiguration } from '../../types.js'; +import { encryptorFactory } from '../keyring-controller/encryptor.js'; export const seedlessOnboardingController: InitializationConfiguration< SeedlessOnboardingController, @@ -14,6 +15,7 @@ export const seedlessOnboardingController: InitializationConfiguration< init: ({ state, messenger, options }) => new SeedlessOnboardingController({ ...options, + encryptor: options?.encryptor ?? encryptorFactory(600_000), state, messenger, }),