From e5e2fe973c124476ca13c551e6142dabcd6ba109 Mon Sep 17 00:00:00 2001 From: grypez <143971198+grypez@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:17:56 -0400 Subject: [PATCH 1/2] feat(wallet): add injectable `logger` to `WalletOptions` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an optional `logger?: Pick` field to `WalletOptions`. When provided, `initialize()` emits a `[wallet] ${name}: initialized` breadcrumb via `logger.info` immediately after each controller's `init()` completes, in initialization order. The field is no-op by default, so library consumers are unaffected. This gives visibility into controller initialization order and timing — the data needed to observe which messenger actions are called during `init()` vs. at runtime, informing the init-messenger pattern decision. Co-Authored-By: Claude Opus 4.8 --- packages/wallet/CHANGELOG.md | 2 + packages/wallet/src/Wallet.test.ts | 46 +++++++++++++++++++ packages/wallet/src/Wallet.ts | 2 + .../src/initialization/initialization.ts | 3 ++ packages/wallet/src/types.ts | 6 +++ 5 files changed, 59 insertions(+) diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index ee0ce0342a..6a25934d0a 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add an optional `logger` option to `WalletOptions` for initialization diagnostics ([#8791](https://github.com/MetaMask/core/pull/8791)) + - When provided (e.g. `{ logger: console }`), a `[wallet] ${name}: initialized` breadcrumb is emitted via `logger.info` immediately after each controller's `init()` completes, in initialization order. Defaults to no output. - **BREAKING:** Add `AccountsController` and `ConnectivityController` as default initialized controllers ([#8924](https://github.com/MetaMask/core/pull/8924)) - Passing `instanceOptions.connectivityController.connectivityAdapter` is now required. - Export `AlwaysOnlineAdapter` from the package root for environments without a platform-specific network API (e.g. Node/tests). diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index a89602c3fc..6e8e0f7420 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -5,6 +5,7 @@ import { Json } from '@metamask/utils'; import { webcrypto } from 'crypto'; import MockEncryptor from '../../keyring-controller/tests/mocks/mockEncryptor'; +import { defaultConfigurations } from './initialization/defaults'; import * as initializationModule from './initialization/initialization'; import { AlwaysOnlineAdapter } from './initialization/instances/connectivity-controller/always-online-adapter'; import { importSecretRecoveryPhrase } from './utilities'; @@ -149,6 +150,51 @@ describe('Wallet', () => { expect((state as Record).TestService).toBeUndefined(); }); + it('logs an initialization breadcrumb for each controller in order when a logger is provided', () => { + const info = jest.fn(); + + const wallet = new Wallet({ + logger: { info }, + instanceOptions: { + connectivityController: { + connectivityAdapter: new AlwaysOnlineAdapter(), + }, + storageService: { + storage: new InMemoryStorageAdapter(), + }, + remoteFeatureFlagController: REMOTE_FEATURE_FLAG_OPTIONS, + }, + }); + + const loggedMessages = info.mock.calls.map(([message]) => message); + const expectedMessages = Object.values(defaultConfigurations).map( + (config) => `[wallet] ${config.name}: initialized`, + ); + + expect(loggedMessages).toStrictEqual(expectedMessages); + // The constructed wallet exposes the controllers it logged. + expect( + wallet.getInstance(defaultConfigurations.keyringController.name), + ).toBeDefined(); + }); + + it('does not throw when no logger is provided', () => { + expect( + () => + new Wallet({ + instanceOptions: { + connectivityController: { + connectivityAdapter: new AlwaysOnlineAdapter(), + }, + storageService: { + storage: new InMemoryStorageAdapter(), + }, + remoteFeatureFlagController: REMOTE_FEATURE_FLAG_OPTIONS, + }, + }), + ).not.toThrow(); + }); + it('exposes controllerMetadata for each initialized controller', async () => { const wallet = await setupWallet(); diff --git a/packages/wallet/src/Wallet.ts b/packages/wallet/src/Wallet.ts index 5ba1566599..1c76c3c543 100644 --- a/packages/wallet/src/Wallet.ts +++ b/packages/wallet/src/Wallet.ts @@ -34,6 +34,8 @@ export class Wallet { * required beyond the ones included by default. * @param options.instanceOptions - An optional object containing options that should be passed * to specific instances for additional customization. + * @param options.logger - An optional logger used to emit a breadcrumb after each controller's + * initialization completes. Defaults to no output. */ constructor(options: WalletOptions) { this.#messenger = diff --git a/packages/wallet/src/initialization/initialization.ts b/packages/wallet/src/initialization/initialization.ts index 28a7b835f5..5c82336eb0 100644 --- a/packages/wallet/src/initialization/initialization.ts +++ b/packages/wallet/src/initialization/initialization.ts @@ -23,6 +23,7 @@ export function initialize(options: InitializeOptions): DefaultInstances { state = {}, initializationConfigurations = [], instanceOptions, + logger, } = options; const overriddenConfiguration = initializationConfigurations.map( @@ -55,6 +56,8 @@ export function initialize(options: InitializeOptions): DefaultInstances { }); instances[name] = instance; + + logger?.info(`[wallet] ${name}: initialized`); } return instances as DefaultInstances; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 0bc6c0efaa..9e2f95af23 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -20,6 +20,12 @@ export type WalletOptions = { unknown >[]; instanceOptions: InstanceSpecificOptions; + /** + * An optional logger used to emit initialization diagnostics. When provided, + * a breadcrumb is logged immediately after each controller's `init()` + * completes, in initialization order. Defaults to no output. + */ + logger?: Pick; }; export type InstanceSpecificOptions = { From f01e8f5aac622122abb401251b7ac6d556840ffa Mon Sep 17 00:00:00 2001 From: grypez <143971198+grypez@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:09:45 -0400 Subject: [PATCH 2/2] docs(wallet): link changelog entry to PR #9097 The `changelog:check` CI gate requires entries to reference the current PR rather than the issue. Co-Authored-By: Claude Opus 4.8 --- packages/wallet/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 6a25934d0a..6aa0ced51f 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add an optional `logger` option to `WalletOptions` for initialization diagnostics ([#8791](https://github.com/MetaMask/core/pull/8791)) +- Add an optional `logger` option to `WalletOptions` for initialization diagnostics ([#9097](https://github.com/MetaMask/core/pull/9097)) - When provided (e.g. `{ logger: console }`), a `[wallet] ${name}: initialized` breadcrumb is emitted via `logger.info` immediately after each controller's `init()` completes, in initialization order. Defaults to no output. - **BREAKING:** Add `AccountsController` and `ConnectivityController` as default initialized controllers ([#8924](https://github.com/MetaMask/core/pull/8924)) - Passing `instanceOptions.connectivityController.connectivityAdapter` is now required.