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; diff --git a/packages/wallet/CHANGELOG.md b/packages/wallet/CHANGELOG.md index 395e3257b50..9484da9860b 100644 --- a/packages/wallet/CHANGELOG.md +++ b/packages/wallet/CHANGELOG.md @@ -7,6 +7,12 @@ 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. +- Add the `Wallet.init` function which calls `init` on required instances ([#9001](https://github.com/MetaMask/core/pull/9001)) + ### Changed - Bump `@metamask/accounts-controller` from `^39.0.2` to `^39.0.3` ([#9231](https://github.com/MetaMask/core/pull/9231)) diff --git a/packages/wallet/package.json b/packages/wallet/package.json index a60081b716b..f2a5d4d87e4 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": "^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/packages/wallet/src/Wallet.test.ts b/packages/wallet/src/Wallet.test.ts index a89602c3fcc..2cc683da6b8 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(), }, @@ -187,6 +199,14 @@ 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).toHaveLength(2); + }); + it('disallows modifying the messenger', async () => { const wallet = await setupWallet(); @@ -249,6 +269,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -286,6 +309,9 @@ describe('Wallet', () => { connectivityController: { connectivityAdapter: new AlwaysOnlineAdapter(), }, + networkController: { + infuraProjectId: 'fake-infura-project-id', + }, storageService: { storage: new InMemoryStorageAdapter(), }, @@ -361,6 +387,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/Wallet.ts b/packages/wallet/src/Wallet.ts index 5ba1566599d..01c945570e5 100644 --- a/packages/wallet/src/Wallet.ts +++ b/packages/wallet/src/Wallet.ts @@ -123,6 +123,24 @@ 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(): Promise[]> { + return Promise.allSettled( + Object.values(this.#instances) + .filter( + (instance): instance is Extract => + // We do actually want to check the prototype here. + // eslint-disable-next-line no-restricted-syntax + 'init' in instance && typeof instance.init === 'function', + ) + .map(async (instance) => instance.init()), + ); + } + /** * Destroy the wallet instance. */ 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..f48f37fed3f --- /dev/null +++ b/packages/wallet/src/initialization/instances/network-controller/network-controller.ts @@ -0,0 +1,43 @@ +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, + failoverUrls: options.failoverUrls, + }), + getMessenger: (parent) => { + const networkControllerMessenger: NetworkControllerMessenger = + new Messenger({ + namespace: 'NetworkController', + parent, + }); + + parent.delegate({ + messenger: networkControllerMessenger, + actions: [ + 'ConnectivityController:getState', + 'RemoteFeatureFlagController:getState', + ], + + events: [ + // eslint-disable-next-line no-restricted-syntax + 'RemoteFeatureFlagController:stateChange', + ], + }); + + 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..246e2ade719 --- /dev/null +++ b/packages/wallet/src/initialization/instances/network-controller/types.ts @@ -0,0 +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; +}; diff --git a/packages/wallet/src/types.ts b/packages/wallet/src/types.ts index 0bc6c0efaad..6a66903dde0 100644 --- a/packages/wallet/src/types.ts +++ b/packages/wallet/src/types.ts @@ -8,9 +8,10 @@ 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 { InitializationConfiguration } from './initialization/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..b5beabe3eb8 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:^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"