From 24f1fcef2bd5b8ffcedde9aca54fbe11fe0b8f9f Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Thu, 4 Jun 2026 11:05:23 +0200 Subject: [PATCH 01/11] feat(wallet): Add NetworkController initialization --- packages/wallet/package.json | 1 + .../src/initialization/instances/index.ts | 1 + .../network-controller/network-controller.ts | 35 +++++++++++++++++++ .../instances/network-controller/types.ts | 6 ++++ packages/wallet/src/types.ts | 4 ++- packages/wallet/tsconfig.build.json | 1 + packages/wallet/tsconfig.json | 1 + yarn.lock | 1 + 8 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/wallet/src/initialization/instances/network-controller/network-controller.ts create mode 100644 packages/wallet/src/initialization/instances/network-controller/types.ts diff --git a/packages/wallet/package.json b/packages/wallet/package.json index a60081b716b..89cb74d05fe 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -61,6 +61,7 @@ "@metamask/controller-utils": "^12.3.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^1.2.0", + "@metamask/network-controller": "^32.0.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 bed6e52b26d..03fab5e5d5b 100644 --- a/packages/wallet/src/initialization/instances/index.ts +++ b/packages/wallet/src/initialization/instances/index.ts @@ -2,5 +2,6 @@ export { accountsController } from './accounts-controller/accounts-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 { remoteFeatureFlagController } from './remote-feature-flag-controller/remote-feature-flag-controller'; export { storageService } from './storage-service/storage-service'; diff --git a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts new file mode 100644 index 00000000000..ee1f9e21ff0 --- /dev/null +++ b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts @@ -0,0 +1,35 @@ +import { Messenger } from '@metamask/messenger'; +import { + NetworkController, + NetworkControllerMessenger, +} from '@metamask/network-controller'; + +import { InitializationConfiguration } from '../../types'; + +export const networkController: InitializationConfiguration< + NetworkController, + NetworkControllerMessenger +> = { + name: 'NetworkController', + init: ({ state, messenger, options }) => + new NetworkController({ + state, + messenger, + infuraProjectId: options.infuraProjectId, + }), + getMessenger: (parent) => { + const networkControllerMessenger: NetworkControllerMessenger = + new Messenger({ + namespace: 'NetworkController', + parent, + }); + + parent.delegate({ + messenger: networkControllerMessenger, + actions: ['ConnectivityController:getState'], + events: [], + }); + + return networkControllerMessenger; + }, +}; diff --git a/packages/wallet/src/initialization/instances/network-controller/types.ts b/packages/wallet/src/initialization/instances/network-controller/types.ts new file mode 100644 index 00000000000..69fbc03eaae --- /dev/null +++ b/packages/wallet/src/initialization/instances/network-controller/types.ts @@ -0,0 +1,6 @@ +/** + * Per-instance options for the wallet's `NetworkController`. + */ +export type NetworkControllerInstanceOptions = { + infuraProjectId: string; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 0bc6c0efaad..05bc1f365f4 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -10,7 +10,8 @@ import type { ConnectivityControllerInstanceOptions } from './initialization/ins import type { KeyringControllerInstanceOptions } from './initialization/instances/keyring-controller/types'; import type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; -import { InitializationConfiguration } from './initialization/types'; +import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; +import type { InitializationConfiguration } from './initialization/types'; export type WalletOptions = { messenger?: RootMessenger; @@ -26,6 +27,7 @@ export type InstanceSpecificOptions = { approvalController?: ApprovalControllerInstanceOptions; connectivityController: ConnectivityControllerInstanceOptions; keyringController?: KeyringControllerInstanceOptions; + networkController: NetworkControllerInstanceOptions; remoteFeatureFlagController: RemoteFeatureFlagControllerInstanceOptions; storageService: StorageServiceInstanceOptions; }; diff --git a/packages/wallet/tsconfig.build.json b/packages/wallet/tsconfig.build.json index 317fe28aac2..fcf397e033d 100644 --- a/packages/wallet/tsconfig.build.json +++ b/packages/wallet/tsconfig.build.json @@ -13,6 +13,7 @@ { "path": "../controller-utils/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, { "path": "../messenger/tsconfig.build.json" }, + { "path": "../network-controller/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" }, { "path": "../storage-service/tsconfig.build.json" } ], diff --git a/packages/wallet/tsconfig.json b/packages/wallet/tsconfig.json index abbd559b3e9..a206b1babae 100644 --- a/packages/wallet/tsconfig.json +++ b/packages/wallet/tsconfig.json @@ -11,6 +11,7 @@ { "path": "../controller-utils/tsconfig.json" }, { "path": "../keyring-controller/tsconfig.json" }, { "path": "../messenger/tsconfig.json" }, + { "path": "../network-controller/tsconfig.json" }, { "path": "../remote-feature-flag-controller/tsconfig.json" }, { "path": "../storage-service/tsconfig.json" } ], diff --git a/yarn.lock b/yarn.lock index fe82fba5629..fb22996f033 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8941,6 +8941,7 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^1.2.0" + "@metamask/network-controller": "npm:^32.0.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 733942aa4554b5467d48e794677f93f784a78d7f Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Thu, 4 Jun 2026 13:44:42 +0200 Subject: [PATCH 02/11] Fix tests --- packages/wallet/src/Wallet.test.ts | 21 +++++++++++++++++++++ packages/wallet/src/types.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index a89602c3fcc..586f6ee58c1 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -28,6 +28,9 @@ async function setupWallet(): Promise { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -88,6 +91,9 @@ describe('Wallet', () => { keyringController: { encryptor: new MockEncryptor(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -134,6 +140,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -174,6 +183,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -249,6 +261,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -286,6 +301,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -361,6 +379,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, keyringController: { encryptor: new MockEncryptor() }, storageService: { storage: new InMemoryStorageAdapter() }, remoteFeatureFlagController: { diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 05bc1f365f4..6a66903dde0 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -8,9 +8,9 @@ import type { 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 type { RemoteFeatureFlagControllerInstanceOptions } from './initialization/instances/remote-feature-flag-controller/types'; import type { StorageServiceInstanceOptions } from './initialization/instances/storage-service/types'; -import type { NetworkControllerInstanceOptions } from './initialization/instances/network-controller/types'; import type { InitializationConfiguration } from './initialization/types'; export type WalletOptions = { From 1176ef2785492e720c62070410e911cc12d8e007 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 5 Jun 2026 09:58:31 +0200 Subject: [PATCH 03/11] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a87c5c0c7fb..bba0a54920f 100644 --- a/README.md +++ b/README.md @@ -615,6 +615,7 @@ linkStyle default opacity:0.5 wallet --> controller_utils; wallet --> keyring_controller; wallet --> messenger; + wallet --> network_controller; wallet --> remote_feature_flag_controller; wallet --> storage_service; wallet_cli --> base_controller; From c9f9d7c945ffa2b91bb2d4b6e960c9ede04fad52 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 16 Jun 2026 15:37:22 +0200 Subject: [PATCH 04/11] Add failoverUrls --- .../instances/network-controller/network-controller.ts | 1 + .../initialization/instances/network-controller/types.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts index ee1f9e21ff0..e0de4d10e22 100644 --- a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts +++ b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts @@ -16,6 +16,7 @@ export const networkController: InitializationConfiguration< state, messenger, infuraProjectId: options.infuraProjectId, + failoverUrls: options.failoverUrls, }), getMessenger: (parent) => { const networkControllerMessenger: NetworkControllerMessenger = diff --git a/packages/wallet/src/initialization/instances/network-controller/types.ts b/packages/wallet/src/initialization/instances/network-controller/types.ts index 69fbc03eaae..246e2ade719 100644 --- a/packages/wallet/src/initialization/instances/network-controller/types.ts +++ b/packages/wallet/src/initialization/instances/network-controller/types.ts @@ -1,6 +1,15 @@ +import { Hex } from '@metamask/utils'; + /** * Per-instance options for the wallet's `NetworkController`. */ export type NetworkControllerInstanceOptions = { + /** + * The API key for Infura, used to make requests to Infura. + */ infuraProjectId: string; + /** + * An optional map of available failover URLs for each chain ID. + */ + failoverUrls?: Record; }; From ebab5e85347d8b1e632b0554ec11afe9567b1c90 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 23 Jun 2026 10:58:34 +0200 Subject: [PATCH 05/11] Fix Yarn constraints post rebase --- packages/wallet/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 89cb74d05fe..f2a5d4d87e4 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -61,7 +61,7 @@ "@metamask/controller-utils": "^12.3.0", "@metamask/keyring-controller": "^27.1.0", "@metamask/messenger": "^1.2.0", - "@metamask/network-controller": "^32.0.0", + "@metamask/network-controller": "^33.0.0", "@metamask/remote-feature-flag-controller": "^4.2.2", "@metamask/scure-bip39": "^2.1.1", "@metamask/storage-service": "^1.0.2", diff --git a/yarn.lock b/yarn.lock index fb22996f033..b5beabe3eb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8941,7 +8941,7 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^1.2.0" - "@metamask/network-controller": "npm:^32.0.0" + "@metamask/network-controller": "npm:^33.0.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 10a1d2110bb53f81bf64660496c2e9704232692d Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 23 Jun 2026 11:02:03 +0200 Subject: [PATCH 06/11] Update CHANGELOG --- packages/wallet/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 395e3257b50..6059a853d6e 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- **BREAKING:** Add `NetworkController` initialization ([#9001](https://github.com/MetaMask/core/pull/9001)) + - Passing `instanceOptions.networkController.infuraProjectId` is now required. + ### Changed - Bump `@metamask/accounts-controller` from `^39.0.2` to `^39.0.3` ([#9231](https://github.com/MetaMask/core/pull/9231)) From 128158592c60fb79285356fe4fc360dc19afb7a5 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 23 Jun 2026 11:19:13 +0200 Subject: [PATCH 07/11] Wire up init --- packages/wallet/src/Wallet.test.ts | 13 +++++++++++++ packages/wallet/src/Wallet.ts | 13 +++++++++++++ .../network-controller/network-controller.ts | 7 +++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index 586f6ee58c1..96d211dc1f6 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -199,6 +199,19 @@ describe('Wallet', () => { expect(Object.keys(wallet.state)).toStrictEqual(['WithMeta', 'NoMeta']); }); + it('calls init on all instances and returns the results', async () => { + const wallet = await setupWallet(); + + const results = await wallet.init(); + + expect(results).toStrictEqual([ + { + status: 'fulfilled', + value: undefined, + }, + ]); + }); + it('disallows modifying the messenger', async () => { const wallet = await setupWallet(); diff --git a/packages/wallet/src/Wallet.ts b/packages/wallet/src/Wallet.ts index 5ba1566599d..1ef016a04be 100644 --- a/packages/wallet/src/Wallet.ts +++ b/packages/wallet/src/Wallet.ts @@ -123,6 +123,19 @@ export class Wallet { return this.#instances[name as keyof DefaultInstances]; } + /** + * Complete additional initialization of instantiated controllers or services after instantiating `Wallet`. + * + * @returns The results of all initialization calls. + */ + init() { + return Promise.allSettled( + Object.values(this.#instances) + .map((instance) => ('init' in instance ? instance.init() : null)) + .filter(Boolean), + ); + } + /** * Destroy the wallet instance. */ diff --git a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts index e0de4d10e22..31b3f99f1e9 100644 --- a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts +++ b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts @@ -27,8 +27,11 @@ export const networkController: InitializationConfiguration< parent.delegate({ messenger: networkControllerMessenger, - actions: ['ConnectivityController:getState'], - events: [], + actions: [ + 'ConnectivityController:getState', + 'RemoteFeatureFlagController:getState', + ], + events: ['RemoteFeatureFlagController:stateChange'], }); return networkControllerMessenger; From bc27234e8f960270e56b7f6d178106b8030c8205 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 23 Jun 2026 11:20:46 +0200 Subject: [PATCH 08/11] Update CHANGELOG --- packages/wallet/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 6059a853d6e..9484da9860b 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Add `NetworkController` initialization ([#9001](https://github.com/MetaMask/core/pull/9001)) - Passing `instanceOptions.networkController.infuraProjectId` is now required. +- Add the `Wallet.init` function which calls `init` on required instances ([#9001](https://github.com/MetaMask/core/pull/9001)) ### Changed From 592ba4f9fa0913a61156b97a8071c1f39d054566 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 23 Jun 2026 12:27:18 +0200 Subject: [PATCH 09/11] Fix lint --- packages/wallet/src/Wallet.test.ts | 7 +------ packages/wallet/src/Wallet.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index 96d211dc1f6..2cc683da6b8 100644 --- a/packages/wallet/src/Wallet.test.ts +++ b/packages/wallet/src/Wallet.test.ts @@ -204,12 +204,7 @@ describe('Wallet', () => { const results = await wallet.init(); - expect(results).toStrictEqual([ - { - status: 'fulfilled', - value: undefined, - }, - ]); + expect(results).toHaveLength(2); }); it('disallows modifying the messenger', async () => { diff --git a/packages/wallet/src/Wallet.ts b/packages/wallet/src/Wallet.ts index 1ef016a04be..fb1f632719c 100644 --- a/packages/wallet/src/Wallet.ts +++ b/packages/wallet/src/Wallet.ts @@ -128,11 +128,16 @@ export class Wallet { * * @returns The results of all initialization calls. */ - init() { + init(): Promise[]> { return Promise.allSettled( Object.values(this.#instances) - .map((instance) => ('init' in instance ? instance.init() : null)) - .filter(Boolean), + .filter( + (instance): instance is Extract => + // We do actually want to check the prototype here. + // eslint-disable-next-line no-restricted-syntax + 'init' in instance, + ) + .map(async (instance) => instance.init()), ); } From 9b4a53b67c97bbe1622e42005103d38fba785cc0 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Tue, 23 Jun 2026 12:59:41 +0200 Subject: [PATCH 10/11] Ignore lint error for :stateChange use --- .../instances/network-controller/network-controller.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts index 31b3f99f1e9..f48f37fed3f 100644 --- a/packages/wallet/src/initialization/instances/network-controller/network-controller.ts +++ b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts @@ -31,7 +31,11 @@ export const networkController: InitializationConfiguration< 'ConnectivityController:getState', 'RemoteFeatureFlagController:getState', ], - events: ['RemoteFeatureFlagController:stateChange'], + + events: [ + // eslint-disable-next-line no-restricted-syntax + 'RemoteFeatureFlagController:stateChange', + ], }); return networkControllerMessenger; From f882afddc9199ebbc567109c0f3efbdb9b1d56bf Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Wed, 24 Jun 2026 13:27:42 +0200 Subject: [PATCH 11/11] Check that instance.init is a function --- packages/wallet/src/Wallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wallet/src/Wallet.ts b/packages/wallet/src/Wallet.ts index fb1f632719c..01c945570e5 100644 --- a/packages/wallet/src/Wallet.ts +++ b/packages/wallet/src/Wallet.ts @@ -135,7 +135,7 @@ export class Wallet { (instance): instance is Extract => // We do actually want to check the prototype here. // eslint-disable-next-line no-restricted-syntax - 'init' in instance, + 'init' in instance && typeof instance.init === 'function', ) .map(async (instance) => instance.init()), );