From 2a224609b59256069eecddc4bbfa0c9582785f6d Mon Sep 17 00:00:00 2001 From: Matthias Geihs Date: Fri, 22 Aug 2025 12:07:30 +0200 Subject: [PATCH 001/247] migrate messenger --- .../package.json | 1 + .../src/SeedlessOnboardingController.test.ts | 37 ++++++---- .../src/SeedlessOnboardingController.ts | 6 +- .../src/types.ts | 8 +- .../tests/__fixtures__/mockMessenger.ts | 73 +++++++++++++++++-- yarn.lock | 1 + 6 files changed, 97 insertions(+), 29 deletions(-) diff --git a/packages/seedless-onboarding-controller/package.json b/packages/seedless-onboarding-controller/package.json index beaaa22e364..50f0e5fa427 100644 --- a/packages/seedless-onboarding-controller/package.json +++ b/packages/seedless-onboarding-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/auth-network-utils": "^0.3.0", "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0", "@metamask/toprf-secure-backup": "^0.7.1", "@metamask/utils": "^11.4.2", "@noble/ciphers": "^1.3.0", diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts index 9f350689f87..5bdd6ad50d3 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts @@ -1,5 +1,4 @@ import { keccak256AndHexify } from '@metamask/auth-network-utils'; -import type { Messenger } from '@metamask/base-controller'; import type { EncryptionKey } from '@metamask/browser-passworder'; import { encrypt, @@ -9,6 +8,7 @@ import { decryptWithKey as decryptWithKeyBrowserPassworder, importKey as importKeyBrowserPassworder, } from '@metamask/browser-passworder'; +import type { Messenger } from '@metamask/messenger'; import { TOPRFError, type FetchAuthPubKeyResult, @@ -44,13 +44,17 @@ import { SeedlessOnboardingController, } from './SeedlessOnboardingController'; import type { - AllowedActions, - AllowedEvents, SeedlessOnboardingControllerMessenger, SeedlessOnboardingControllerOptions, SeedlessOnboardingControllerState, VaultEncryptor, } from './types'; +import type { + AllSeedlessOnboardingControllerActions, + AllSeedlessOnboardingControllerEvents, + baseMessengerName, + MockKeyringControllerMessenger, +} from '../tests/__fixtures__/mockMessenger'; import { mockSeedlessOnboardingMessenger } from '../tests/__fixtures__/mockMessenger'; import { handleMockSecretDataGet, @@ -115,7 +119,12 @@ type WithControllerCallback = ({ encryptor: VaultEncryptor; initialState: SeedlessOnboardingControllerState; messenger: SeedlessOnboardingControllerMessenger; - baseMessenger: Messenger; + baseMessenger: Messenger< + typeof baseMessengerName, + AllSeedlessOnboardingControllerActions, + AllSeedlessOnboardingControllerEvents + >; + keyringControllerMessenger: MockKeyringControllerMessenger; toprfClient: ToprfSecureBackup; mockRefreshJWTToken: jest.Mock; mockRevokeRefreshToken: jest.Mock; @@ -174,7 +183,8 @@ async function withController( ) { const [{ ...rest }, fn] = args.length === 2 ? args : [{}, args[0]]; const encryptor = new MockVaultEncryptor(); - const { messenger, baseMessenger } = mockSeedlessOnboardingMessenger(); + const { messenger, baseMessenger, keyringControllerMessenger } = + mockSeedlessOnboardingMessenger(); const mockRefreshJWTToken = jest.fn().mockResolvedValue({ idTokens: ['newIdToken'], @@ -222,6 +232,7 @@ async function withController( initialState: controller.state, messenger, baseMessenger, + keyringControllerMessenger, toprfClient, mockRefreshJWTToken, mockRevokeRefreshToken, @@ -2745,9 +2756,9 @@ describe('SeedlessOnboardingController', () => { withMockAuthenticatedUser: true, }), }, - async ({ controller, encryptor, baseMessenger }) => { + async ({ controller, encryptor, keyringControllerMessenger }) => { // unlock the controller - baseMessenger.publish('KeyringController:unlock'); + keyringControllerMessenger.publish('KeyringController:unlock'); await new Promise((resolve) => setTimeout(resolve, 100)); jest @@ -2860,14 +2871,14 @@ describe('SeedlessOnboardingController', () => { // Intentionally missing nodeAuthTokens, authConnectionId, userId }, }, - async ({ controller, baseMessenger, encryptor }) => { + async ({ controller, keyringControllerMessenger, encryptor }) => { // Mock the encryptor to pass verifyVaultPassword jest .spyOn(encryptor, 'decrypt') .mockResolvedValueOnce('mock decrypted data'); // unlock the controller - baseMessenger.publish('KeyringController:unlock'); + keyringControllerMessenger.publish('KeyringController:unlock'); await new Promise((resolve) => setTimeout(resolve, 100)); await expect( @@ -3098,7 +3109,7 @@ describe('SeedlessOnboardingController', () => { withMockAuthenticatedUser: true, }), }, - async ({ controller, baseMessenger, toprfClient }) => { + async ({ controller, keyringControllerMessenger, toprfClient }) => { await mockCreateToprfKeyAndBackupSeedPhrase( toprfClient, controller, @@ -3107,7 +3118,7 @@ describe('SeedlessOnboardingController', () => { MOCK_KEYRING_ID, ); - baseMessenger.publish('KeyringController:lock'); + keyringControllerMessenger.publish('KeyringController:lock'); await expect( controller.addNewSecretData(MOCK_SEED_PHRASE, SecretType.Mnemonic, { @@ -3127,7 +3138,7 @@ describe('SeedlessOnboardingController', () => { withMockAuthenticatedUser: true, }), }, - async ({ controller, baseMessenger }) => { + async ({ controller, keyringControllerMessenger }) => { await expect( controller.addNewSecretData(MOCK_SEED_PHRASE, SecretType.Mnemonic, { keyringId: MOCK_KEYRING_ID, @@ -3136,7 +3147,7 @@ describe('SeedlessOnboardingController', () => { SeedlessOnboardingControllerErrorMessage.ControllerLocked, ); - baseMessenger.publish('KeyringController:unlock'); + keyringControllerMessenger.publish('KeyringController:unlock'); await new Promise((resolve) => setTimeout(resolve, 100)); diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts index 2b9406fca91..163d8a678eb 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts @@ -1,6 +1,6 @@ import { keccak256AndHexify } from '@metamask/auth-network-utils'; import type { StateMetadata } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import type { KeyPair, RecoverEncryptionKeyResult, @@ -239,10 +239,10 @@ export class SeedlessOnboardingController extends BaseController< // setup subscriptions to the keyring lock event // when the keyring is locked (wallet is locked), the controller will be cleared of its credentials - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.setLocked(); }); - this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.messenger.subscribe('KeyringController:unlock', () => { this.#setUnlocked(); }); } diff --git a/packages/seedless-onboarding-controller/src/types.ts b/packages/seedless-onboarding-controller/src/types.ts index d871eeb2581..d9f8eef323d 100644 --- a/packages/seedless-onboarding-controller/src/types.ts +++ b/packages/seedless-onboarding-controller/src/types.ts @@ -1,4 +1,3 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { ControllerGetStateAction } from '@metamask/base-controller'; import type { ControllerStateChangeEvent } from '@metamask/base-controller'; import type { @@ -6,6 +5,7 @@ import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NodeAuthTokens } from '@metamask/toprf-secure-backup'; import type { MutexInterface } from 'async-mutex'; @@ -202,12 +202,10 @@ export type AllowedEvents = | KeyringControllerUnlockEvent; // Messenger -export type SeedlessOnboardingControllerMessenger = RestrictedMessenger< +export type SeedlessOnboardingControllerMessenger = Messenger< typeof controllerName, SeedlessOnboardingControllerActions | AllowedActions, - SeedlessOnboardingControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SeedlessOnboardingControllerEvents | AllowedEvents >; /** diff --git a/packages/seedless-onboarding-controller/tests/__fixtures__/mockMessenger.ts b/packages/seedless-onboarding-controller/tests/__fixtures__/mockMessenger.ts index 01a7124e147..b1a280aefb4 100644 --- a/packages/seedless-onboarding-controller/tests/__fixtures__/mockMessenger.ts +++ b/packages/seedless-onboarding-controller/tests/__fixtures__/mockMessenger.ts @@ -1,33 +1,89 @@ -import { Messenger } from '@metamask/base-controller'; +import type { + KeyringControllerLockEvent, + KeyringControllerUnlockEvent, +} from '@metamask/keyring-controller'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; +import { controllerName } from '../../src/constants'; import type { AllowedActions, AllowedEvents, SeedlessOnboardingControllerMessenger, } from '../../src/types'; +export type AllSeedlessOnboardingControllerActions = + MessengerActions; +export type AllSeedlessOnboardingControllerEvents = + MessengerEvents; + +export const baseMessengerName = 'Root'; + +export type MockKeyringControllerMessenger = Messenger< + 'KeyringController', + never, + KeyringControllerLockEvent | KeyringControllerUnlockEvent +>; + /** * creates a custom seedless onboarding messenger, in case tests need different permissions * * @returns base messenger, and messenger. You can pass this into the mocks below to mock messenger calls */ export function createCustomSeedlessOnboardingMessenger() { - const baseMessenger = new Messenger(); - const messenger = baseMessenger.getRestricted({ - name: 'SeedlessOnboardingController', - allowedActions: [], - allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'], + // Create the root messenger + const baseMessenger = new Messenger< + typeof baseMessengerName, + AllSeedlessOnboardingControllerActions, + AllSeedlessOnboardingControllerEvents + >({ namespace: baseMessengerName }); + + const keyringControllerMessenger = new Messenger< + 'KeyringController', + never, + KeyringControllerLockEvent | KeyringControllerUnlockEvent, + typeof baseMessenger + >({ namespace: 'KeyringController', parent: baseMessenger }); + + // Create the seedless onboarding controller messenger + const messenger = new Messenger< + typeof controllerName, + AllSeedlessOnboardingControllerActions, + AllSeedlessOnboardingControllerEvents, + typeof baseMessenger + >({ + namespace: controllerName, + parent: baseMessenger, + }); + + // Delegate external actions/events + keyringControllerMessenger.delegate({ + events: ['KeyringController:lock', 'KeyringController:unlock'], + messenger: baseMessenger, + }); + baseMessenger.delegate({ + events: ['KeyringController:lock', 'KeyringController:unlock'], + messenger, }); return { baseMessenger, messenger, + keyringControllerMessenger, }; } type OverrideMessengers = { - baseMessenger: Messenger; + baseMessenger: Messenger< + typeof baseMessengerName, + AllowedActions, + AllowedEvents + >; messenger: SeedlessOnboardingControllerMessenger; + keyringControllerMessenger: MockKeyringControllerMessenger; }; /** @@ -39,7 +95,7 @@ type OverrideMessengers = { export function mockSeedlessOnboardingMessenger( overrideMessengers?: OverrideMessengers, ) { - const { baseMessenger, messenger } = + const { baseMessenger, messenger, keyringControllerMessenger } = overrideMessengers ?? createCustomSeedlessOnboardingMessenger(); const mockKeyringGetAccounts = jest.fn(); @@ -50,6 +106,7 @@ export function mockSeedlessOnboardingMessenger( return { baseMessenger, messenger, + keyringControllerMessenger, mockKeyringGetAccounts, mockKeyringAddAccounts, mockAccountsListAccounts, diff --git a/yarn.lock b/yarn.lock index 45c85513834..1412a47ba94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4378,6 +4378,7 @@ __metadata: "@metamask/base-controller": "npm:^8.2.0" "@metamask/browser-passworder": "npm:^4.3.0" "@metamask/keyring-controller": "npm:^23.0.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/toprf-secure-backup": "npm:^0.7.1" "@metamask/utils": "npm:^11.4.2" "@noble/ciphers": "npm:^1.3.0" From 15d3a73ea75fec24f472debc5cae0a0cb9ef8685 Mon Sep 17 00:00:00 2001 From: Matthias Geihs Date: Fri, 22 Aug 2025 12:12:17 +0200 Subject: [PATCH 002/247] update CHANGELOG --- packages/seedless-onboarding-controller/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/seedless-onboarding-controller/CHANGELOG.md b/packages/seedless-onboarding-controller/CHANGELOG.md index 60b1c346b7d..0e52e0f3ac0 100644 --- a/packages/seedless-onboarding-controller/CHANGELOG.md +++ b/packages/seedless-onboarding-controller/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **Breaking:** Migrate to new messenger ([#6364](https://github.com/MetaMask/core/pull/6364)) - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [3.0.0] From ec61fb6410b8b81de2b4cddaa716fed5505300fb Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 25 Aug 2025 11:37:14 +0200 Subject: [PATCH 003/247] refactor: migrate `KeyringController` to `@metamask/messenger` --- packages/keyring-controller/package.json | 1 + .../src/KeyringController.test.ts | 25 +++------- .../src/KeyringController.ts | 46 +++++++++---------- yarn.lock | 1 + 4 files changed, 31 insertions(+), 42 deletions(-) diff --git a/packages/keyring-controller/package.json b/packages/keyring-controller/package.json index a5328f2d690..c16033fe31b 100644 --- a/packages/keyring-controller/package.json +++ b/packages/keyring-controller/package.json @@ -55,6 +55,7 @@ "@metamask/eth-simple-keyring": "^10.0.0", "@metamask/keyring-api": "^20.1.0", "@metamask/keyring-internal-api": "^8.1.0", + "@metamask/messenger": "^0.1.0", "@metamask/utils": "^11.4.2", "async-mutex": "^0.5.0", "ethereumjs-wallet": "^1.0.1", diff --git a/packages/keyring-controller/src/KeyringController.test.ts b/packages/keyring-controller/src/KeyringController.test.ts index d8dbfb9c274..11ffee026f7 100644 --- a/packages/keyring-controller/src/KeyringController.test.ts +++ b/packages/keyring-controller/src/KeyringController.test.ts @@ -1,7 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common'; import type { TypedTxData } from '@ethereumjs/tx'; import { TransactionFactory } from '@ethereumjs/tx'; -import { Messenger } from '@metamask/base-controller'; import { HdKeyring } from '@metamask/eth-hd-keyring'; import { normalize, @@ -14,6 +13,7 @@ import { import SimpleKeyring from '@metamask/eth-simple-keyring'; import type { EthKeyring } from '@metamask/keyring-internal-api'; import type { KeyringClass } from '@metamask/keyring-utils'; +import { Messenger } from '@metamask/messenger'; import { wordlist } from '@metamask/scure-bip39/dist/wordlists/english'; import { bytesToHex, isValidHexAddress, type Hex } from '@metamask/utils'; import * as sinon from 'sinon'; @@ -4272,28 +4272,17 @@ function stubKeyringClassWithAccount( .mockResolvedValue([account]); } -/** - * Build a messenger that includes all events used by the keyring - * controller. - * - * @returns The messenger. - */ -function buildMessenger() { - return new Messenger(); -} - /** * Build a restricted messenger for the keyring controller. * - * @param messenger - A messenger. * @returns The keyring controller restricted messenger. */ -function buildKeyringControllerMessenger(messenger = buildMessenger()) { - return messenger.getRestricted({ - name: 'KeyringController', - allowedActions: [], - allowedEvents: [], - }); +function buildKeyringControllerMessenger() { + return new Messenger< + 'KeyringController', + KeyringControllerActions, + KeyringControllerEvents + >({ namespace: 'KeyringController' }); } /** diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index 3d2f9524fe1..4d428cca1e0 100644 --- a/packages/keyring-controller/src/KeyringController.ts +++ b/packages/keyring-controller/src/KeyringController.ts @@ -1,7 +1,6 @@ import type { TypedTransaction, TypedTxData } from '@ethereumjs/tx'; import { isValidPrivate, getBinarySize } from '@ethereumjs/util'; -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import * as encryptorUtils from '@metamask/browser-passworder'; import { HdKeyring } from '@metamask/eth-hd-keyring'; import { normalize as ethNormalize } from '@metamask/eth-sig-util'; @@ -15,6 +14,7 @@ import type { } from '@metamask/keyring-api'; import type { EthKeyring } from '@metamask/keyring-internal-api'; import type { KeyringClass } from '@metamask/keyring-utils'; +import type { Messenger } from '@metamask/messenger'; import type { Eip1024EncryptedData, Hex, Json } from '@metamask/utils'; import { add0x, @@ -228,12 +228,10 @@ export type KeyringControllerEvents = | KeyringControllerUnlockEvent | KeyringControllerAccountRemovedEvent; -export type KeyringControllerMessenger = RestrictedMessenger< +export type KeyringControllerMessenger = Messenger< typeof name, KeyringControllerActions, - KeyringControllerEvents, - never, - never + KeyringControllerEvents >; export type KeyringControllerOptions = { @@ -1149,7 +1147,7 @@ export class KeyringController extends BaseController< } }); - this.messagingSystem.publish(`${name}:accountRemoved`, address); + this.messenger.publish(`${name}:accountRemoved`, address); } /** @@ -1171,7 +1169,7 @@ export class KeyringController extends BaseController< delete state.encryptionSalt; }); - this.messagingSystem.publish(`${name}:lock`); + this.messenger.publish(`${name}:lock`); }); } @@ -1671,77 +1669,77 @@ export class KeyringController extends BaseController< * actions. */ #registerMessageHandlers() { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:signMessage`, this.signMessage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:signEip7702Authorization`, this.signEip7702Authorization.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:signPersonalMessage`, this.signPersonalMessage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:signTypedMessage`, this.signTypedMessage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:decryptMessage`, this.decryptMessage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:getEncryptionPublicKey`, this.getEncryptionPublicKey.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:getAccounts`, this.getAccounts.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:getKeyringsByType`, this.getKeyringsByType.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:getKeyringForAccount`, this.getKeyringForAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:persistAllKeyrings`, this.persistAllKeyrings.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:prepareUserOperation`, this.prepareUserOperation.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:patchUserOperation`, this.patchUserOperation.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:signUserOperation`, this.signUserOperation.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:addNewAccount`, this.addNewAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:withKeyring`, this.withKeyring.bind(this), ); @@ -2415,7 +2413,7 @@ export class KeyringController extends BaseController< this.update((state) => { state.isUnlocked = true; }); - this.messagingSystem.publish(`${name}:unlock`); + this.messenger.publish(`${name}:unlock`); } /** diff --git a/yarn.lock b/yarn.lock index 45c85513834..d66a437d83f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3647,6 +3647,7 @@ __metadata: "@metamask/keyring-api": "npm:^20.1.0" "@metamask/keyring-internal-api": "npm:^8.1.0" "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/utils": "npm:^11.4.2" "@types/jest": "npm:^27.4.1" From f38a94c2a580e5439db94def67c5fed97e955dc9 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 25 Aug 2025 11:40:08 +0200 Subject: [PATCH 004/247] update changelog --- packages/keyring-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/keyring-controller/CHANGELOG.md b/packages/keyring-controller/CHANGELOG.md index 0c0491e1b20..b187f662ffc 100644 --- a/packages/keyring-controller/CHANGELOG.md +++ b/packages/keyring-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6370](https://github.com/MetaMask/core/pull/6370)) + - Previously, `KeyringController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [23.0.0] From cf248084af81c484c013ad71972e2d01f73c678d Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 00:21:13 +0200 Subject: [PATCH 005/247] refactor: migrate `AccountTreeController` to `@metamask/messenger` --- packages/account-tree-controller/package.json | 1 + .../src/AccountTreeController.test.ts | 235 +++++++++--------- .../src/AccountTreeController.ts | 53 ++-- .../account-tree-controller/src/rule.test.ts | 60 +---- .../src/rules/entropy.test.ts | 68 ++--- .../src/rules/keyring.test.ts | 68 ++--- .../src/rules/snap.test.ts | 69 ++--- packages/account-tree-controller/src/types.ts | 8 +- .../tests/mockMessenger.ts | 144 +++++++++++ .../account-tree-controller/tsconfig.json | 2 +- .../src/next/BaseController.ts | 2 +- yarn.lock | 1 + 12 files changed, 353 insertions(+), 358 deletions(-) create mode 100644 packages/account-tree-controller/tests/mockMessenger.ts diff --git a/packages/account-tree-controller/package.json b/packages/account-tree-controller/package.json index fe564355ef7..bed34daa831 100644 --- a/packages/account-tree-controller/package.json +++ b/packages/account-tree-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "lodash": "^4.17.21" diff --git a/packages/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index 1d314b74f81..b2da818a9b3 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -8,7 +8,6 @@ import { toMultichainAccountWalletId, type AccountGroupId, } from '@metamask/account-api'; -import { Messenger } from '@metamask/base-controller'; import { EthAccountType, EthMethod, @@ -25,14 +24,14 @@ import type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controlle import { AccountTreeController } from './AccountTreeController'; import { getAccountWalletNameFromKeyringType } from './rules/keyring'; +import { type AccountTreeControllerState } from './types'; import { - type AccountTreeControllerMessenger, - type AccountTreeControllerActions, - type AccountTreeControllerEvents, - type AccountTreeControllerState, - type AllowedActions, - type AllowedEvents, -} from './types'; + getAccountsControllerMessenger, + getAccountTreeControllerMessenger, + getKeyringControllerMessenger, + getRootMessenger, + getSnapControllerMessenger, +} from '../tests/mockMessenger'; // Local mock of EMPTY_ACCOUNT to avoid circular dependency const EMPTY_ACCOUNT_MOCK: InternalAccount = { @@ -190,46 +189,6 @@ const MOCK_HARDWARE_ACCOUNT_1: InternalAccount = { }, }; -/** - * Creates a new root messenger instance for testing. - * - * @returns A new Messenger instance. - */ -function getRootMessenger() { - return new Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >(); -} - -/** - * Retrieves a restricted messenger for the AccountTreeController. - * - * @param messenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). - * @returns The restricted messenger for the AccountTreeController. - */ -function getAccountTreeControllerMessenger( - messenger = getRootMessenger(), -): AccountTreeControllerMessenger { - return messenger.getRestricted({ - name: 'AccountTreeController', - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRenamed', - 'AccountsController:accountRemoved', - 'AccountsController:selectedAccountChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); -} - /** * Sets up the AccountTreeController for testing. * @@ -247,18 +206,20 @@ function setup({ keyrings = [], }: { state?: Partial; - messenger?: Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >; + messenger?: ReturnType; accounts?: InternalAccount[]; keyrings?: KeyringObject[]; } = {}): { controller: AccountTreeController; - messenger: Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents + messenger: ReturnType; + accountTreeControllerMessenger: ReturnType< + typeof getAccountTreeControllerMessenger + >; + accountsControllerMessenger: ReturnType< + typeof getAccountsControllerMessenger >; + keyringControllerMessenger: ReturnType; + snapControllerMessenger: ReturnType; spies: { consoleWarn: jest.SpyInstance; }; @@ -285,12 +246,16 @@ function setup({ getAccount: jest.fn(), }, }; + const accountsControllerMessenger = getAccountsControllerMessenger(messenger); + const keyringControllerMessenger = getKeyringControllerMessenger(messenger); + const snapControllerMessenger = getSnapControllerMessenger(messenger); if (accounts) { mocks.AccountsController.listMultichainAccounts.mockImplementation( () => mocks.AccountsController.accounts, ); - messenger.registerActionHandler( + + accountsControllerMessenger.registerActionHandler( 'AccountsController:listMultichainAccounts', mocks.AccountsController.listMultichainAccounts, ); @@ -298,19 +263,19 @@ function setup({ mocks.AccountsController.getAccount.mockImplementation((id) => mocks.AccountsController.accounts.find((account) => account.id === id), ); - messenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', mocks.AccountsController.getAccount, ); // Mock AccountsController:getSelectedAccount to return the first account - messenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', () => accounts[0] || MOCK_HD_ACCOUNT_1, ); // Mock AccountsController:setSelectedAccount - messenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:setSelectedAccount', jest.fn(), ); @@ -321,14 +286,16 @@ function setup({ isUnlocked: true, keyrings: mocks.KeyringController.keyrings, })); - messenger.registerActionHandler( + keyringControllerMessenger.registerActionHandler( 'KeyringController:getState', mocks.KeyringController.getState, ); } + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); const controller = new AccountTreeController({ - messenger: getAccountTreeControllerMessenger(messenger), + messenger: accountTreeControllerMessenger, state, }); @@ -339,6 +306,10 @@ function setup({ return { controller, messenger, + accountTreeControllerMessenger, + accountsControllerMessenger, + keyringControllerMessenger, + snapControllerMessenger, spies: { consoleWarn: consoleWarnSpy }, mocks, }; @@ -351,7 +322,7 @@ describe('AccountTreeController', () => { describe('init', () => { it('groups accounts by entropy source, then snapId, then wallet type', () => { - const { controller, messenger } = setup({ + const { controller, snapControllerMessenger } = setup({ accounts: [ MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2, @@ -362,7 +333,7 @@ describe('AccountTreeController', () => { keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); - messenger.registerActionHandler( + snapControllerMessenger.registerActionHandler( 'SnapController:get', () => // TODO: Update this to avoid the unknown cast if possible. @@ -546,12 +517,12 @@ describe('AccountTreeController', () => { }, } as const; - const { controller, messenger } = setup({ + const { controller, snapControllerMessenger } = setup({ accounts: [mockSnapAccountWithEntropy], keyrings: [MOCK_HD_KEYRING_2], }); - messenger.registerActionHandler( + snapControllerMessenger.registerActionHandler( 'SnapController:get', () => ({ @@ -578,12 +549,15 @@ describe('AccountTreeController', () => { }); it('fallback to Snap ID if Snap cannot be found', () => { - const { controller, messenger } = setup({ + const { controller, snapControllerMessenger } = setup({ accounts: [MOCK_SNAP_ACCOUNT_1], keyrings: [], }); - messenger.registerActionHandler('SnapController:get', () => undefined); // Snap won't be found. + snapControllerMessenger.registerActionHandler( + 'SnapController:get', + () => undefined, + ); // Snap won't be found. controller.init(); @@ -772,7 +746,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [mockHdAccount1, mockHdAccount2], keyrings: [MOCK_HD_KEYRING_1], }); @@ -780,7 +754,10 @@ describe('AccountTreeController', () => { // Create entropy wallets that will both get "Wallet" as base name, then get numbered controller.init(); - messenger.publish('AccountsController:accountRemoved', mockHdAccount1.id); + accountsControllerMessenger.publish( + 'AccountsController:accountRemoved', + mockHdAccount1.id, + ); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -838,14 +815,17 @@ describe('AccountTreeController', () => { }, }; - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [mockHdAccount1, mockHdAccount2], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); - messenger.publish('AccountsController:accountRemoved', mockHdAccount1.id); + accountsControllerMessenger.publish( + 'AccountsController:accountRemoved', + mockHdAccount1.id, + ); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -896,14 +876,17 @@ describe('AccountTreeController', () => { it('prunes an empty wallet if it holds no groups', () => { const mockHdAccount1: Bip44Account = MOCK_HD_ACCOUNT_1; - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [mockHdAccount1], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); - messenger.publish('AccountsController:accountRemoved', mockHdAccount1.id); + accountsControllerMessenger.publish( + 'AccountsController:accountRemoved', + mockHdAccount1.id, + ); expect(controller.state).toStrictEqual({ accountGroupsMetadata: {}, @@ -943,7 +926,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [mockHdAccount1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -951,7 +934,10 @@ describe('AccountTreeController', () => { // Create entropy wallets that will both get "Wallet" as base name, then get numbered controller.init(); - messenger.publish('AccountsController:accountAdded', mockHdAccount2); + accountsControllerMessenger.publish( + 'AccountsController:accountAdded', + mockHdAccount2, + ); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -1021,7 +1007,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, messenger, mocks } = setup({ + const { controller, accountsControllerMessenger, mocks } = setup({ accounts: [mockHdAccount1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1031,7 +1017,10 @@ describe('AccountTreeController', () => { mocks.KeyringController.keyrings = [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2]; mocks.AccountsController.accounts = [mockHdAccount1, mockHdAccount2]; - messenger.publish('AccountsController:accountAdded', mockHdAccount2); + accountsControllerMessenger.publish( + 'AccountsController:accountAdded', + mockHdAccount2, + ); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -1112,14 +1101,14 @@ describe('AccountTreeController', () => { describe('on AccountsController:accountRenamed', () => { it('renames a group in the tree if the renamed internal account is of EVM type, the group name is default and the internal account name is not default', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); const newName = 'New Account Name'; - messenger.publish('AccountsController:accountRenamed', { + accountsControllerMessenger.publish('AccountsController:accountRenamed', { ...MOCK_HD_ACCOUNT_1, metadata: { ...MOCK_HD_ACCOUNT_1.metadata, @@ -1151,7 +1140,7 @@ describe('AccountTreeController', () => { }); it('does not rename a group in the tree if the renamed internal account is of EVM type, but the group name is not default', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1167,7 +1156,7 @@ describe('AccountTreeController', () => { customGroupName, // Set a non-default group name ); - messenger.publish('AccountsController:accountRenamed', { + accountsControllerMessenger.publish('AccountsController:accountRenamed', { ...MOCK_HD_ACCOUNT_1, metadata: { ...MOCK_HD_ACCOUNT_1.metadata, @@ -1199,13 +1188,13 @@ describe('AccountTreeController', () => { }); it('does not rename a group in the tree if the renamed internal account is of EVM type, the group name is default and the internal account name is also default', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); - messenger.publish('AccountsController:accountRenamed', { + accountsControllerMessenger.publish('AccountsController:accountRenamed', { ...MOCK_HD_ACCOUNT_1, metadata: { ...MOCK_HD_ACCOUNT_1.metadata, @@ -1237,14 +1226,14 @@ describe('AccountTreeController', () => { }); it('does not rename an account in the tree if the renamed internal account is not of EVM type', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); const newName = 'New Account Name'; - messenger.publish('AccountsController:accountRenamed', { + accountsControllerMessenger.publish('AccountsController:accountRenamed', { ...MOCK_HD_ACCOUNT_1, type: SolAccountType.DataAccount, // Not an EVM account type metadata: { @@ -1333,7 +1322,7 @@ describe('AccountTreeController', () => { }); it('updates selectedAccountGroup when AccountsController selected account changes', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1341,7 +1330,7 @@ describe('AccountTreeController', () => { controller.init(); const initialGroup = controller.getSelectedAccountGroup(); - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:selectedAccountChange', MOCK_HD_ACCOUNT_2, ); @@ -1351,12 +1340,15 @@ describe('AccountTreeController', () => { }); it('updates AccountsController selected account (with EVM account) when selectedAccountGroup changes', () => { - const { controller, messenger } = setup({ + const { controller, accountTreeControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); - const setSelectedAccountSpy = jest.spyOn(messenger, 'call'); + const setSelectedAccountSpy = jest.spyOn( + accountTreeControllerMessenger, + 'call', + ); controller.init(); @@ -1388,7 +1380,7 @@ describe('AccountTreeController', () => { }, }, } as const; - const { controller, messenger } = setup({ + const { controller, accountTreeControllerMessenger } = setup({ accounts: [ MOCK_HD_ACCOUNT_1, nonEvmAccount2, // Wallet 2 > Account 1. @@ -1396,7 +1388,10 @@ describe('AccountTreeController', () => { keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); - const setSelectedAccountSpy = jest.spyOn(messenger, 'call'); + const setSelectedAccountSpy = jest.spyOn( + accountTreeControllerMessenger, + 'call', + ); controller.init(); @@ -1452,7 +1447,7 @@ describe('AccountTreeController', () => { }); it('is idempotent - receiving selectedAccountChange for account in same group should not update state', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1471,7 +1466,7 @@ describe('AccountTreeController', () => { const initialState = { ...controller.state }; - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:selectedAccountChange', MOCK_HD_ACCOUNT_1, ); @@ -1496,7 +1491,7 @@ describe('AccountTreeController', () => { }); it('handles AccountsController selectedAccountChange for account not in tree gracefully', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1509,7 +1504,7 @@ describe('AccountTreeController', () => { id: 'unknown-account-id', }; - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:selectedAccountChange', unknownAccount, ); @@ -1518,16 +1513,16 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when AccountsController returns EMPTY_ACCOUNT', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); // Unregister existing handler and register new one BEFORE init - messenger.unregisterActionHandler( + accountsControllerMessenger.unregisterActionHandler( 'AccountsController:getSelectedAccount', ); - messenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', () => EMPTY_ACCOUNT_MOCK, ); @@ -1547,7 +1542,7 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when selected account is not in tree', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1558,10 +1553,10 @@ describe('AccountTreeController', () => { id: 'unknown-account-id', }; - messenger.unregisterActionHandler( + accountsControllerMessenger.unregisterActionHandler( 'AccountsController:getSelectedAccount', ); - messenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', () => unknownAccount, ); @@ -1581,16 +1576,16 @@ describe('AccountTreeController', () => { }); it('returns empty string when no wallets exist and getSelectedAccount returns EMPTY_ACCOUNT', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [], keyrings: [], }); // Mock getSelectedAccount to return EMPTY_ACCOUNT_MOCK (id is '') BEFORE init - messenger.unregisterActionHandler( + accountsControllerMessenger.unregisterActionHandler( 'AccountsController:getSelectedAccount', ); - messenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', () => EMPTY_ACCOUNT_MOCK, ); @@ -1604,7 +1599,7 @@ describe('AccountTreeController', () => { describe('account removal and memory management', () => { it('cleans up reverse mapping and does not change selectedAccountGroup when removing from non-selected group', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1624,7 +1619,7 @@ describe('AccountTreeController', () => { const initialSelectedGroup = controller.getSelectedAccountGroup(); // Remove account from the second group (not selected) - tests false branch and reverse cleanup - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:accountRemoved', MOCK_HD_ACCOUNT_2.id, ); @@ -1633,7 +1628,7 @@ describe('AccountTreeController', () => { expect(controller.getSelectedAccountGroup()).toBe(initialSelectedGroup); // Test that subsequent selectedAccountChange for removed account is handled gracefully (indirect test of reverse cleanup) - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:selectedAccountChange', MOCK_HD_ACCOUNT_2, ); @@ -1641,7 +1636,7 @@ describe('AccountTreeController', () => { }); it('updates selectedAccountGroup when last account in selected group is removed and other groups exist', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1667,7 +1662,7 @@ describe('AccountTreeController', () => { ); // Remove the account from the selected group - tests true branch and findFirstNonEmptyGroup finding a group - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:accountRemoved', MOCK_HD_ACCOUNT_1.id, ); @@ -1677,7 +1672,7 @@ describe('AccountTreeController', () => { }); it('sets selectedAccountGroup to empty when no non-empty groups exist', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1685,7 +1680,7 @@ describe('AccountTreeController', () => { controller.init(); // Remove the only account - tests findFirstNonEmptyGroup returning empty string - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:accountRemoved', MOCK_HD_ACCOUNT_1.id, ); @@ -1695,7 +1690,7 @@ describe('AccountTreeController', () => { }); it('handles removal gracefully when account is not found in reverse mapping', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1705,14 +1700,17 @@ describe('AccountTreeController', () => { // Try to remove an account that was never added const unknownAccountId = 'unknown-account-id'; - messenger.publish('AccountsController:accountRemoved', unknownAccountId); + accountsControllerMessenger.publish( + 'AccountsController:accountRemoved', + unknownAccountId, + ); // State should remain unchanged expect(controller.state).toStrictEqual(initialState); }); it('handles edge cases gracefully in account removal', () => { - const { controller, messenger } = setup({ + const { controller, accountsControllerMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1720,7 +1718,7 @@ describe('AccountTreeController', () => { controller.init(); expect(() => { - messenger.publish( + accountsControllerMessenger.publish( 'AccountsController:accountRemoved', 'non-existent-account', ); @@ -2237,7 +2235,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, messenger, mocks } = setup({ + const { controller, accountsControllerMessenger, mocks } = setup({ accounts: [existingAccount], keyrings: [MOCK_HD_KEYRING_1], }); @@ -2246,7 +2244,10 @@ describe('AccountTreeController', () => { // Add the new account to the existing group mocks.AccountsController.accounts = [existingAccount, newAccount]; - messenger.publish('AccountsController:accountAdded', newAccount); + accountsControllerMessenger.publish( + 'AccountsController:accountAdded', + newAccount, + ); const expectedWalletId = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, diff --git a/packages/account-tree-controller/src/AccountTreeController.ts b/packages/account-tree-controller/src/AccountTreeController.ts index eaa4987a3ee..5ceefea9373 100644 --- a/packages/account-tree-controller/src/AccountTreeController.ts +++ b/packages/account-tree-controller/src/AccountTreeController.ts @@ -7,7 +7,7 @@ import type { import { AccountWalletType, select } from '@metamask/account-api'; import { type AccountId } from '@metamask/accounts-controller'; import type { StateMetadata } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import { isEvmAccountType } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; @@ -125,40 +125,34 @@ export class AccountTreeController extends BaseController< // Rules to apply to construct the wallets tree. this.#rules = [ // 1. We group by entropy-source - new EntropyRule(this.messagingSystem), + new EntropyRule(this.messenger), // 2. We group by Snap ID - new SnapRule(this.messagingSystem), + new SnapRule(this.messenger), // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts) - new KeyringRule(this.messagingSystem), + new KeyringRule(this.messenger), ]; - this.messagingSystem.subscribe( - 'AccountsController:accountAdded', - (account) => { - this.#handleAccountAdded(account); - }, - ); + this.messenger.subscribe('AccountsController:accountAdded', (account) => { + this.#handleAccountAdded(account); + }); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountRemoved', (accountId) => { this.#handleAccountRemoved(accountId); }, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:selectedAccountChange', (account) => { this.#handleSelectedAccountChange(account); }, ); - this.messagingSystem.subscribe( - 'AccountsController:accountRenamed', - (account) => { - this.#handleAccountRenamed(account); - }, - ); + this.messenger.subscribe('AccountsController:accountRenamed', (account) => { + this.#handleAccountRenamed(account); + }); this.#registerMessageHandlers(); } @@ -404,10 +398,7 @@ export class AccountTreeController extends BaseController< const accounts: InternalAccount[] = []; for (const id of group.accounts) { - const account = this.messagingSystem.call( - 'AccountsController:getAccount', - id, - ); + const account = this.messenger.call('AccountsController:getAccount', id); // For now, we're filtering undefined account, but I believe // throwing would be more appropriate here. @@ -660,9 +651,7 @@ export class AccountTreeController extends BaseController< * @returns The list of all internal accounts. */ #listAccounts(): InternalAccount[] { - return this.messagingSystem.call( - 'AccountsController:listMultichainAccounts', - ); + return this.messenger.call('AccountsController:listMultichainAccounts'); } /** @@ -726,7 +715,7 @@ export class AccountTreeController extends BaseController< // Update AccountsController - this will trigger selectedAccountChange event, // but our handler is idempotent so it won't cause infinite loop - this.messagingSystem.call( + this.messenger.call( 'AccountsController:setSelectedAccount', accountToSelect, ); @@ -741,7 +730,7 @@ export class AccountTreeController extends BaseController< #getDefaultSelectedAccountGroup(wallets: { [walletId: AccountWalletId]: AccountWalletObject; }): AccountGroupId | '' { - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getSelectedAccount', ); if (selectedAccount && selectedAccount.id) { @@ -812,7 +801,7 @@ export class AccountTreeController extends BaseController< if (group) { let candidate; for (const id of group.accounts) { - const account = this.messagingSystem.call( + const account = this.messenger.call( 'AccountsController:getAccount', id, ); @@ -855,7 +844,7 @@ export class AccountTreeController extends BaseController< } for (const id of group.accounts) { - const account = this.messagingSystem.call( + const account = this.messenger.call( 'AccountsController:getAccount', id, ); @@ -983,17 +972,17 @@ export class AccountTreeController extends BaseController< * Registers message handlers for the AccountTreeController. */ #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getSelectedAccountGroup`, this.getSelectedAccountGroup.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setSelectedAccountGroup`, this.setSelectedAccountGroup.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getAccountsFromSelectedAccountGroup`, this.getAccountsFromSelectedAccountGroup.bind(this), ); diff --git a/packages/account-tree-controller/src/rule.test.ts b/packages/account-tree-controller/src/rule.test.ts index 7bbadcc931e..af4cb81f066 100644 --- a/packages/account-tree-controller/src/rule.test.ts +++ b/packages/account-tree-controller/src/rule.test.ts @@ -4,7 +4,6 @@ import { toMultichainAccountGroupId, toMultichainAccountWalletId, } from '@metamask/account-api'; -import { Messenger } from '@metamask/base-controller'; import { EthAccountType, EthMethod, @@ -16,13 +15,11 @@ import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { AccountGroupObject } from './group'; import { BaseRule } from './rule'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from './types'; +import { + getAccountTreeControllerMessenger, + getAccountsControllerMessenger, + getRootMessenger, +} from '../tests/mockMessenger'; const ETH_EOA_METHODS = [ EthMethod.PersonalSign, @@ -56,53 +53,16 @@ const MOCK_HD_ACCOUNT_1: Bip44Account = { }, }; -/** - * Creates a new root messenger instance for testing. - * - * @returns A new Messenger instance. - */ -function getRootMessenger() { - return new Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >(); -} - -/** - * Retrieves a restricted messenger for the AccountTreeController. - * - * @param messenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). - * @returns The restricted messenger for the AccountTreeController. - */ -function getAccountTreeControllerMessenger( - messenger = getRootMessenger(), -): AccountTreeControllerMessenger { - return messenger.getRestricted({ - name: 'AccountTreeController', - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - 'AccountsController:selectedAccountChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); -} - describe('BaseRule', () => { describe('getComputedAccountGroupName', () => { it('returns empty string when account is not found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new BaseRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -130,9 +90,11 @@ describe('BaseRule', () => { it('returns account name when account is found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new BaseRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HD_ACCOUNT_1, ); diff --git a/packages/account-tree-controller/src/rules/entropy.test.ts b/packages/account-tree-controller/src/rules/entropy.test.ts index 40cd5574672..761c9f305ae 100644 --- a/packages/account-tree-controller/src/rules/entropy.test.ts +++ b/packages/account-tree-controller/src/rules/entropy.test.ts @@ -4,7 +4,6 @@ import { toMultichainAccountGroupId, toMultichainAccountWalletId, } from '@metamask/account-api'; -import { Messenger } from '@metamask/base-controller'; import { EthAccountType, EthMethod, @@ -15,14 +14,12 @@ import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { EntropyRule } from './entropy'; +import { + getAccountTreeControllerMessenger, + getAccountsControllerMessenger, + getRootMessenger, +} from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from '../types'; const ETH_EOA_METHODS = [ EthMethod.PersonalSign, @@ -62,53 +59,16 @@ const MOCK_HD_ACCOUNT_1: Bip44Account = { }, }; -/** - * Creates a new root messenger instance for testing. - * - * @returns A new Messenger instance. - */ -function getRootMessenger() { - return new Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >(); -} - -/** - * Retrieves a restricted messenger for the AccountTreeController. - * - * @param messenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). - * @returns The restricted messenger for the AccountTreeController. - */ -function getAccountTreeControllerMessenger( - messenger = getRootMessenger(), -): AccountTreeControllerMessenger { - return messenger.getRestricted({ - name: 'AccountTreeController', - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - 'AccountsController:selectedAccountChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); -} - describe('EntropyRule', () => { describe('getComputedAccountGroupName', () => { it('uses BaseRule implementation', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new EntropyRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HD_ACCOUNT_1, ); @@ -138,9 +98,11 @@ describe('EntropyRule', () => { it('returns empty string when account is not found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new EntropyRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -199,6 +161,8 @@ describe('EntropyRule', () => { it('getComputedAccountGroupName returns account name with EVM priority', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new EntropyRule(messenger); const mockEvmAccount: InternalAccount = { @@ -211,7 +175,7 @@ describe('EntropyRule', () => { }, }; - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => mockEvmAccount, ); @@ -239,9 +203,11 @@ describe('EntropyRule', () => { it('getComputedAccountGroupName returns empty string when no accounts found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new EntropyRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); diff --git a/packages/account-tree-controller/src/rules/keyring.test.ts b/packages/account-tree-controller/src/rules/keyring.test.ts index c2b116d2847..08bf92450d5 100644 --- a/packages/account-tree-controller/src/rules/keyring.test.ts +++ b/packages/account-tree-controller/src/rules/keyring.test.ts @@ -4,20 +4,17 @@ import { toAccountWalletId, AccountWalletType, } from '@metamask/account-api'; -import { Messenger } from '@metamask/base-controller'; import { EthAccountType, EthMethod, EthScope } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { KeyringRule, getAccountWalletNameFromKeyringType } from './keyring'; +import { + getAccountTreeControllerMessenger, + getAccountsControllerMessenger, + getRootMessenger, +} from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from '../types'; import type { AccountWalletObjectOf } from '../wallet'; describe('keyring', () => { @@ -67,52 +64,15 @@ describe('keyring', () => { }, }; - /** - * Creates a new root messenger instance for testing. - * - * @returns A new Messenger instance. - */ - function getRootMessenger() { - return new Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >(); - } - - /** - * Retrieves a restricted messenger for the AccountTreeController. - * - * @param messenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). - * @returns The restricted messenger for the AccountTreeController. - */ - function getAccountTreeControllerMessenger( - messenger = getRootMessenger(), - ): AccountTreeControllerMessenger { - return messenger.getRestricted({ - name: 'AccountTreeController', - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - 'AccountsController:selectedAccountChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); - } - describe('getComputedAccountGroupName', () => { it('uses BaseRule implementation', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new KeyringRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HARDWARE_ACCOUNT_1, ); @@ -139,9 +99,11 @@ describe('keyring', () => { it('returns empty string when account is not found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new KeyringRule(messenger); - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -178,10 +140,12 @@ describe('keyring', () => { it('getComputedAccountGroupName returns computed name from base class', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new KeyringRule(messenger); // Mock the AccountsController to always return the account - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HARDWARE_ACCOUNT_1, ); @@ -211,10 +175,12 @@ describe('keyring', () => { it('getComputedAccountGroupName returns empty string when account not found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new KeyringRule(messenger); // Mock the AccountsController to return undefined (account not found) - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); diff --git a/packages/account-tree-controller/src/rules/snap.test.ts b/packages/account-tree-controller/src/rules/snap.test.ts index 678cc55c2be..0abd1bb97de 100644 --- a/packages/account-tree-controller/src/rules/snap.test.ts +++ b/packages/account-tree-controller/src/rules/snap.test.ts @@ -12,14 +12,13 @@ import type { SnapId } from '@metamask/snaps-sdk'; import type { Snap } from '@metamask/snaps-utils'; import { SnapRule } from './snap'; +import { + getAccountTreeControllerMessenger, + getAccountsControllerMessenger, + getRootMessenger, + getSnapControllerMessenger, +} from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from '../types'; import type { AccountWalletObjectOf } from '../wallet'; const ETH_EOA_METHODS = [ @@ -58,54 +57,17 @@ const MOCK_SNAP_ACCOUNT_1: InternalAccount = { }, }; -/** - * Creates a new root messenger instance for testing. - * - * @returns A new Messenger instance. - */ -function getRootMessenger() { - return new Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >(); -} - -/** - * Retrieves a restricted messenger for the AccountTreeController. - * - * @param messenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). - * @returns The restricted messenger for the AccountTreeController. - */ -function getAccountTreeControllerMessenger( - messenger = getRootMessenger(), -): AccountTreeControllerMessenger { - return messenger.getRestricted({ - name: 'AccountTreeController', - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - 'AccountsController:selectedAccountChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); -} - describe('SnapRule', () => { describe('getComputedAccountGroupName', () => { it('returns computed name from base class', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new SnapRule(messenger); // Mock the AccountsController to return an account - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_SNAP_ACCOUNT_1, ); @@ -132,10 +94,12 @@ describe('SnapRule', () => { it('returns empty string when account not found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const accountsControllerMessenger = + getAccountsControllerMessenger(rootMessenger); const rule = new SnapRule(messenger); // Mock the AccountsController to return undefined (account not found) - rootMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -175,10 +139,11 @@ describe('SnapRule', () => { it('returns snap proposed name when available', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const snapControllerMessenger = getSnapControllerMessenger(rootMessenger); const rule = new SnapRule(messenger); // Mock SnapController to return snap with proposed name - rootMessenger.registerActionHandler( + snapControllerMessenger.registerActionHandler( 'SnapController:get', () => MOCK_SNAP_1 as unknown as Snap, ); @@ -199,6 +164,7 @@ describe('SnapRule', () => { it('returns cleaned snap ID when no proposed name available', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const snapControllerMessenger = getSnapControllerMessenger(rootMessenger); const rule = new SnapRule(messenger); const snapWithoutProposedName = { @@ -213,7 +179,7 @@ describe('SnapRule', () => { }; // Mock SnapController to return snap without proposed name - rootMessenger.registerActionHandler( + snapControllerMessenger.registerActionHandler( 'SnapController:get', () => snapWithoutProposedName as unknown as Snap, ); @@ -238,10 +204,11 @@ describe('SnapRule', () => { it('returns cleaned snap ID when snap not found', () => { const rootMessenger = getRootMessenger(); const messenger = getAccountTreeControllerMessenger(rootMessenger); + const snapControllerMessenger = getSnapControllerMessenger(rootMessenger); const rule = new SnapRule(messenger); // Mock SnapController to return undefined (snap not found) - rootMessenger.registerActionHandler( + snapControllerMessenger.registerActionHandler( 'SnapController:get', () => undefined, ); diff --git a/packages/account-tree-controller/src/types.ts b/packages/account-tree-controller/src/types.ts index a268cceaa61..e2c4196673e 100644 --- a/packages/account-tree-controller/src/types.ts +++ b/packages/account-tree-controller/src/types.ts @@ -12,9 +12,9 @@ import type { import { type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, } from '@metamask/base-controller'; import type { KeyringControllerGetStateAction } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers'; import type { @@ -108,10 +108,8 @@ export type AllowedEvents = export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent; -export type AccountTreeControllerMessenger = RestrictedMessenger< +export type AccountTreeControllerMessenger = Messenger< typeof controllerName, AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AccountTreeControllerEvents | AllowedEvents >; diff --git a/packages/account-tree-controller/tests/mockMessenger.ts b/packages/account-tree-controller/tests/mockMessenger.ts new file mode 100644 index 00000000000..77799ea8b36 --- /dev/null +++ b/packages/account-tree-controller/tests/mockMessenger.ts @@ -0,0 +1,144 @@ +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; +import type { GetSnap } from '@metamask/snaps-controllers'; + +import type { + AccountsControllerAccountAddedEvent, + AccountsControllerAccountRemovedEvent, + AccountsControllerAccountRenamedEvent, + AccountsControllerGetAccountAction, + AccountsControllerGetSelectedAccountAction, + AccountsControllerListMultichainAccountsAction, + AccountsControllerSelectedAccountChangeEvent, + AccountsControllerSetSelectedAccountAction, +} from '../../accounts-controller/src/AccountsController'; +import type { KeyringControllerGetStateAction } from '../../keyring-controller/src/KeyringController'; +import type { AccountTreeControllerMessenger } from '../src/types'; + +type AllAccountTreeControllerActions = + MessengerActions; + +type AllAccountTreeControllerEvents = + MessengerEvents; + +/** + * Creates a new root messenger instance for testing. + * + * @returns A new Messenger instance. + */ +export function getRootMessenger() { + return new Messenger< + 'Root', + AllAccountTreeControllerActions, + AllAccountTreeControllerEvents + >({ namespace: 'Root' }); +} + +/** + * Retrieves a messenger for the AccountTreeController. + * + * @param rootMessenger - The root messenger instance. + * @returns The messenger for the AccountTreeController. + */ +export function getAccountTreeControllerMessenger( + rootMessenger: ReturnType, +): AccountTreeControllerMessenger { + const accountTreeControllerMessenger = new Messenger< + 'AccountTreeController', + AllAccountTreeControllerActions, + AllAccountTreeControllerEvents, + typeof rootMessenger + >({ namespace: 'AccountTreeController', parent: rootMessenger }); + rootMessenger.delegate({ + messenger: accountTreeControllerMessenger, + events: [ + 'AccountsController:accountAdded', + 'AccountsController:accountRemoved', + 'AccountsController:accountRenamed', + 'AccountsController:selectedAccountChange', + ], + actions: [ + 'AccountsController:listMultichainAccounts', + 'AccountsController:getAccount', + 'AccountsController:getSelectedAccount', + 'AccountsController:setSelectedAccount', + 'KeyringController:getState', + 'SnapController:get', + ], + }); + return accountTreeControllerMessenger; +} + +/** + * Retrieves a messenger for the AccountsController. + * + * @param rootMessenger - The root messenger instance. + * @returns The messenger for the AccountsController. + */ +export function getAccountsControllerMessenger( + rootMessenger: ReturnType, +): Messenger< + 'AccountsController', + | AccountsControllerGetAccountAction + | AccountsControllerGetSelectedAccountAction + | AccountsControllerListMultichainAccountsAction + | AccountsControllerSetSelectedAccountAction, + | AccountsControllerAccountAddedEvent + | AccountsControllerAccountRemovedEvent + | AccountsControllerAccountRenamedEvent + | AccountsControllerSelectedAccountChangeEvent, + typeof rootMessenger +> { + return new Messenger< + 'AccountsController', + | AccountsControllerGetAccountAction + | AccountsControllerGetSelectedAccountAction + | AccountsControllerListMultichainAccountsAction + | AccountsControllerSetSelectedAccountAction, + | AccountsControllerAccountAddedEvent + | AccountsControllerAccountRemovedEvent + | AccountsControllerAccountRenamedEvent + | AccountsControllerSelectedAccountChangeEvent, + typeof rootMessenger + >({ namespace: 'AccountsController', parent: rootMessenger }); +} + +/** + * Retrieves a messenger for the KeyringController. + * + * @param rootMessenger - The root messenger instance. + * @returns The messenger for the KeyringController. + */ +export function getKeyringControllerMessenger( + rootMessenger: ReturnType, +): Messenger< + 'KeyringController', + KeyringControllerGetStateAction, + never, + typeof rootMessenger +> { + return new Messenger< + 'KeyringController', + KeyringControllerGetStateAction, + never, + typeof rootMessenger + >({ namespace: 'KeyringController', parent: rootMessenger }); +} + +/** + * Retrieves a messenger for the SnapController. + * + * @param rootMessenger - The root messenger instance. + * @returns The messenger for the SnapController. + */ +export function getSnapControllerMessenger( + rootMessenger: ReturnType, +): Messenger<'SnapController', GetSnap, never, typeof rootMessenger> { + return new Messenger<'SnapController', GetSnap, never, typeof rootMessenger>({ + namespace: 'SnapController', + parent: rootMessenger, + }); +} diff --git a/packages/account-tree-controller/tsconfig.json b/packages/account-tree-controller/tsconfig.json index 8b6228af6b8..df1ebfb08b5 100644 --- a/packages/account-tree-controller/tsconfig.json +++ b/packages/account-tree-controller/tsconfig.json @@ -14,5 +14,5 @@ "path": "../accounts-controller" } ], - "include": ["../../types", "./src"] + "include": ["../../types", "./src", "./tests"] } diff --git a/packages/base-controller/src/next/BaseController.ts b/packages/base-controller/src/next/BaseController.ts index 6e25ee9aefb..05a9317794c 100644 --- a/packages/base-controller/src/next/BaseController.ts +++ b/packages/base-controller/src/next/BaseController.ts @@ -171,7 +171,7 @@ export class BaseController< /** * The controller messenger. * - * This is the same as the `messagingSystem` property, but has a type that only lets us use + * This is the same as the `messenger` property, but has a type that only lets us use * actions and events that are part of the `BaseController` class. */ readonly #messenger: Messenger< diff --git a/yarn.lock b/yarn.lock index 45c85513834..899824de18b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2407,6 +2407,7 @@ __metadata: "@metamask/base-controller": "npm:^8.2.0" "@metamask/keyring-api": "npm:^20.1.0" "@metamask/keyring-controller": "npm:^23.0.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" From 83294c8eaa7b53e61b9681f619497677389bdc0e Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 00:33:16 +0200 Subject: [PATCH 006/247] fix: re-delegate after unregister and register action --- .../src/AccountTreeController.test.ts | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index b2da818a9b3..e745355d94d 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -1513,7 +1513,12 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when AccountsController returns EMPTY_ACCOUNT', () => { - const { controller, accountsControllerMessenger } = setup({ + const { + controller, + messenger, + accountsControllerMessenger, + accountTreeControllerMessenger, + } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1526,6 +1531,10 @@ describe('AccountTreeController', () => { 'AccountsController:getSelectedAccount', () => EMPTY_ACCOUNT_MOCK, ); + messenger.delegate({ + messenger: accountTreeControllerMessenger, + actions: ['AccountsController:getSelectedAccount'], + }); controller.init(); @@ -1542,7 +1551,12 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when selected account is not in tree', () => { - const { controller, accountsControllerMessenger } = setup({ + const { + controller, + messenger, + accountsControllerMessenger, + accountTreeControllerMessenger, + } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1560,6 +1574,10 @@ describe('AccountTreeController', () => { 'AccountsController:getSelectedAccount', () => unknownAccount, ); + messenger.delegate({ + messenger: accountTreeControllerMessenger, + actions: ['AccountsController:getSelectedAccount'], + }); controller.init(); @@ -1576,7 +1594,12 @@ describe('AccountTreeController', () => { }); it('returns empty string when no wallets exist and getSelectedAccount returns EMPTY_ACCOUNT', () => { - const { controller, accountsControllerMessenger } = setup({ + const { + controller, + messenger, + accountsControllerMessenger, + accountTreeControllerMessenger, + } = setup({ accounts: [], keyrings: [], }); @@ -1589,6 +1612,10 @@ describe('AccountTreeController', () => { 'AccountsController:getSelectedAccount', () => EMPTY_ACCOUNT_MOCK, ); + messenger.delegate({ + messenger: accountTreeControllerMessenger, + actions: ['AccountsController:getSelectedAccount'], + }); controller.init(); From 75b539810ed013e5fcc19c929ccfabd42ee0fd6e Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 00:40:04 +0200 Subject: [PATCH 007/247] update changelog --- packages/account-tree-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index 0c15875e48b..93f92d923dc 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) + - Previously, `AccountTreeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [0.10.0] From 33c4ea262a9b5333d0a96a8960094351222b5659 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 00:43:41 +0200 Subject: [PATCH 008/247] revert unintentional change --- packages/base-controller/src/next/BaseController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/base-controller/src/next/BaseController.ts b/packages/base-controller/src/next/BaseController.ts index 05a9317794c..6e25ee9aefb 100644 --- a/packages/base-controller/src/next/BaseController.ts +++ b/packages/base-controller/src/next/BaseController.ts @@ -171,7 +171,7 @@ export class BaseController< /** * The controller messenger. * - * This is the same as the `messenger` property, but has a type that only lets us use + * This is the same as the `messagingSystem` property, but has a type that only lets us use * actions and events that are part of the `BaseController` class. */ readonly #messenger: Messenger< From a8d9b51cdeba8684bd412d8d327a093c2a956fd9 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 10:28:45 +0200 Subject: [PATCH 009/247] fix lint --- packages/account-tree-controller/src/rules/snap.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/account-tree-controller/src/rules/snap.test.ts b/packages/account-tree-controller/src/rules/snap.test.ts index 0abd1bb97de..e72c839be47 100644 --- a/packages/account-tree-controller/src/rules/snap.test.ts +++ b/packages/account-tree-controller/src/rules/snap.test.ts @@ -4,7 +4,6 @@ import { toAccountWalletId, AccountWalletType, } from '@metamask/account-api'; -import { Messenger } from '@metamask/base-controller'; import { EthAccountType, EthMethod, EthScope } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; From 752ab8a8863cc8e999f769da57e01fb1c38934d2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:08:00 +0200 Subject: [PATCH 010/247] refactor: migrate `AddressBookController` to `@metamask/messenger` --- packages/address-book-controller/package.json | 1 + .../src/AddressBookController.test.ts | 25 +++++++++++-------- .../src/AddressBookController.ts | 23 +++++++---------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/address-book-controller/package.json b/packages/address-book-controller/package.json index 35b5e54aea5..8b0e34c7218 100644 --- a/packages/address-book-controller/package.json +++ b/packages/address-book-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.2.0", "@metamask/controller-utils": "^11.12.0", + "@metamask/messenger": "^0.1.0", "@metamask/utils": "^11.4.2" }, "devDependencies": { diff --git a/packages/address-book-controller/src/AddressBookController.test.ts b/packages/address-book-controller/src/AddressBookController.test.ts index 060948a59cb..00710157d2f 100644 --- a/packages/address-book-controller/src/AddressBookController.test.ts +++ b/packages/address-book-controller/src/AddressBookController.test.ts @@ -1,5 +1,5 @@ -import { Messenger } from '@metamask/base-controller'; import { toHex } from '@metamask/controller-utils'; +import { Messenger } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import type { @@ -20,17 +20,22 @@ import { * @returns Test fixtures including messenger, controller, and event listeners */ function arrangeMocks() { - const messenger = new Messenger< + const rootMessenger = new Messenger< + 'Root', AddressBookControllerActions, AddressBookControllerEvents - >(); - const restrictedMessenger = messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], + >({ namespace: 'Root' }); + const addressBookControllerMessenger = new Messenger< + typeof controllerName, + AddressBookControllerActions, + AddressBookControllerEvents, + typeof rootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, }); const controller = new AddressBookController({ - messenger: restrictedMessenger, + messenger: addressBookControllerMessenger, }); // Set up mock event listeners @@ -38,11 +43,11 @@ function arrangeMocks() { const contactDeletedListener = jest.fn(); // Subscribe to events - messenger.subscribe( + rootMessenger.subscribe( 'AddressBookController:contactUpdated' as AddressBookControllerContactUpdatedEvent['type'], contactUpdatedListener, ); - messenger.subscribe( + rootMessenger.subscribe( 'AddressBookController:contactDeleted' as AddressBookControllerContactDeletedEvent['type'], contactDeletedListener, ); diff --git a/packages/address-book-controller/src/AddressBookController.ts b/packages/address-book-controller/src/AddressBookController.ts index b7637b22049..c9825e24571 100644 --- a/packages/address-book-controller/src/AddressBookController.ts +++ b/packages/address-book-controller/src/AddressBookController.ts @@ -1,9 +1,8 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import { normalizeEnsName, isValidHexAddress, @@ -11,6 +10,7 @@ import { toChecksumHexAddress, toHex, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; /** @@ -165,12 +165,10 @@ export const getDefaultAddressBookControllerState = /** * The messenger of the {@link AddressBookController} for communication. */ -export type AddressBookControllerMessenger = RestrictedMessenger< +export type AddressBookControllerMessenger = Messenger< typeof controllerName, AddressBookControllerActions, - AddressBookControllerEvents, - never, - never + AddressBookControllerEvents >; /** @@ -270,7 +268,7 @@ export class AddressBookController extends BaseController< // These entries with chainId='*' are the wallet's own accounts (internal MetaMask accounts), // not user-created contacts. They don't need to trigger sync events. if (String(chainId) !== WALLET_ACCOUNTS_CHAIN_ID) { - this.messagingSystem.publish( + this.messenger.publish( 'AddressBookController:contactDeleted', deletedEntry, ); @@ -330,10 +328,7 @@ export class AddressBookController extends BaseController< // These entries with chainId='*' are the wallet's own accounts (internal MetaMask accounts), // not user-created contacts. They don't need to trigger sync events. if (String(chainId) !== WALLET_ACCOUNTS_CHAIN_ID) { - this.messagingSystem.publish( - 'AddressBookController:contactUpdated', - entry, - ); + this.messenger.publish('AddressBookController:contactUpdated', entry); } return true; @@ -343,15 +338,15 @@ export class AddressBookController extends BaseController< * Registers message handlers for the AddressBookController. */ #registerMessageHandlers() { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:list`, this.list.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:set`, this.set.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:delete`, this.delete.bind(this), ); From 82ec30e8c9e7f9f1c8945b2ab354d425f734f9b4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:10:14 +0200 Subject: [PATCH 011/247] update changelog --- packages/address-book-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/address-book-controller/CHANGELOG.md b/packages/address-book-controller/CHANGELOG.md index 2e69e2e245d..ea3afd73b38 100644 --- a/packages/address-book-controller/CHANGELOG.md +++ b/packages/address-book-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) + - Previously, `AddressBookController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.2.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) - Bump `@metamask/controller-utils` from `^11.11.0` to `^11.12.0` ([#6303](https://github.com/MetaMask/core/pull/6303)) From 74782eec991968593097f9dd0dcd82ddbde4cfd8 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:11:29 +0200 Subject: [PATCH 012/247] update pr link --- packages/address-book-controller/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/address-book-controller/CHANGELOG.md b/packages/address-book-controller/CHANGELOG.md index ea3afd73b38..28556c8b226 100644 --- a/packages/address-book-controller/CHANGELOG.md +++ b/packages/address-book-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6383](https://github.com/MetaMask/core/pull/6383)) - Previously, `AddressBookController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.2.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) - Bump `@metamask/controller-utils` from `^11.11.0` to `^11.12.0` ([#6303](https://github.com/MetaMask/core/pull/6303)) From 75606e1b54586c31fd9f97dd2ff991aacf35dbdf Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:18:22 +0200 Subject: [PATCH 013/247] refactor: migrate `AnnouncementController` to `@metamask/messenger` --- packages/announcement-controller/package.json | 3 +- .../src/AnnouncementController.test.ts | 29 +++++++++++-------- .../src/AnnouncementController.ts | 10 +++---- yarn.lock | 1 + 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/announcement-controller/package.json b/packages/announcement-controller/package.json index 695542500f6..bd3c080cd96 100644 --- a/packages/announcement-controller/package.json +++ b/packages/announcement-controller/package.json @@ -47,7 +47,8 @@ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { - "@metamask/base-controller": "^8.2.0" + "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/announcement-controller/src/AnnouncementController.test.ts b/packages/announcement-controller/src/AnnouncementController.test.ts index 56991f6ff53..52bf0cae9cf 100644 --- a/packages/announcement-controller/src/AnnouncementController.test.ts +++ b/packages/announcement-controller/src/AnnouncementController.test.ts @@ -1,4 +1,4 @@ -import { Messenger } from '@metamask/base-controller'; +import { Messenger } from '@metamask/messenger'; import type { AnnouncementControllerState, @@ -16,15 +16,20 @@ const name = 'AnnouncementController'; * * @returns A restricted controller messenger. */ -function getRestrictedMessenger() { +function getMessenger() { const messenger = new Messenger< + 'Root', AnnouncementControllerActions, AnnouncementControllerEvents - >(); - return messenger.getRestricted({ - name, - allowedActions: [], - allowedEvents: [], + >({ namespace: 'Root' }); + return new Messenger< + typeof name, + AnnouncementControllerActions, + AnnouncementControllerEvents, + typeof messenger + >({ + namespace: name, + parent: messenger, }); } const allAnnouncements: AnnouncementMap = { @@ -89,7 +94,7 @@ const state2: AnnouncementControllerState = { describe('announcement controller', () => { it('should add announcement to state', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); expect(Object.keys(controller.state.announcements)).toHaveLength(2); @@ -110,7 +115,7 @@ describe('announcement controller', () => { it('should add new announcement to state and a new announcement should be created with isShown as false', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), state: state1, allAnnouncements: allAnnouncements2, }); @@ -123,7 +128,7 @@ describe('announcement controller', () => { describe('resetViewed', () => { it('resets all announcement isShown states to false', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), state: state2, allAnnouncements: allAnnouncements2, }); @@ -142,7 +147,7 @@ describe('announcement controller', () => { describe('update viewed announcements', () => { it('should update isShown status', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), state: state2, allAnnouncements: allAnnouncements2, }); @@ -154,7 +159,7 @@ describe('announcement controller', () => { it('should update isShown of more than one announcement', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), state: state2, allAnnouncements: allAnnouncements2, }); diff --git a/packages/announcement-controller/src/AnnouncementController.ts b/packages/announcement-controller/src/AnnouncementController.ts index 8bb1bb35c65..de3e074c366 100644 --- a/packages/announcement-controller/src/AnnouncementController.ts +++ b/packages/announcement-controller/src/AnnouncementController.ts @@ -1,9 +1,9 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; type ViewedAnnouncement = { [id: number]: boolean; @@ -66,12 +66,10 @@ const metadata = { }, }; -export type AnnouncementControllerMessenger = RestrictedMessenger< +export type AnnouncementControllerMessenger = Messenger< typeof controllerName, AnnouncementControllerActions, - AnnouncementControllerEvents, - never, - never + AnnouncementControllerEvents >; /** diff --git a/yarn.lock b/yarn.lock index a431876c636..db3f8c6abf9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2507,6 +2507,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.1.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" From fe2644056ec6f8b4ea63d8cc23dc3c3b106dca0c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:20:40 +0200 Subject: [PATCH 014/247] update changelog --- packages/announcement-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/announcement-controller/CHANGELOG.md b/packages/announcement-controller/CHANGELOG.md index b408b0390a6..4ec664f877c 100644 --- a/packages/announcement-controller/CHANGELOG.md +++ b/packages/announcement-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6384](https://github.com/MetaMask/core/pull/6384)) + - Previously, `AnnouncementController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.0` to `^8.2.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) ## [7.0.3] From dfd33e989d7a0cf1700f7338f011927dd9ed15a1 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:22:08 +0200 Subject: [PATCH 015/247] update lockfile --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index a431876c636..eb517cdb6d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2490,6 +2490,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" "@metamask/controller-utils": "npm:^11.12.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/utils": "npm:^11.4.2" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" From e38749c7ebaca2cce2ce2976db688d725b005224 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:31:04 +0200 Subject: [PATCH 016/247] refactor: migrate `AppMetadataController` to `@metamask/messenger` --- packages/app-metadata-controller/package.json | 3 ++- .../src/AppMetadataController.test.ts | 16 ++++++++++------ .../src/AppMetadataController.ts | 10 ++++------ yarn.lock | 1 + 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/app-metadata-controller/package.json b/packages/app-metadata-controller/package.json index a1ab3d41312..6124821ac84 100644 --- a/packages/app-metadata-controller/package.json +++ b/packages/app-metadata-controller/package.json @@ -47,7 +47,8 @@ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { - "@metamask/base-controller": "^8.2.0" + "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/app-metadata-controller/src/AppMetadataController.test.ts b/packages/app-metadata-controller/src/AppMetadataController.test.ts index 5bef4d66462..1a05b323966 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.test.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.test.ts @@ -1,4 +1,4 @@ -import { Messenger } from '@metamask/base-controller'; +import { Messenger } from '@metamask/messenger'; import { AppMetadataController, @@ -148,12 +148,16 @@ function withController( ): ReturnValue { const [options = {}, fn] = args.length === 2 ? args : [{}, args[0]]; - const messenger = new Messenger(); + const rootMessenger = new Messenger<'Root'>({ namespace: 'Root' }); - const appMetadataControllerMessenger = messenger.getRestricted({ - name: 'AppMetadataController', - allowedActions: [], - allowedEvents: [], + const appMetadataControllerMessenger = new Messenger< + 'AppMetadataController', + never, + never, + typeof rootMessenger + >({ + namespace: 'AppMetadataController', + parent: rootMessenger, }); return fn({ diff --git a/packages/app-metadata-controller/src/AppMetadataController.ts b/packages/app-metadata-controller/src/AppMetadataController.ts index 4f7d2170cd0..e456467fc68 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.ts @@ -1,10 +1,10 @@ -import { BaseController } from '@metamask/base-controller'; import type { StateMetadata, ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; // Unique name for the controller const controllerName = 'AppMetadataController'; @@ -88,12 +88,10 @@ type AllowedEvents = never; * @returns A restricted messenger type that defines the allowed actions and events * for the AppMetadataController */ -export type AppMetadataControllerMessenger = RestrictedMessenger< +export type AppMetadataControllerMessenger = Messenger< typeof controllerName, AppMetadataControllerActions | AllowedActions, - AppMetadataControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AppMetadataControllerEvents | AllowedEvents >; /** diff --git a/yarn.lock b/yarn.lock index a431876c636..1ce17ff6ec7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2530,6 +2530,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.1.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" From 5c69e3d1587ed4d810b1454545c5cce9e5717cbc Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:43:20 +0200 Subject: [PATCH 017/247] refactor: migrate `ApprovalController` to `@metamask/messenger` --- packages/approval-controller/package.json | 1 + .../src/ApprovalController.test.ts | 77 ++++++++----------- .../src/ApprovalController.ts | 32 ++++---- yarn.lock | 1 + 4 files changed, 49 insertions(+), 62 deletions(-) diff --git a/packages/approval-controller/package.json b/packages/approval-controller/package.json index 2309e14830f..0d590c6fc92 100644 --- a/packages/approval-controller/package.json +++ b/packages/approval-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/utils": "^11.4.2", "nanoid": "^3.3.8" diff --git a/packages/approval-controller/src/ApprovalController.test.ts b/packages/approval-controller/src/ApprovalController.test.ts index 18cc824b451..c9ca5253d3f 100644 --- a/packages/approval-controller/src/ApprovalController.test.ts +++ b/packages/approval-controller/src/ApprovalController.test.ts @@ -1,6 +1,6 @@ /* eslint-disable jest/expect-expect */ -import { Messenger } from '@metamask/base-controller'; +import { Messenger } from '@metamask/messenger'; import { errorCodes, JsonRpcError } from '@metamask/rpc-errors'; import { nanoid } from 'nanoid'; @@ -223,20 +223,28 @@ function getError(message: string, code?: number) { } /** - * Constructs a restricted messenger. + * Constructs a controller messenger. * - * @returns A restricted messenger. + * @returns A controller messenger. */ -function getRestrictedMessenger() { - const messenger = new Messenger< +function getMessengers() { + const rootMessenger = new Messenger< + 'Root', ApprovalControllerActions, ApprovalControllerEvents - >(); - return messenger.getRestricted({ - name: 'ApprovalController', - allowedActions: [], - allowedEvents: [], - }); + >({ namespace: 'Root' }); + return { + rootMessenger, + approvalControllerMessenger: new Messenger< + 'ApprovalController', + ApprovalControllerActions, + ApprovalControllerEvents, + typeof rootMessenger + >({ + namespace: 'ApprovalController', + parent: rootMessenger, + }), + }; } describe('approval controller', () => { @@ -250,7 +258,7 @@ describe('approval controller', () => { showApprovalRequest = jest.fn(); approvalController = new ApprovalController({ - messenger: getRestrictedMessenger(), + messenger: getMessengers().approvalControllerMessenger, showApprovalRequest, }); }); @@ -445,7 +453,7 @@ describe('approval controller', () => { it('does not throw on origin and type collision if type excluded', () => { approvalController = new ApprovalController({ - messenger: getRestrictedMessenger(), + messenger: getMessengers().approvalControllerMessenger, showApprovalRequest, typesExcludedFromRateLimiting: ['myType'], }); @@ -638,7 +646,7 @@ describe('approval controller', () => { it('gets the count when specifying origin and type with type excluded from rate limiting', () => { approvalController = new ApprovalController({ - messenger: getRestrictedMessenger(), + messenger: getMessengers().approvalControllerMessenger, showApprovalRequest, typesExcludedFromRateLimiting: [TYPE], }); @@ -678,7 +686,7 @@ describe('approval controller', () => { it('gets the total approval count with type excluded from rate limiting', () => { approvalController = new ApprovalController({ - messenger: getRestrictedMessenger(), + messenger: getMessengers().approvalControllerMessenger, showApprovalRequest, typesExcludedFromRateLimiting: ['type0'], }); @@ -1269,23 +1277,16 @@ describe('approval controller', () => { describe('actions', () => { it('addApprovalRequest: shouldShowRequest = true', async () => { - const messenger = new Messenger< - ApprovalControllerActions, - ApprovalControllerEvents - >(); + const { rootMessenger, approvalControllerMessenger } = getMessengers(); approvalController = new ApprovalController({ - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], - }), + messenger: approvalControllerMessenger, showApprovalRequest, }); // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/no-floating-promises - messenger.call( + rootMessenger.call( 'ApprovalController:addRequest', { id: 'foo', origin: 'bar.baz', type: TYPE }, true, @@ -1295,23 +1296,16 @@ describe('approval controller', () => { }); it('addApprovalRequest: shouldShowRequest = false', async () => { - const messenger = new Messenger< - ApprovalControllerActions, - ApprovalControllerEvents - >(); + const { rootMessenger, approvalControllerMessenger } = getMessengers(); approvalController = new ApprovalController({ - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], - }), + messenger: approvalControllerMessenger, showApprovalRequest, }); // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/no-floating-promises - messenger.call( + rootMessenger.call( 'ApprovalController:addRequest', { id: 'foo', origin: 'bar.baz', type: TYPE }, false, @@ -1321,17 +1315,10 @@ describe('approval controller', () => { }); it('updateRequestState', () => { - const messenger = new Messenger< - ApprovalControllerActions, - ApprovalControllerEvents - >(); + const { rootMessenger, approvalControllerMessenger } = getMessengers(); approvalController = new ApprovalController({ - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], - }), + messenger: approvalControllerMessenger, showApprovalRequest, }); @@ -1344,7 +1331,7 @@ describe('approval controller', () => { requestState: { foo: 'bar' }, }); - messenger.call('ApprovalController:updateRequestState', { + rootMessenger.call('ApprovalController:updateRequestState', { id: 'foo', requestState: { foo: 'foobar' }, }); diff --git a/packages/approval-controller/src/ApprovalController.ts b/packages/approval-controller/src/ApprovalController.ts index 5b7398a83f8..cf675a35f19 100644 --- a/packages/approval-controller/src/ApprovalController.ts +++ b/packages/approval-controller/src/ApprovalController.ts @@ -2,8 +2,8 @@ import type { ControllerGetStateAction } from '@metamask/base-controller'; import { BaseController, type ControllerStateChangeEvent, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { JsonRpcError, DataWithOptionalCause } from '@metamask/rpc-errors'; import { rpcErrors } from '@metamask/rpc-errors'; import type { Json, OptionalField } from '@metamask/utils'; @@ -119,12 +119,10 @@ export type ApprovalControllerState = { approvalFlows: ApprovalFlowState[]; }; -export type ApprovalControllerMessenger = RestrictedMessenger< +export type ApprovalControllerMessenger = Messenger< typeof controllerName, ApprovalControllerActions, - ApprovalControllerEvents, - never, - never + ApprovalControllerEvents >; // Option Types @@ -398,12 +396,12 @@ export class ApprovalController extends BaseController< * actions. */ private registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:clearRequests` as const, this.clear.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:addRequest` as const, (opts: AddApprovalOptions, shouldShowRequest: boolean) => { if (shouldShowRequest) { @@ -413,47 +411,47 @@ export class ApprovalController extends BaseController< }, ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:hasRequest` as const, this.has.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:acceptRequest` as const, this.accept.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:rejectRequest` as const, this.reject.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateRequestState` as const, this.updateRequestState.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:startFlow` as const, this.startFlow.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:endFlow` as const, this.endFlow.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setFlowLoadingText` as const, this.setFlowLoadingText.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:showSuccess` as const, this.success.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:showError` as const, this.error.bind(this), ); diff --git a/yarn.lock b/yarn.lock index a431876c636..d2066afdb79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2547,6 +2547,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.4.2" "@types/jest": "npm:^27.4.1" From ff3989cca92b594190bc0abb27c7b7d22d94adb0 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:45:14 +0200 Subject: [PATCH 018/247] update changelog --- packages/app-metadata-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app-metadata-controller/CHANGELOG.md b/packages/app-metadata-controller/CHANGELOG.md index f3d87951e95..3b29921b764 100644 --- a/packages/app-metadata-controller/CHANGELOG.md +++ b/packages/app-metadata-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6385](https://github.com/MetaMask/core/pull/6385)) + - Previously, `AppMetadataController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.0` to `^8.2.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) ## [1.0.0] From 65c4c78a4182ea189fe274784529d601913544be Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 14:45:53 +0200 Subject: [PATCH 019/247] update changelog --- packages/approval-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/approval-controller/CHANGELOG.md b/packages/approval-controller/CHANGELOG.md index dd9151626b3..41b63f9ae2d 100644 --- a/packages/approval-controller/CHANGELOG.md +++ b/packages/approval-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `ApprovalController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.2.0` to `^11.4.2` ([#6054](https://github.com/MetaMask/core/pull/6054)) - Bump `@metamask/base-controller` from `^8.0.0` to `^8.2.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) From 0f6f36052b9b42656778b61d1df3a7d00900a54e Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 26 Aug 2025 15:07:49 +0200 Subject: [PATCH 020/247] add `AppMetadataControllerActions` and `AppMetadataControllerEvents` to test messenger --- .../src/AppMetadataController.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/app-metadata-controller/src/AppMetadataController.test.ts b/packages/app-metadata-controller/src/AppMetadataController.test.ts index 1a05b323966..0d7d8148943 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.test.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.test.ts @@ -4,6 +4,8 @@ import { AppMetadataController, getDefaultAppMetadataControllerState, type AppMetadataControllerOptions, + AppMetadataControllerActions, + AppMetadataControllerEvents, } from './AppMetadataController'; describe('AppMetadataController', () => { @@ -148,12 +150,16 @@ function withController( ): ReturnValue { const [options = {}, fn] = args.length === 2 ? args : [{}, args[0]]; - const rootMessenger = new Messenger<'Root'>({ namespace: 'Root' }); + const rootMessenger = new Messenger< + 'Root', + AppMetadataControllerActions, + AppMetadataControllerEvents + >({ namespace: 'Root' }); const appMetadataControllerMessenger = new Messenger< 'AppMetadataController', - never, - never, + AppMetadataControllerActions, + AppMetadataControllerEvents, typeof rootMessenger >({ namespace: 'AppMetadataController', From 78225500027816137bc42ab798e76e6603410b60 Mon Sep 17 00:00:00 2001 From: Michele Esposito <34438276+mikesposito@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:18:55 +0200 Subject: [PATCH 021/247] mark import as types --- .../app-metadata-controller/src/AppMetadataController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app-metadata-controller/src/AppMetadataController.test.ts b/packages/app-metadata-controller/src/AppMetadataController.test.ts index 0d7d8148943..e906eb7c9e3 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.test.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.test.ts @@ -4,8 +4,8 @@ import { AppMetadataController, getDefaultAppMetadataControllerState, type AppMetadataControllerOptions, - AppMetadataControllerActions, - AppMetadataControllerEvents, + type AppMetadataControllerActions, + type AppMetadataControllerEvents, } from './AppMetadataController'; describe('AppMetadataController', () => { From 0549ba06e5c06a1b2763d9db57e2c8f4a68edba4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 27 Aug 2025 14:53:47 +0200 Subject: [PATCH 022/247] refactor: migrate `TransactionController` to `@metamask/messenger` --- packages/transaction-controller/package.json | 1 + .../src/TransactionController.test.ts | 94 ++++++++----- .../src/TransactionController.ts | 127 ++++++++---------- .../src/utils/eip7702.test.ts | 38 ++++-- .../src/utils/feature-flags.test.ts | 40 ++++-- .../src/utils/swaps.test.ts | 38 ++++-- yarn.lock | 1 + 7 files changed, 205 insertions(+), 134 deletions(-) diff --git a/packages/transaction-controller/package.json b/packages/transaction-controller/package.json index e6029489768..865b31e7b93 100644 --- a/packages/transaction-controller/package.json +++ b/packages/transaction-controller/package.json @@ -57,6 +57,7 @@ "@metamask/base-controller": "^8.2.0", "@metamask/controller-utils": "^11.12.0", "@metamask/eth-query": "^4.0.0", + "@metamask/messenger": "^0.1.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/nonce-tracker": "^6.0.0", "@metamask/rpc-errors": "^7.0.2", diff --git a/packages/transaction-controller/src/TransactionController.test.ts b/packages/transaction-controller/src/TransactionController.test.ts index 1b81c49e974..5d62786bf75 100644 --- a/packages/transaction-controller/src/TransactionController.test.ts +++ b/packages/transaction-controller/src/TransactionController.test.ts @@ -4,7 +4,6 @@ import type { AddApprovalRequest, AddResult, } from '@metamask/approval-controller'; -import { Messenger } from '@metamask/base-controller'; import { ChainId, NetworkType, @@ -15,6 +14,7 @@ import { import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider'; import EthQuery from '@metamask/eth-query'; import HttpProvider from '@metamask/ethjs-provider-http'; +import { Messenger } from '@metamask/messenger'; import type { BlockTracker, NetworkClientConfiguration, @@ -56,6 +56,7 @@ import type { MethodData, TransactionControllerActions, TransactionControllerEvents, + TransactionControllerMessenger, TransactionControllerOptions, } from './TransactionController'; import { TransactionController } from './TransactionController'; @@ -111,7 +112,8 @@ import { buildMockGetNetworkClientById, } from '../../network-controller/tests/helpers'; -type UnrestrictedMessenger = Messenger< +type RootMessenger = Messenger< + 'Root', TransactionControllerActions | AllowedActions, TransactionControllerEvents | AllowedEvents >; @@ -328,10 +330,7 @@ function buildMockGasFeeFlow(): jest.Mocked { * @returns A promise that resolves with the transaction meta when the transaction is finished. */ function waitForTransactionFinished( - messenger: Messenger< - TransactionControllerActions | AllowedActions, - TransactionControllerEvents | AllowedEvents - >, + messenger: TransactionControllerMessenger | RootMessenger, { confirmed = false } = {}, ): Promise { const eventName = confirmed @@ -657,19 +656,33 @@ describe('TransactionController', () => { listener(networkState); }); }; - const unrestrictedMessenger: UnrestrictedMessenger = new Messenger(); + const rootMessenger: RootMessenger = new Messenger({ + namespace: 'Root', + }); + const networkControllerMessenger = new Messenger< + 'NetworkController', + AllowedActions, + AllowedEvents, + typeof rootMessenger + >({ namespace: 'NetworkController', parent: rootMessenger }); const getNetworkClientById = buildMockGetNetworkClientById( mockNetworkClientConfigurationsByNetworkClientId, ); - unrestrictedMessenger.registerActionHandler( + networkControllerMessenger.registerActionHandler( 'NetworkController:getNetworkClientById', getNetworkClientById, ); const { addTransactionApprovalRequest = { state: 'pending' } } = messengerOptions; + const approvalControllerMessenger = new Messenger< + 'ApprovalController', + AllowedActions, + AllowedEvents, + typeof rootMessenger + >({ namespace: 'ApprovalController', parent: rootMessenger }); const mockTransactionApprovalRequest = mockAddTransactionApprovalRequest( - unrestrictedMessenger, + approvalControllerMessenger, addTransactionApprovalRequest, ); @@ -694,28 +707,38 @@ describe('TransactionController', () => { ...givenOptions, }; - const restrictedMessenger = + const transactionControllerMessenger: TransactionControllerMessenger = givenRestrictedMessenger ?? - unrestrictedMessenger.getRestricted({ - name: 'TransactionController', - allowedActions: [ - 'AccountsController:getSelectedAccount', - 'AccountsController:getState', - 'ApprovalController:addRequest', - 'NetworkController:getNetworkClientById', - 'NetworkController:findNetworkClientIdByChainId', - 'RemoteFeatureFlagController:getState', - ], - allowedEvents: [], - }); + new Messenger({ + namespace: 'TransactionController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: transactionControllerMessenger, + actions: [ + 'AccountsController:getSelectedAccount', + 'AccountsController:getState', + 'ApprovalController:addRequest', + 'NetworkController:getNetworkClientById', + 'NetworkController:findNetworkClientIdByChainId', + 'RemoteFeatureFlagController:getState', + ], + }); + + const accountsControllerMessenger = new Messenger< + 'AccountsController', + AllowedActions, + AllowedEvents, + typeof rootMessenger + >({ namespace: 'AccountsController', parent: rootMessenger }); const mockGetSelectedAccount = jest.fn().mockReturnValue(selectedAccount); - unrestrictedMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', mockGetSelectedAccount, ); - unrestrictedMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getState', () => ({}) as never, ); @@ -724,14 +747,24 @@ describe('TransactionController', () => { featureFlags: {}, }); - unrestrictedMessenger.registerActionHandler( + const remoteFeatureFlagControllerMessenger = new Messenger< + 'RemoteFeatureFlagController', + AllowedActions, + AllowedEvents, + typeof rootMessenger + >({ + namespace: 'RemoteFeatureFlagController', + parent: rootMessenger, + }); + + remoteFeatureFlagControllerMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', remoteFeatureFlagControllerGetStateMock, ); const controller = new TransactionController({ ...otherOptions, - messenger: restrictedMessenger, + messenger: transactionControllerMessenger, } as TransactionControllerOptions); const state = givenOptions?.state; @@ -754,7 +787,8 @@ describe('TransactionController', () => { return { controller, - messenger: unrestrictedMessenger, + messenger: transactionControllerMessenger, + networkControllerMessenger, mockTransactionApprovalRequest, mockGetSelectedAccount, changeNetwork, @@ -783,7 +817,7 @@ describe('TransactionController', () => { * finally the mocked version of the action handler itself. */ function mockAddTransactionApprovalRequest( - messenger: UnrestrictedMessenger, + messenger: Messenger<'ApprovalController', AllowedActions, AllowedEvents>, options: | { state: 'approved'; @@ -6015,8 +6049,8 @@ describe('TransactionController', () => { }); it('uses the nonceTracker for the networkClientId matching the chainId', async () => { - const { controller, messenger } = setupController(); - messenger.registerActionHandler( + const { controller, networkControllerMessenger } = setupController(); + networkControllerMessenger.registerActionHandler( 'NetworkController:findNetworkClientIdByChainId', () => 'sepolia', ); diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index e8f0f025820..f2f937dd3b6 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -11,9 +11,8 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { query, ApprovalType, @@ -27,6 +26,7 @@ import type { GasFeeState, } from '@metamask/gas-fee-controller'; import type { KeyringControllerSignEip7702AuthorizationAction } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { BlockTracker, NetworkClientId, @@ -671,12 +671,10 @@ export type TransactionControllerEvents = /** * The messenger of the {@link TransactionController}. */ -export type TransactionControllerMessenger = RestrictedMessenger< +export type TransactionControllerMessenger = Messenger< typeof controllerName, TransactionControllerActions | AllowedActions, - TransactionControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TransactionControllerEvents | AllowedEvents >; /** @@ -868,7 +866,7 @@ export class TransactionController extends BaseController< }, }); - this.messagingSystem = messenger; + this.messenger = messenger; this.#afterAdd = hooks?.afterAdd ?? (() => Promise.resolve({})); this.#afterSign = hooks?.afterSign ?? (() => true); @@ -916,7 +914,7 @@ export class TransactionController extends BaseController< this.#transactionHistoryLimit = transactionHistoryLimit; const findNetworkClientIdByChainId = (chainId: Hex) => { - return this.messagingSystem.call( + return this.messenger.call( `NetworkController:findNetworkClientIdByChainId`, chainId, ); @@ -925,7 +923,7 @@ export class TransactionController extends BaseController< this.#multichainTrackingHelper = new MultichainTrackingHelper({ findNetworkClientIdByChainId, getNetworkClientById: ((networkClientId: NetworkClientId) => { - return this.messagingSystem.call( + return this.messenger.call( `NetworkController:getNetworkClientById`, networkClientId, ); @@ -937,10 +935,7 @@ export class TransactionController extends BaseController< createPendingTransactionTracker: this.#createPendingTransactionTracker.bind(this), onNetworkStateChange: (listener) => { - this.messagingSystem.subscribe( - 'NetworkController:stateChange', - listener, - ); + this.messenger.subscribe('NetworkController:stateChange', listener); }, }); @@ -956,12 +951,9 @@ export class TransactionController extends BaseController< getTransactions: () => this.state.transactions, getTransactionBatches: () => this.state.transactionBatches, layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, onStateChange: (listener) => { - this.messagingSystem.subscribe( - 'TransactionController:stateChange', - listener, - ); + this.messenger.subscribe('TransactionController:stateChange', listener); }, }); @@ -996,7 +988,7 @@ export class TransactionController extends BaseController< includeTokenTransfers: this.#incomingTransactionOptions.includeTokenTransfers, isEnabled: this.#incomingTransactionOptions.isEnabled, - messenger: this.messagingSystem, + messenger: this.messenger, remoteTransactionSource: new AccountsApiRemoteTransactionSource(), trimTransactions: this.#trimTransactionsForState.bind(this), updateTransactions: this.#incomingTransactionOptions.updateTransactions, @@ -1008,7 +1000,7 @@ export class TransactionController extends BaseController< // when transactionsController state changes // check for pending transactions and start polling if there are any - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:stateChange', this.#checkForPendingTransactionAndStartPolling, ); @@ -1016,7 +1008,7 @@ export class TransactionController extends BaseController< new ResimulateHelper({ simulateTransaction: this.#updateSimulationData.bind(this), onTransactionsUpdate: (listener) => { - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:stateChange', listener, (controllerState) => controllerState.transactions, @@ -1060,7 +1052,7 @@ export class TransactionController extends BaseController< async addTransactionBatch( request: TransactionBatchRequest, ): Promise { - const { blockTracker } = this.messagingSystem.call( + const { blockTracker } = this.messenger.call( `NetworkController:getNetworkClientById`, request.networkClientId, ); @@ -1082,7 +1074,7 @@ export class TransactionController extends BaseController< getTransaction: (transactionId) => this.#getTransactionOrThrow(transactionId), isSimulationEnabled: this.#isSimulationEnabled, - messenger: this.messagingSystem, + messenger: this.messenger, publishBatchHook: this.#publishBatchHook, publicKeyEIP7702: this.#publicKeyEIP7702, publishTransaction: ( @@ -1107,7 +1099,7 @@ export class TransactionController extends BaseController< return isAtomicBatchSupported({ ...request, getEthQuery: (chainId) => this.#getEthQuery({ chainId }), - messenger: this.messagingSystem, + messenger: this.messenger, publicKeyEIP7702: this.#publicKeyEIP7702, }); } @@ -1331,7 +1323,7 @@ export class TransactionController extends BaseController< { isSwapsDisabled: this.#isSwapsDisabled, cancelTransaction: this.#rejectTransaction.bind(this), - messenger: this.messagingSystem, + messenger: this.messenger, }, ); @@ -1356,7 +1348,7 @@ export class TransactionController extends BaseController< ); } - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:unapprovedTransactionAdded`, addedTransactionMeta, ); @@ -1424,7 +1416,7 @@ export class TransactionController extends BaseController< txParams.value = '0x0'; }, afterSubmit: (newTransactionMeta) => { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, newTransactionMeta, ); @@ -1463,7 +1455,7 @@ export class TransactionController extends BaseController< transactionId, transactionType: TransactionType.retry, afterSubmit: (newTransactionMeta) => { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:speedupTransactionAdded`, newTransactionMeta, ); @@ -1579,12 +1571,12 @@ export class TransactionController extends BaseController< this.#addMetadata(newTransactionMeta); // speedUpTransaction has no approval request, so we assume the user has already approved the transaction - this.messagingSystem.publish(`${controllerName}:transactionApproved`, { + this.messenger.publish(`${controllerName}:transactionApproved`, { transactionMeta: newTransactionMeta, actionId, }); - this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { + this.messenger.publish(`${controllerName}:transactionSubmitted`, { transactionMeta: newTransactionMeta, actionId, }); @@ -1620,7 +1612,7 @@ export class TransactionController extends BaseController< ignoreDelegationSignatures, isSimulationEnabled: this.#isSimulationEnabled(), getSimulationConfig: this.#getSimulationConfig, - messenger: this.messagingSystem, + messenger: this.messenger, txParams: transaction, }); @@ -1649,7 +1641,7 @@ export class TransactionController extends BaseController< ethQuery, isSimulationEnabled: this.#isSimulationEnabled(), getSimulationConfig: this.#getSimulationConfig, - messenger: this.messagingSystem, + messenger: this.messenger, txParams: transaction, }); @@ -1796,7 +1788,7 @@ export class TransactionController extends BaseController< throw error; }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionConfirmed`, updatedTransactionMeta, ); @@ -2140,7 +2132,7 @@ export class TransactionController extends BaseController< await updateTransactionLayer1GasFee({ layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, provider, transactionMeta: updatedTransaction, }); @@ -2334,7 +2326,7 @@ export class TransactionController extends BaseController< status as TransactionStatus, ) ) { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, updatedTransactionMeta, ); @@ -2468,7 +2460,7 @@ export class TransactionController extends BaseController< const gasFeeFlow = getGasFeeFlow( transactionMeta, this.#gasFeeFlows, - this.messagingSystem, + this.messenger, ) as GasFeeFlow; const ethQuery = new EthQuery(provider); @@ -2480,7 +2472,7 @@ export class TransactionController extends BaseController< return gasFeeFlow.getGasFees({ ethQuery, gasFeeControllerData, - messenger: this.messagingSystem, + messenger: this.messenger, transactionMeta, }); } @@ -2510,7 +2502,7 @@ export class TransactionController extends BaseController< return await getTransactionLayer1GasFee({ layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, provider, transactionMeta: { txParams: transactionParams, @@ -2786,7 +2778,7 @@ export class TransactionController extends BaseController< gasFeeFlows: this.#gasFeeFlows, getGasFeeEstimates: this.#getGasFeeEstimates, getSavedGasFees: this.#getSavedGasFees.bind(this), - messenger: this.messagingSystem, + messenger: this.messenger, txMeta: transactionMeta, }), ); @@ -2796,7 +2788,7 @@ export class TransactionController extends BaseController< async () => await updateTransactionLayer1GasFee({ layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, provider, transactionMeta, }), @@ -2903,13 +2895,10 @@ export class TransactionController extends BaseController< const updatedTransactionMeta = this.#getTransaction( transactionId, ) as TransactionMeta; - this.messagingSystem.publish( - `${controllerName}:transactionApproved`, - { - transactionMeta: updatedTransactionMeta, - actionId, - }, - ); + this.messenger.publish(`${controllerName}:transactionApproved`, { + transactionMeta: updatedTransactionMeta, + actionId, + }); } } catch (rawError: unknown) { const error = rawError as Error & { code?: number; data?: Json }; @@ -3041,7 +3030,7 @@ export class TransactionController extends BaseController< if (!(await this.#beforePublish(transactionMeta))) { log('Skipping publishing transaction based on hook'); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionPublishingSkipped`, transactionMeta, ); @@ -3121,11 +3110,11 @@ export class TransactionController extends BaseController< }, ); - this.messagingSystem.publish(`${controllerName}:transactionSubmitted`, { + this.messenger.publish(`${controllerName}:transactionSubmitted`, { transactionMeta, }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, transactionMeta, ); @@ -3183,7 +3172,7 @@ export class TransactionController extends BaseController< error: normalizeTxError(error ?? providerErrors.userRejectedRequest()), }; - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, updatedTransactionMeta, ); @@ -3193,7 +3182,7 @@ export class TransactionController extends BaseController< updatedTransactionMeta, ); - this.messagingSystem.publish(`${controllerName}:transactionRejected`, { + this.messenger.publish(`${controllerName}:transactionRejected`, { transactionMeta: updatedTransactionMeta, actionId, }); @@ -3295,7 +3284,7 @@ export class TransactionController extends BaseController< parentContext: traceContext, }); - return (await this.messagingSystem.call( + return (await this.messenger.call( 'ApprovalController:addRequest', { id, @@ -3431,7 +3420,7 @@ export class TransactionController extends BaseController< ); }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:incomingTransactionsReceived`, finalTransactions, ); @@ -3580,7 +3569,7 @@ export class TransactionController extends BaseController< ...transactionMeta, status: TransactionStatus.dropped as const, }; - this.messagingSystem.publish(`${controllerName}:transactionDropped`, { + this.messenger.publish(`${controllerName}:transactionDropped`, { transactionMeta: updatedTransactionMeta, }); this.updateTransaction( @@ -3670,7 +3659,7 @@ export class TransactionController extends BaseController< const signedAuthorizationList = await signAuthorizationList({ authorizationList, - messenger: this.messagingSystem, + messenger: this.messenger, transactionMeta, }); @@ -3762,7 +3751,7 @@ export class TransactionController extends BaseController< } #onTransactionStatusChange(transactionMeta: TransactionMeta) { - this.messagingSystem.publish(`${controllerName}:transactionStatusUpdated`, { + this.messenger.publish(`${controllerName}:transactionStatusUpdated`, { transactionMeta, }); } @@ -3785,7 +3774,7 @@ export class TransactionController extends BaseController< this.#markNonceDuplicatesDropped(transactionMeta.id); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionConfirmed`, transactionMeta, ); @@ -3816,7 +3805,7 @@ export class TransactionController extends BaseController< updateTransaction: this.updateTransaction.bind(this), }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:postTransactionBalanceUpdated`, { transactionMeta: updatedTransactionMeta, @@ -3880,7 +3869,7 @@ export class TransactionController extends BaseController< this.#multichainTrackingHelper.acquireNonceLockForChainIdKey({ chainId, }), - messenger: this.messagingSystem, + messenger: this.messenger, publishTransaction: (_ethQuery, transactionMeta) => this.#publishTransaction(_ethQuery, transactionMeta, { skipSubmitHistory: true, @@ -4230,7 +4219,7 @@ export class TransactionController extends BaseController< chainId, getSimulationConfig: this.#getSimulationConfig, isEIP7702GasFeeTokensEnabled: this.#isEIP7702GasFeeTokensEnabled, - messenger: this.messagingSystem, + messenger: this.messenger, publicKeyEIP7702: this.#publicKeyEIP7702, transactionMeta, }); @@ -4328,11 +4317,11 @@ export class TransactionController extends BaseController< } #getSelectedAccount() { - return this.messagingSystem.call('AccountsController:getSelectedAccount'); + return this.messenger.call('AccountsController:getSelectedAccount'); } #getInternalAccounts(): Hex[] { - const state = this.messagingSystem.call('AccountsController:getState'); + const state = this.messenger.call('AccountsController:getState'); return Object.values(state.internalAccounts?.accounts ?? {}) .filter((account) => account.type === 'eip155:eoa') @@ -4392,18 +4381,18 @@ export class TransactionController extends BaseController< isCustomNetwork, isSimulationEnabled: this.#isSimulationEnabled(), getSimulationConfig: this.#getSimulationConfig, - messenger: this.messagingSystem, + messenger: this.messenger, txMeta: transactionMeta, }); } #registerActionHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:estimateGas`, this.estimateGas.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateCustodialTransaction`, this.updateCustodialTransaction.bind(this), ); @@ -4477,7 +4466,7 @@ export class TransactionController extends BaseController< }; } - this.messagingSystem.publish(`${controllerName}:transactionFailed`, { + this.messenger.publish(`${controllerName}:transactionFailed`, { actionId, error: error.message, transactionMeta: newTransactionMeta, @@ -4485,7 +4474,7 @@ export class TransactionController extends BaseController< this.#onTransactionStatusChange(newTransactionMeta); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, newTransactionMeta, ); diff --git a/packages/transaction-controller/src/utils/eip7702.test.ts b/packages/transaction-controller/src/utils/eip7702.test.ts index 1b70fa09db1..af58b1ea7ad 100644 --- a/packages/transaction-controller/src/utils/eip7702.test.ts +++ b/packages/transaction-controller/src/utils/eip7702.test.ts @@ -1,5 +1,10 @@ import { query } from '@metamask/controller-utils'; import type EthQuery from '@metamask/eth-query'; +import { + Messenger, + MessengerActions, + MessengerEvents, +} from '@metamask/messenger'; import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; import type { Hex } from '@metamask/utils'; import { remove0x } from '@metamask/utils'; @@ -16,7 +21,6 @@ import { getEIP7702ContractAddresses, getEIP7702SupportedChains, } from './feature-flags'; -import { Messenger } from '../../../base-controller/src'; import type { KeyringControllerSignEip7702AuthorizationAction } from '../../../keyring-controller/src'; import type { TransactionControllerMessenger } from '../TransactionController'; import type { AuthorizationList } from '../types'; @@ -73,10 +77,10 @@ const AUTHORIZATION_LIST_MOCK: AuthorizationList = [ ]; describe('EIP-7702 Utils', () => { - let baseMessenger: Messenger< - | KeyringControllerSignEip7702AuthorizationAction - | RemoteFeatureFlagControllerGetStateAction, - never + let rootMessenger: Messenger< + 'Root', + MessengerActions, + MessengerEvents >; const getCodeMock = jest.mocked(query); @@ -95,21 +99,33 @@ describe('EIP-7702 Utils', () => { beforeEach(() => { jest.resetAllMocks(); - baseMessenger = new Messenger(); + rootMessenger = new Messenger({ namespace: 'Root' }); signAuthorizationMock = jest .fn() .mockResolvedValue(AUTHORIZATION_SIGNATURE_MOCK); - baseMessenger.registerActionHandler( + const keyringControllerMessenger = new Messenger< + 'KeyringController', + KeyringControllerSignEip7702AuthorizationAction, + never, + typeof rootMessenger + >({ + namespace: 'KeyringController', + parent: rootMessenger, + }); + keyringControllerMessenger.registerActionHandler( 'KeyringController:signEip7702Authorization', signAuthorizationMock, ); - controllerMessenger = baseMessenger.getRestricted({ - name: 'TransactionController', - allowedActions: ['KeyringController:signEip7702Authorization'], - allowedEvents: [], + controllerMessenger = new Messenger({ + namespace: 'TransactionController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: controllerMessenger, + actions: ['KeyringController:signEip7702Authorization'], }); }); diff --git a/packages/transaction-controller/src/utils/feature-flags.test.ts b/packages/transaction-controller/src/utils/feature-flags.test.ts index 84bc2e1922a..652d3ec168e 100644 --- a/packages/transaction-controller/src/utils/feature-flags.test.ts +++ b/packages/transaction-controller/src/utils/feature-flags.test.ts @@ -1,4 +1,8 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; import type { Hex } from '@metamask/utils'; @@ -36,13 +40,21 @@ const GAS_BUFFER_4_MOCK = 1.4; const GAS_BUFFER_5_MOCK = 1.5; describe('Feature Flags Utils', () => { - let baseMessenger: Messenger< - RemoteFeatureFlagControllerGetStateAction, - never + let rootMessenger: Messenger< + 'Root', + MessengerActions, + MessengerEvents >; let controllerMessenger: TransactionControllerMessenger; + let remoteFeatureFlagControllerMessenger: Messenger< + 'RemoteFeatureFlagController', + RemoteFeatureFlagControllerGetStateAction, + never, + typeof rootMessenger + >; + let getFeatureFlagsMock: jest.MockedFn< RemoteFeatureFlagControllerGetStateAction['handler'] >; @@ -66,17 +78,25 @@ describe('Feature Flags Utils', () => { getFeatureFlagsMock = jest.fn(); - baseMessenger = new Messenger(); + rootMessenger = new Messenger({ namespace: 'Root' }); + + remoteFeatureFlagControllerMessenger = new Messenger({ + namespace: 'RemoteFeatureFlagController', + parent: rootMessenger, + }); - baseMessenger.registerActionHandler( + remoteFeatureFlagControllerMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', getFeatureFlagsMock, ); - controllerMessenger = baseMessenger.getRestricted({ - name: 'TransactionController', - allowedActions: ['RemoteFeatureFlagController:getState'], - allowedEvents: [], + controllerMessenger = new Messenger({ + namespace: 'TransactionController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: controllerMessenger, + actions: ['RemoteFeatureFlagController:getState'], }); isValidSignatureMock.mockReturnValue(true); diff --git a/packages/transaction-controller/src/utils/swaps.test.ts b/packages/transaction-controller/src/utils/swaps.test.ts index 457aa0134c9..9f74710da54 100644 --- a/packages/transaction-controller/src/utils/swaps.test.ts +++ b/packages/transaction-controller/src/utils/swaps.test.ts @@ -1,5 +1,9 @@ -import { Messenger } from '@metamask/base-controller'; import { query } from '@metamask/controller-utils'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { updateSwapsTransaction, @@ -9,13 +13,7 @@ import { } from './swaps'; import { flushPromises } from '../../../../tests/helpers'; import { CHAIN_IDS } from '../constants'; -import type { - AllowedActions, - AllowedEvents, - TransactionControllerActions, - TransactionControllerEvents, - TransactionControllerMessenger, -} from '../TransactionController'; +import type { TransactionControllerMessenger } from '../TransactionController'; import type { TransactionMeta } from '../types'; import { TransactionType, TransactionStatus } from '../types'; @@ -47,17 +45,29 @@ describe('updateSwapsTransaction', () => { destinationTokenSymbol: 'DAI', }, }; + const rootMessenger = new Messenger< + 'Root', + MessengerActions, + MessengerEvents + >({ + namespace: 'Root', + }); messenger = new Messenger< - TransactionControllerActions | AllowedActions, - TransactionControllerEvents | AllowedEvents - >().getRestricted({ - name: 'TransactionController', - allowedActions: [ + 'TransactionController', + MessengerActions, + MessengerEvents, + typeof rootMessenger + >({ + namespace: 'TransactionController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: [ 'ApprovalController:addRequest', 'NetworkController:getNetworkClientById', 'NetworkController:findNetworkClientIdByChainId', ], - allowedEvents: [], }); request = { isSwapsDisabled: false, diff --git a/yarn.lock b/yarn.lock index d2066afdb79..b2e49bbd25f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4687,6 +4687,7 @@ __metadata: "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-provider-http": "npm:^0.3.0" "@metamask/gas-fee-controller": "npm:^24.0.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^24.1.0" "@metamask/nonce-tracker": "npm:^6.0.0" From 4941db574ca265b318385899e5dc11de3c3ae919 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 28 Aug 2025 11:52:16 +0200 Subject: [PATCH 023/247] refactor: migrate `NetworkController` to `@metamask/messenger` --- packages/network-controller/package.json | 1 + .../src/NetworkController.ts | 75 ++++++++----------- ...create-auto-managed-network-client.test.ts | 48 ++++-------- .../tests/NetworkController.test.ts | 53 +++++++------ packages/network-controller/tests/helpers.ts | 73 ++++++++++++++---- yarn.lock | 1 + 6 files changed, 134 insertions(+), 117 deletions(-) diff --git a/packages/network-controller/package.json b/packages/network-controller/package.json index 9a4abda1c09..c2d2898102d 100644 --- a/packages/network-controller/package.json +++ b/packages/network-controller/package.json @@ -55,6 +55,7 @@ "@metamask/eth-json-rpc-provider": "^4.1.8", "@metamask/eth-query": "^4.0.0", "@metamask/json-rpc-engine": "^10.0.3", + "@metamask/messenger": "^0.1.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/swappable-obj-proxy": "^2.3.0", "@metamask/utils": "^11.4.2", diff --git a/packages/network-controller/src/NetworkController.ts b/packages/network-controller/src/NetworkController.ts index ceaef81f810..c4f2fee4f01 100644 --- a/packages/network-controller/src/NetworkController.ts +++ b/packages/network-controller/src/NetworkController.ts @@ -1,9 +1,8 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import type { Partialize } from '@metamask/controller-utils'; import { InfuraNetworkType, @@ -20,6 +19,7 @@ import { import type { ErrorReportingServiceCaptureExceptionAction } from '@metamask/error-reporting-service'; import type { PollingBlockTrackerOptions } from '@metamask/eth-block-tracker'; import EthQuery from '@metamask/eth-query'; +import type { Messenger } from '@metamask/messenger'; import { errorCodes } from '@metamask/rpc-errors'; import { createEventEmitterProxy } from '@metamask/swappable-obj-proxy'; import type { SwappableProxy } from '@metamask/swappable-obj-proxy'; @@ -603,12 +603,10 @@ export type NetworkControllerActions = */ type AllowedActions = ErrorReportingServiceCaptureExceptionAction; -export type NetworkControllerMessenger = RestrictedMessenger< +export type NetworkControllerMessenger = Messenger< typeof controllerName, NetworkControllerActions | AllowedActions, - NetworkControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + NetworkControllerEvents | AllowedEvents >; /** @@ -1229,7 +1227,7 @@ export class NetworkController extends BaseController< this.state.networkConfigurationsByChainId, ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:getEthQuery`, @@ -1238,80 +1236,80 @@ export class NetworkController extends BaseController< }, ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:getNetworkClientById`, this.getNetworkClientById.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:getEIP1559Compatibility`, this.getEIP1559Compatibility.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:setActiveNetwork`, this.setActiveNetwork.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:setProviderType`, this.setProviderType.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:findNetworkClientIdByChainId`, this.findNetworkClientIdByChainId.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:getNetworkConfigurationByChainId`, this.getNetworkConfigurationByChainId.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // ESLint is mistaken here; `name` is a string. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:getNetworkConfigurationByNetworkClientId`, this.getNetworkConfigurationByNetworkClientId.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${this.name}:getSelectedNetworkClient`, this.getSelectedNetworkClient.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${this.name}:getSelectedChainId`, this.getSelectedChainId.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // ESLint is mistaken here; `name` is a string. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:addNetwork`, this.addNetwork.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // ESLint is mistaken here; `name` is a string. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:removeNetwork`, this.removeNetwork.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // ESLint is mistaken here; `name` is a string. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:updateNetwork`, @@ -1535,15 +1533,9 @@ export class NetworkController extends BaseController< updateState?: (state: Draft) => void; } = {}, ) { - this.messagingSystem.publish( - 'NetworkController:networkWillChange', - this.state, - ); + this.messenger.publish('NetworkController:networkWillChange', this.state); this.#applyNetworkSelection(networkClientId, options); - this.messagingSystem.publish( - 'NetworkController:networkDidChange', - this.state, - ); + this.messenger.publish('NetworkController:networkDidChange', this.state); await this.lookupNetwork(); } @@ -1681,7 +1673,7 @@ export class NetworkController extends BaseController< const listener = () => { networkChanged = true; try { - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'NetworkController:networkDidChange', listener, ); @@ -1705,10 +1697,7 @@ export class NetworkController extends BaseController< } } }; - this.messagingSystem.subscribe( - 'NetworkController:networkDidChange', - listener, - ); + this.messenger.subscribe('NetworkController:networkDidChange', listener); let updatedNetworkStatus: NetworkStatus; let updatedIsEIP1559Compatible: boolean | undefined; @@ -1772,7 +1761,7 @@ export class NetworkController extends BaseController< } try { - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'NetworkController:networkDidChange', listener, ); @@ -1798,15 +1787,15 @@ export class NetworkController extends BaseController< if (isInfura) { if (updatedNetworkStatus === NetworkStatus.Available) { - this.messagingSystem.publish('NetworkController:infuraIsUnblocked'); + this.messenger.publish('NetworkController:infuraIsUnblocked'); } else if (updatedNetworkStatus === NetworkStatus.Blocked) { - this.messagingSystem.publish('NetworkController:infuraIsBlocked'); + this.messenger.publish('NetworkController:infuraIsBlocked'); } } else { // Always publish infuraIsUnblocked regardless of network status to // prevent consumers from being stuck in a blocked state if they were // previously connected to an Infura network that was blocked - this.messagingSystem.publish('NetworkController:infuraIsUnblocked'); + this.messenger.publish('NetworkController:infuraIsUnblocked'); } } @@ -2047,7 +2036,7 @@ export class NetworkController extends BaseController< }); }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:networkAdded`, newNetworkConfiguration, ); @@ -2384,7 +2373,7 @@ export class NetworkController extends BaseController< }); }); - this.messagingSystem.publish( + this.messenger.publish( 'NetworkController:networkRemoved', existingNetworkConfiguration, ); @@ -2788,7 +2777,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, }); } else { @@ -2804,7 +2793,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, }); } @@ -2966,7 +2955,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, }), ] as const; @@ -2983,7 +2972,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, }), ] as const; diff --git a/packages/network-controller/src/create-auto-managed-network-client.test.ts b/packages/network-controller/src/create-auto-managed-network-client.test.ts index 662b0f1a7df..bf555303429 100644 --- a/packages/network-controller/src/create-auto-managed-network-client.test.ts +++ b/packages/network-controller/src/create-auto-managed-network-client.test.ts @@ -1,18 +1,14 @@ -import { Messenger } from '@metamask/base-controller'; import { BUILT_IN_NETWORKS, NetworkType } from '@metamask/controller-utils'; import { createAutoManagedNetworkClient } from './create-auto-managed-network-client'; import * as createNetworkClientModule from './create-network-client'; -import type { - NetworkControllerActions, - NetworkControllerEvents, -} from './NetworkController'; import type { CustomNetworkClientConfiguration, InfuraNetworkClientConfiguration, } from './types'; import { NetworkClientType } from './types'; import { mockNetwork } from '../../../tests/mock-network'; +import { buildNetworkControllerMessenger } from '../tests/helpers'; describe('createAutoManagedNetworkClient', () => { const networkClientConfigurations: [ @@ -44,7 +40,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); @@ -60,7 +56,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); }).not.toThrow(); @@ -73,7 +69,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); @@ -121,7 +117,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); @@ -161,7 +157,7 @@ describe('createAutoManagedNetworkClient', () => { const getBlockTrackerOptions = () => ({ pollingInterval: 5000, }); - const messenger = getNetworkControllerMessenger(); + const messenger = buildNetworkControllerMessenger(); const { provider } = createAutoManagedNetworkClient({ networkClientConfiguration, @@ -220,7 +216,7 @@ describe('createAutoManagedNetworkClient', () => { const getBlockTrackerOptions = () => ({ pollingInterval: 5000, }); - const messenger = getNetworkControllerMessenger(); + const messenger = buildNetworkControllerMessenger(); const autoManagedNetworkClient = createAutoManagedNetworkClient({ networkClientConfiguration, @@ -288,7 +284,7 @@ describe('createAutoManagedNetworkClient', () => { const getBlockTrackerOptions = () => ({ pollingInterval: 5000, }); - const messenger = getNetworkControllerMessenger(); + const messenger = buildNetworkControllerMessenger(); const autoManagedNetworkClient = createAutoManagedNetworkClient({ networkClientConfiguration, @@ -337,7 +333,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); @@ -396,7 +392,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); @@ -457,7 +453,7 @@ describe('createAutoManagedNetworkClient', () => { const getBlockTrackerOptions = () => ({ pollingInterval: 5000, }); - const messenger = getNetworkControllerMessenger(); + const messenger = buildNetworkControllerMessenger(); const { blockTracker } = createAutoManagedNetworkClient({ networkClientConfiguration, @@ -512,7 +508,7 @@ describe('createAutoManagedNetworkClient', () => { const getBlockTrackerOptions = () => ({ pollingInterval: 5000, }); - const messenger = getNetworkControllerMessenger(); + const messenger = buildNetworkControllerMessenger(); const autoManagedNetworkClient = createAutoManagedNetworkClient({ networkClientConfiguration, @@ -574,7 +570,7 @@ describe('createAutoManagedNetworkClient', () => { const getBlockTrackerOptions = () => ({ pollingInterval: 5000, }); - const messenger = getNetworkControllerMessenger(); + const messenger = buildNetworkControllerMessenger(); const autoManagedNetworkClient = createAutoManagedNetworkClient({ networkClientConfiguration, @@ -632,7 +628,7 @@ describe('createAutoManagedNetworkClient', () => { fetch, btoa, }), - messenger: getNetworkControllerMessenger(), + messenger: buildNetworkControllerMessenger(), isRpcFailoverEnabled: false, }); // Start the block tracker @@ -646,19 +642,3 @@ describe('createAutoManagedNetworkClient', () => { }); } }); - -/** - * Constructs a NetworkController messenger. - * - * @returns The NetworkController messenger. - */ -function getNetworkControllerMessenger() { - return new Messenger< - NetworkControllerActions, - NetworkControllerEvents - >().getRestricted({ - name: 'NetworkController', - allowedActions: [], - allowedEvents: [], - }); -} diff --git a/packages/network-controller/tests/NetworkController.test.ts b/packages/network-controller/tests/NetworkController.test.ts index 784b6ab79cd..7981931fcb4 100644 --- a/packages/network-controller/tests/NetworkController.test.ts +++ b/packages/network-controller/tests/NetworkController.test.ts @@ -26,6 +26,7 @@ import { buildCustomNetworkClientConfiguration, buildCustomNetworkConfiguration, buildCustomRpcEndpoint, + buildErrorReportingServiceMessenger, buildInfuraNetworkClientConfiguration, buildInfuraNetworkConfiguration, buildInfuraRpcEndpoint, @@ -165,11 +166,11 @@ describe('NetworkController', () => { describe('constructor', () => { it('throws given an empty networkConfigurationsByChainId collection', () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { networkConfigurationsByChainId: {}, }, @@ -186,11 +187,11 @@ describe('NetworkController', () => { it('throws if the key under which a network configuration is filed does not match the chain ID of that network configuration', () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -212,11 +213,11 @@ describe('NetworkController', () => { it('throws if a network configuration has a defaultBlockExplorerUrlIndex that does not refer to an entry in blockExplorerUrls', () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -245,11 +246,11 @@ describe('NetworkController', () => { it('throws if a network configuration has a non-empty blockExplorerUrls but an absent defaultBlockExplorerUrlIndex', () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -277,11 +278,11 @@ describe('NetworkController', () => { it('throws if a network configuration has an invalid defaultRpcEndpointIndex', () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -309,11 +310,11 @@ describe('NetworkController', () => { it('throws if more than one RPC endpoint across network configurations has the same networkClientId', () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { networkConfigurationsByChainId: { '0x1337': buildCustomNetworkConfiguration({ @@ -352,13 +353,15 @@ describe('NetworkController', () => { describe('if selectedNetworkClientId does not match the networkClientId of an RPC endpoint in networkConfigurationsByChainId', () => { it('corrects selectedNetworkClientId to the default RPC endpoint of the first chain', () => { const messenger = buildRootMessenger(); - messenger.registerActionHandler( + const errorReportingServiceMessenger = + buildErrorReportingServiceMessenger(messenger); + errorReportingServiceMessenger.registerActionHandler( 'ErrorReportingService:captureException', jest.fn(), ); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); const controller = new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { selectedNetworkClientId: 'nonexistent', networkConfigurationsByChainId: { @@ -392,15 +395,17 @@ describe('NetworkController', () => { it('logs a Sentry error', () => { const messenger = buildRootMessenger(); + const errorReportingServiceMessenger = + buildErrorReportingServiceMessenger(messenger); const captureExceptionMock = jest.fn(); - messenger.registerActionHandler( + errorReportingServiceMessenger.registerActionHandler( 'ErrorReportingService:captureException', captureExceptionMock, ); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { selectedNetworkClientId: 'nonexistent', networkConfigurationsByChainId: { @@ -441,11 +446,11 @@ describe('NetworkController', () => { invalidProjectId, )}"`, () => { const messenger = buildRootMessenger(); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); expect( () => new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: {}, // @ts-expect-error We are intentionally passing bad input. infuraProjectId: invalidProjectId, @@ -2127,7 +2132,7 @@ describe('NetworkController', () => { }, infuraProjectId, }, - async ({ controller, messenger }) => { + async ({ controller, networkControllerMessenger }) => { const fakeProvider = buildFakeProvider([ // Called during provider initialization { @@ -2151,7 +2156,7 @@ describe('NetworkController', () => { const lookupNetworkPromise = controller.lookupNetwork(); const error = new Error('oops'); jest - .spyOn(messenger, 'unsubscribe') + .spyOn(networkControllerMessenger, 'unsubscribe') .mockImplementation((eventType) => { // This is okay. // eslint-disable-next-line jest/no-conditional-in-test @@ -2539,7 +2544,7 @@ describe('NetworkController', () => { }, infuraProjectId, }, - async ({ controller, messenger }) => { + async ({ controller, networkControllerMessenger }) => { const fakeProvider = buildFakeProvider([ // Called during provider initialization { @@ -2563,7 +2568,7 @@ describe('NetworkController', () => { const lookupNetworkPromise = controller.lookupNetwork(); const error = new Error('oops'); jest - .spyOn(messenger, 'unsubscribe') + .spyOn(networkControllerMessenger, 'unsubscribe') .mockImplementation((eventType) => { // This is okay. // eslint-disable-next-line jest/no-conditional-in-test diff --git a/packages/network-controller/tests/helpers.ts b/packages/network-controller/tests/helpers.ts index 77d6100549b..1a509bd1fb8 100644 --- a/packages/network-controller/tests/helpers.ts +++ b/packages/network-controller/tests/helpers.ts @@ -1,4 +1,3 @@ -import { Messenger } from '@metamask/base-controller'; import { ChainId, InfuraNetworkType, @@ -6,6 +5,11 @@ import { NetworksTicker, toHex, } from '@metamask/controller-utils'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import { v4 as uuidV4 } from 'uuid'; @@ -13,10 +17,6 @@ import { FakeBlockTracker } from '../../../tests/fake-block-tracker'; import { FakeProvider } from '../../../tests/fake-provider'; import type { FakeProviderStub } from '../../../tests/fake-provider'; import { buildTestObject } from '../../../tests/helpers'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { type BuiltInNetworkClientId, type CustomNetworkClientId, @@ -42,9 +42,16 @@ import type { } from '../src/types'; import { NetworkClientType } from '../src/types'; +export type AllNetworkControllerActions = + MessengerActions; + +export type AllNetworkControllerEvents = + MessengerEvents; + export type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + 'Root', + AllNetworkControllerActions, + AllNetworkControllerEvents >; /** @@ -76,22 +83,56 @@ export const TESTNET = { * @returns The messenger. */ export function buildRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** - * Build a restricted messenger for the network controller. + * Build a messenger for the network controller. * - * @param messenger - A messenger. - * @returns The network controller restricted messenger. + * @param rootMessenger - The root messenger. + * @returns The network controller messenger. */ export function buildNetworkControllerMessenger( - messenger = buildRootMessenger(), + rootMessenger = buildRootMessenger(), ): NetworkControllerMessenger { - return messenger.getRestricted({ - name: 'NetworkController', - allowedActions: ['ErrorReportingService:captureException'], - allowedEvents: [], + const networkControllerMessenger = new Messenger< + 'NetworkController', + AllNetworkControllerActions, + AllNetworkControllerEvents, + typeof rootMessenger + >({ + namespace: 'NetworkController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: networkControllerMessenger, + actions: ['ErrorReportingService:captureException'], + }); + return networkControllerMessenger; +} + +/** + * Build a messenger for the error reporting service. + * + * @param rootMessenger - The root messenger. + * @returns The error reporting service messenger. + */ +export function buildErrorReportingServiceMessenger( + rootMessenger = buildRootMessenger(), +): Messenger< + 'ErrorReportingService', + AllNetworkControllerActions, + AllNetworkControllerEvents, + typeof rootMessenger +> { + return new Messenger< + 'ErrorReportingService', + AllNetworkControllerActions, + AllNetworkControllerEvents, + typeof rootMessenger + >({ + namespace: 'ErrorReportingService', + parent: rootMessenger, }); } diff --git a/yarn.lock b/yarn.lock index b2e49bbd25f..411ab461a55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3951,6 +3951,7 @@ __metadata: "@metamask/eth-json-rpc-provider": "npm:^4.1.8" "@metamask/eth-query": "npm:^4.0.0" "@metamask/json-rpc-engine": "npm:^10.0.3" + "@metamask/messenger": "npm:^0.1.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/swappable-obj-proxy": "npm:^2.3.0" "@metamask/utils": "npm:^11.4.2" From 373c5c4f664078188a49833deeb66b00596a124d Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 28 Aug 2025 12:15:49 +0200 Subject: [PATCH 024/247] migrate `TransactionControllerIntegration.test.ts` --- .../TransactionControllerIntegration.test.ts | 110 +++++++++++++----- 1 file changed, 80 insertions(+), 30 deletions(-) diff --git a/packages/transaction-controller/src/TransactionControllerIntegration.test.ts b/packages/transaction-controller/src/TransactionControllerIntegration.test.ts index 3539d6b7982..d49269aff3f 100644 --- a/packages/transaction-controller/src/TransactionControllerIntegration.test.ts +++ b/packages/transaction-controller/src/TransactionControllerIntegration.test.ts @@ -5,7 +5,6 @@ import type { ApprovalControllerEvents, } from '@metamask/approval-controller'; import { ApprovalController } from '@metamask/approval-controller'; -import { Messenger } from '@metamask/base-controller'; import { ApprovalType, BUILT_IN_NETWORKS, @@ -13,6 +12,11 @@ import { InfuraNetworkType, NetworkType, } from '@metamask/controller-utils'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { NetworkController, NetworkClientType, @@ -29,8 +33,7 @@ import { useFakeTimers } from 'sinon'; import { v4 as uuidV4 } from 'uuid'; import type { - TransactionControllerActions, - TransactionControllerEvents, + TransactionControllerMessenger, TransactionControllerOptions, } from './TransactionController'; import { TransactionController } from './TransactionController'; @@ -65,16 +68,25 @@ jest.mock('uuid', () => { }; }); -type UnrestrictedMessenger = Messenger< - | AccountsControllerActions - | ApprovalControllerActions +type AllTransactionControllerActions = + MessengerActions; + +type AllTransactionControllerEvents = + MessengerEvents; + +type AllActions = + | AllTransactionControllerActions | NetworkControllerActions - | TransactionControllerActions - | RemoteFeatureFlagControllerGetStateAction, - | ApprovalControllerEvents + | ApprovalControllerActions + | AccountsControllerActions + | RemoteFeatureFlagControllerGetStateAction; + +type AllEvents = + | AllTransactionControllerEvents | NetworkControllerEvents - | TransactionControllerEvents ->; + | ApprovalControllerEvents; + +type RootMessenger = Messenger<'Root', AllActions, AllEvents>; const uuidV4Mock = jest.mocked(uuidV4); @@ -158,13 +170,19 @@ const setupController = async ( ], }); - const unrestrictedMessenger: UnrestrictedMessenger = new Messenger(); + const rootMessenger: RootMessenger = new Messenger({ namespace: 'Root' }); + + const networkControllerMessenger = new Messenger< + 'NetworkController', + NetworkControllerActions, + NetworkControllerEvents, + typeof rootMessenger + >({ + namespace: 'NetworkController', + parent: rootMessenger, + }); const networkController = new NetworkController({ - messenger: unrestrictedMessenger.getRestricted({ - name: 'NetworkController', - allowedActions: [], - allowedEvents: [], - }), + messenger: networkControllerMessenger, infuraProjectId, getRpcServiceOptions: () => ({ fetch, @@ -177,19 +195,33 @@ const setupController = async ( assert(provider, 'Provider must be available'); assert(blockTracker, 'Provider must be available'); + const approvalControllerMessenger = new Messenger< + 'ApprovalController', + ApprovalControllerActions, + ApprovalControllerEvents, + typeof rootMessenger + >({ + namespace: 'ApprovalController', + parent: rootMessenger, + }); const approvalController = new ApprovalController({ - messenger: unrestrictedMessenger.getRestricted({ - name: 'ApprovalController', - allowedActions: [], - allowedEvents: [], - }), + messenger: approvalControllerMessenger, showApprovalRequest: jest.fn(), typesExcludedFromRateLimiting: [ApprovalType.Transaction], }); - const messenger = unrestrictedMessenger.getRestricted({ - name: 'TransactionController', - allowedActions: [ + const messenger = new Messenger< + 'TransactionController', + MessengerActions, + MessengerEvents, + typeof rootMessenger + >({ + namespace: 'TransactionController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: [ 'AccountsController:getSelectedAccount', 'AccountsController:getState', 'ApprovalController:addRequest', @@ -197,24 +229,42 @@ const setupController = async ( 'NetworkController:findNetworkClientIdByChainId', 'RemoteFeatureFlagController:getState', ], - allowedEvents: ['NetworkController:stateChange'], + events: ['NetworkController:stateChange'], }); const mockGetSelectedAccount = jest .fn() .mockReturnValue(mockData.selectedAccount); - unrestrictedMessenger.registerActionHandler( + const accountsControllerMessenger = new Messenger< + 'AccountsController', + AccountsControllerActions, + never, + typeof rootMessenger + >({ + namespace: 'AccountsController', + parent: rootMessenger, + }); + accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', mockGetSelectedAccount, ); - - unrestrictedMessenger.registerActionHandler( + accountsControllerMessenger.registerActionHandler( 'AccountsController:getState', () => ({}) as never, ); - unrestrictedMessenger.registerActionHandler( + const remoteFeatureFlagControllerMessenger = new Messenger< + 'RemoteFeatureFlagController', + RemoteFeatureFlagControllerGetStateAction, + never, + typeof rootMessenger + >({ + namespace: 'RemoteFeatureFlagController', + parent: rootMessenger, + }); + + remoteFeatureFlagControllerMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', () => ({ cacheTimestamp: 0, remoteFeatureFlags: {} }), ); From 38eb76d884636488d50cbb1e22022e4073811eec Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 28 Aug 2025 12:18:54 +0200 Subject: [PATCH 025/247] update changelogs --- packages/network-controller/CHANGELOG.md | 2 ++ packages/transaction-controller/CHANGELOG.md | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/packages/network-controller/CHANGELOG.md b/packages/network-controller/CHANGELOG.md index a27b90aa487..8db1b2cfad0 100644 --- a/packages/network-controller/CHANGELOG.md +++ b/packages/network-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `NetworkController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ### Deprecated diff --git a/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index 8aa9728f041..78871994f54 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `TransactionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [60.1.0] ### Added From da25c0e130385096db072f8f1b4eb6220d07cc02 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 28 Aug 2025 13:40:44 +0200 Subject: [PATCH 026/247] use jest mock instead of actions handler swap --- .../src/AccountTreeController.test.ts | 64 ++++--------------- 1 file changed, 12 insertions(+), 52 deletions(-) diff --git a/packages/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index e745355d94d..21c99200fed 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -189,6 +189,8 @@ const MOCK_HARDWARE_ACCOUNT_1: InternalAccount = { }, }; +const mockGetSelectedAccountActionHandler = jest.fn(); + /** * Sets up the AccountTreeController for testing. * @@ -271,6 +273,9 @@ function setup({ // Mock AccountsController:getSelectedAccount to return the first account accountsControllerMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', + mockGetSelectedAccountActionHandler, + ); + mockGetSelectedAccountActionHandler.mockImplementation( () => accounts[0] || MOCK_HD_ACCOUNT_1, ); @@ -1513,28 +1518,13 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when AccountsController returns EMPTY_ACCOUNT', () => { - const { - controller, - messenger, - accountsControllerMessenger, - accountTreeControllerMessenger, - } = setup({ + const { controller } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); - // Unregister existing handler and register new one BEFORE init - accountsControllerMessenger.unregisterActionHandler( - 'AccountsController:getSelectedAccount', - ); - accountsControllerMessenger.registerActionHandler( - 'AccountsController:getSelectedAccount', - () => EMPTY_ACCOUNT_MOCK, - ); - messenger.delegate({ - messenger: accountTreeControllerMessenger, - actions: ['AccountsController:getSelectedAccount'], - }); + // Mock action handler BEFORE init + mockGetSelectedAccountActionHandler.mockReturnValue(EMPTY_ACCOUNT_MOCK); controller.init(); @@ -1551,12 +1541,7 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when selected account is not in tree', () => { - const { - controller, - messenger, - accountsControllerMessenger, - accountTreeControllerMessenger, - } = setup({ + const { controller } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1567,17 +1552,7 @@ describe('AccountTreeController', () => { id: 'unknown-account-id', }; - accountsControllerMessenger.unregisterActionHandler( - 'AccountsController:getSelectedAccount', - ); - accountsControllerMessenger.registerActionHandler( - 'AccountsController:getSelectedAccount', - () => unknownAccount, - ); - messenger.delegate({ - messenger: accountTreeControllerMessenger, - actions: ['AccountsController:getSelectedAccount'], - }); + mockGetSelectedAccountActionHandler.mockReturnValue(unknownAccount); controller.init(); @@ -1594,28 +1569,13 @@ describe('AccountTreeController', () => { }); it('returns empty string when no wallets exist and getSelectedAccount returns EMPTY_ACCOUNT', () => { - const { - controller, - messenger, - accountsControllerMessenger, - accountTreeControllerMessenger, - } = setup({ + const { controller } = setup({ accounts: [], keyrings: [], }); // Mock getSelectedAccount to return EMPTY_ACCOUNT_MOCK (id is '') BEFORE init - accountsControllerMessenger.unregisterActionHandler( - 'AccountsController:getSelectedAccount', - ); - accountsControllerMessenger.registerActionHandler( - 'AccountsController:getSelectedAccount', - () => EMPTY_ACCOUNT_MOCK, - ); - messenger.delegate({ - messenger: accountTreeControllerMessenger, - actions: ['AccountsController:getSelectedAccount'], - }); + mockGetSelectedAccountActionHandler.mockReturnValue(EMPTY_ACCOUNT_MOCK); controller.init(); From 5f3e1f3bbaf7d91ec6456bbbb315d7950fecba9f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 28 Aug 2025 13:47:07 +0200 Subject: [PATCH 027/247] fix lint --- packages/approval-controller/src/ApprovalController.test.ts | 4 ++-- packages/transaction-controller/src/utils/eip7702.test.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/approval-controller/src/ApprovalController.test.ts b/packages/approval-controller/src/ApprovalController.test.ts index c9ca5253d3f..1f068825377 100644 --- a/packages/approval-controller/src/ApprovalController.test.ts +++ b/packages/approval-controller/src/ApprovalController.test.ts @@ -236,12 +236,12 @@ function getMessengers() { return { rootMessenger, approvalControllerMessenger: new Messenger< - 'ApprovalController', + typeof controllerName, ApprovalControllerActions, ApprovalControllerEvents, typeof rootMessenger >({ - namespace: 'ApprovalController', + namespace: controllerName, parent: rootMessenger, }), }; diff --git a/packages/transaction-controller/src/utils/eip7702.test.ts b/packages/transaction-controller/src/utils/eip7702.test.ts index af58b1ea7ad..82be470ca94 100644 --- a/packages/transaction-controller/src/utils/eip7702.test.ts +++ b/packages/transaction-controller/src/utils/eip7702.test.ts @@ -2,10 +2,9 @@ import { query } from '@metamask/controller-utils'; import type EthQuery from '@metamask/eth-query'; import { Messenger, - MessengerActions, - MessengerEvents, + type MessengerActions, + type MessengerEvents, } from '@metamask/messenger'; -import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; import type { Hex } from '@metamask/utils'; import { remove0x } from '@metamask/utils'; From 3e6263391a6f81483a760b3a06d199fe1b8aa7f2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 1 Sep 2025 19:13:00 +0200 Subject: [PATCH 028/247] refactor: migrate `AccountsController` to `@metamask/messenger` --- packages/accounts-controller/package.json | 1 + .../src/AccountsController.test.ts | 144 +++++++++++------- .../src/AccountsController.ts | 73 ++++----- yarn.lock | 1 + 4 files changed, 122 insertions(+), 97 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index d7a923c21d6..3a1249f27e7 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -53,6 +53,7 @@ "@metamask/keyring-api": "^20.1.0", "@metamask/keyring-internal-api": "^8.1.0", "@metamask/keyring-utils": "^3.1.0", + "@metamask/messenger": "^0.1.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/superstruct": "^3.1.0", diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 2b7732cf613..5658a2732e7 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,4 +1,3 @@ -import { Messenger } from '@metamask/base-controller'; import { InfuraNetworkType } from '@metamask/controller-utils'; import type { AccountAssetListUpdatedEventPayload, @@ -15,6 +14,12 @@ import { } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + DISABLE_NAMESPACE, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { NetworkClientId } from '@metamask/network-controller'; import type { SnapControllerState } from '@metamask/snaps-controllers'; import { SnapStatus } from '@metamask/snaps-utils'; @@ -25,6 +30,7 @@ import * as uuid from 'uuid'; import type { AccountsControllerActions, AccountsControllerEvents, + AccountsControllerMessenger, AccountsControllerState, AllowedActions, AllowedEvents, @@ -41,6 +47,17 @@ import { keyringTypeToName, } from './utils'; +type AllAccountsControllerActions = + MessengerActions; + +type AllAccountsControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + string, + AllAccountsControllerActions, + AllAccountsControllerEvents +>; + jest.mock('uuid'); const mockUUID = jest.spyOn(uuid, 'v4'); const actualUUID = jest.requireActual('uuid').v4; // We also use uuid.v4 in our mocks @@ -223,21 +240,35 @@ function setExpectedLastSelectedAsAny( */ function buildMessenger() { return new Messenger< - AccountsControllerActions | AllowedActions, - AccountsControllerEvents | AllowedEvents - >(); + string, + AllAccountsControllerActions, + AllAccountsControllerEvents + >({ namespace: DISABLE_NAMESPACE }); } /** - * Builds a restricted messenger for the AccountsController. + * Builds a messenger for the AccountsController. * - * @param messenger - The messenger to restrict. - * @returns The restricted messenger. + * @param rootMessenger - The parent messenger. + * @returns The messenger for AccountsController. */ -function buildAccountsControllerMessenger(messenger = buildMessenger()) { - return messenger.getRestricted({ - name: 'AccountsController', - allowedEvents: [ +function buildAccountsControllerMessenger(rootMessenger = buildMessenger()) { + const accountsControllerMessenger = new Messenger< + 'AccountsController', + AllAccountsControllerActions, + AllAccountsControllerEvents, + typeof rootMessenger + >({ + namespace: 'AccountsController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: accountsControllerMessenger, + actions: [ + 'KeyringController:getState', + 'KeyringController:getKeyringsByType', + ], + events: [ 'SnapController:stateChange', 'KeyringController:stateChange', 'SnapKeyring:accountAssetListUpdated', @@ -245,11 +276,8 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { 'SnapKeyring:accountTransactionsUpdated', 'MultichainNetworkController:networkDidChange', ], - allowedActions: [ - 'KeyringController:getState', - 'KeyringController:getKeyringsByType', - ], }); + return accountsControllerMessenger; } /** @@ -257,7 +285,7 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { * * @param options - The options object. * @param [options.initialState] - The initial state to use for the AccountsController. - * @param [options.messenger] - Messenger to use for the AccountsController. + * @param [options.messenger] - The root messenger to use for creating the AccountsController messenger. * @returns An instance of the AccountsController class. */ function setupAccountsController({ @@ -265,16 +293,11 @@ function setupAccountsController({ messenger = buildMessenger(), }: { initialState?: Partial; - messenger?: Messenger< - AccountsControllerActions | AllowedActions, - AccountsControllerEvents | AllowedEvents - >; + messenger?: RootMessenger; }): { accountsController: AccountsController; - messenger: Messenger< - AccountsControllerActions | AllowedActions, - AccountsControllerEvents | AllowedEvents - >; + messenger: RootMessenger; + accountsControllerMessenger: AccountsControllerMessenger; triggerMultichainNetworkChange: (id: NetworkClientId | CaipChainId) => void; } { const accountsControllerMessenger = @@ -288,7 +311,12 @@ function setupAccountsController({ const triggerMultichainNetworkChange = (id: NetworkClientId | CaipChainId) => messenger.publish('MultichainNetworkController:networkDidChange', id); - return { accountsController, messenger, triggerMultichainNetworkChange }; + return { + accountsController, + messenger, + accountsControllerMessenger, + triggerMultichainNetworkChange, + }; } describe('AccountsController', () => { @@ -1136,11 +1164,10 @@ describe('AccountsController', () => { it('publishes accountAdded event', async () => { const messenger = buildMessenger(); - const messengerSpy = jest.spyOn(messenger, 'publish'); mockUUIDWithNormalAccounts([mockAccount, mockAccount2]); - setupAccountsController({ + const { accountsControllerMessenger } = setupAccountsController({ initialState: { internalAccounts: { accounts: { @@ -1152,6 +1179,8 @@ describe('AccountsController', () => { messenger, }); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -1172,11 +1201,10 @@ describe('AccountsController', () => { [], ); - // First call is 'KeyringController:stateChange' + // First call is 'AccountsController:stateChange' expect(messengerSpy).toHaveBeenNthCalledWith( - // 1. KeyringController:stateChange - // 2. AccountsController:stateChange - 3, + // 1. AccountsController:stateChange + 2, 'AccountsController:accountAdded', MockExpectedInternalAccountBuilder.from(mockAccount2) .setExpectedLastSelectedAsAny() @@ -1437,11 +1465,10 @@ describe('AccountsController', () => { it('publishes accountRemoved event', async () => { const messenger = buildMessenger(); - const messengerSpy = jest.spyOn(messenger, 'publish'); mockUUIDWithNormalAccounts([mockAccount, mockAccount2]); - setupAccountsController({ + const { accountsControllerMessenger } = setupAccountsController({ initialState: { internalAccounts: { accounts: { @@ -1454,6 +1481,8 @@ describe('AccountsController', () => { messenger, }); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -1473,11 +1502,10 @@ describe('AccountsController', () => { [], ); - // First call is 'KeyringController:stateChange' + // First call is 'AccountsController:stateChange' expect(messengerSpy).toHaveBeenNthCalledWith( - // 1. KeyringController:stateChange - // 2. AccountsController:stateChange - 3, + // 1. AccountsController:stateChange + 2, 'AccountsController:accountRemoved', mockAccount3.id, ); @@ -3085,19 +3113,20 @@ describe('AccountsController', () => { }, type: BtcAccountType.P2wpkh, }); - const { accountsController, messenger } = setupAccountsController({ - initialState: { - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - [mockNonEvmAccount.id]: mockNonEvmAccount, + const { accountsController, accountsControllerMessenger } = + setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockNonEvmAccount.id]: mockNonEvmAccount, + }, + selectedAccount: mockAccount.id, }, - selectedAccount: mockAccount.id, }, - }, - }); + }); - const messengerSpy = jest.spyOn(messenger, 'publish'); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); accountsController.setSelectedAccount(mockNonEvmAccount.id); @@ -3191,10 +3220,10 @@ describe('AccountsController', () => { }); it('publishes the accountRenamed event', () => { - const { accountsController, messenger } = + const { accountsController, accountsControllerMessenger } = setupAccountsController(mockState); - const messengerSpy = jest.spyOn(messenger, 'publish'); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); accountsController.setAccountNameAndSelectAccount( mockAccount.id, @@ -3269,16 +3298,17 @@ describe('AccountsController', () => { }); it('publishes the accountRenamed event', () => { - const { accountsController, messenger } = setupAccountsController({ - initialState: { - internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, - selectedAccount: mockAccount.id, + const { accountsController, accountsControllerMessenger } = + setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, }, - }, - }); + }); - const messengerSpy = jest.spyOn(messenger, 'publish'); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); accountsController.setAccountName(mockAccount.id, 'new name'); diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index ff1c6c429bf..b8081c882cf 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -1,10 +1,8 @@ import { type ControllerGetStateAction, type ControllerStateChangeEvent, - type ExtractEventPayload, - type RestrictedMessenger, BaseController, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { type SnapKeyringAccountAssetListUpdatedEvent, type SnapKeyringAccountBalancesUpdatedEvent, @@ -29,6 +27,7 @@ import { } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { isScopeEqualToAny } from '@metamask/keyring-utils'; +import type { Messenger, ExtractEventPayload } from '@metamask/messenger'; import type { NetworkClientId } from '@metamask/network-controller'; import type { SnapControllerState, @@ -212,12 +211,10 @@ export type AccountsControllerEvents = | AccountsControllerAccountTransactionsUpdatedEvent | AccountsControllerAccountAssetListUpdatedEvent; -export type AccountsControllerMessenger = RestrictedMessenger< +export type AccountsControllerMessenger = Messenger< typeof controllerName, AccountsControllerActions | AllowedActions, - AccountsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AccountsControllerEvents | AllowedEvents >; const accountsControllerMetadata = { @@ -484,7 +481,7 @@ export class AccountsController extends BaseController< state.internalAccounts.selectedAccount = account.id; }); - this.messagingSystem.publish( + this.messenger.publish( 'AccountsController:accountRenamed', internalAccount, ); @@ -528,7 +525,7 @@ export class AccountsController extends BaseController< }); if (metadata.name) { - this.messagingSystem.publish( + this.messenger.publish( 'AccountsController:accountRenamed', internalAccount, ); @@ -548,9 +545,7 @@ export class AccountsController extends BaseController< const internalAccounts: AccountsControllerState['internalAccounts']['accounts'] = {}; - const { keyrings } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { keyrings } = this.messenger.call('KeyringController:getState'); for (const keyring of keyrings) { const keyringTypeName = keyringTypeToName(keyring.type); @@ -707,7 +702,7 @@ export class AccountsController extends BaseController< * @returns The Snap keyring if available. */ #getSnapKeyring(): SnapKeyring | undefined { - const [snapKeyring] = this.messagingSystem.call( + const [snapKeyring] = this.messenger.call( 'KeyringController:getKeyringsByType', SnapKeyring.type, ); @@ -731,7 +726,7 @@ export class AccountsController extends BaseController< event: EventType, ...payload: ExtractEventPayload ): void { - this.messagingSystem.publish(event, ...payload); + this.messenger.publish(event, ...payload); } /** @@ -882,11 +877,11 @@ export class AccountsController extends BaseController< // Now publish events for (const id of diff.removed) { - this.messagingSystem.publish('AccountsController:accountRemoved', id); + this.messenger.publish('AccountsController:accountRemoved', id); } for (const account of diff.added) { - this.messagingSystem.publish('AccountsController:accountAdded', account); + this.messenger.publish('AccountsController:accountAdded', account); } // NOTE: Since we also track "updated" accounts with our patches, we could fire a new event @@ -939,12 +934,12 @@ export class AccountsController extends BaseController< // `selectedAccount` to be non-empty. if (account) { if (isEvmAccountType(account.type)) { - this.messagingSystem.publish( + this.messenger.publish( 'AccountsController:selectedEvmAccountChange', account, ); } - this.messagingSystem.publish( + this.messenger.publish( 'AccountsController:selectedAccountChange', account, ); @@ -1186,17 +1181,15 @@ export class AccountsController extends BaseController< * Subscribes to message events. */ #subscribeToMessageEvents() { - this.messagingSystem.subscribe( - 'SnapController:stateChange', - (snapStateState) => this.#handleOnSnapStateChange(snapStateState), + this.messenger.subscribe('SnapController:stateChange', (snapStateState) => + this.#handleOnSnapStateChange(snapStateState), ); - this.messagingSystem.subscribe( - 'KeyringController:stateChange', - (keyringState) => this.#handleOnKeyringStateChange(keyringState), + this.messenger.subscribe('KeyringController:stateChange', (keyringState) => + this.#handleOnKeyringStateChange(keyringState), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'SnapKeyring:accountAssetListUpdated', (snapAccountEvent) => this.#handleOnSnapKeyringAccountEvent( @@ -1205,7 +1198,7 @@ export class AccountsController extends BaseController< ), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'SnapKeyring:accountBalancesUpdated', (snapAccountEvent) => this.#handleOnSnapKeyringAccountEvent( @@ -1214,7 +1207,7 @@ export class AccountsController extends BaseController< ), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'SnapKeyring:accountTransactionsUpdated', (snapAccountEvent) => this.#handleOnSnapKeyringAccountEvent( @@ -1224,7 +1217,7 @@ export class AccountsController extends BaseController< ); // Handle account change when multichain network is changed - this.messagingSystem.subscribe( + this.messenger.subscribe( 'MultichainNetworkController:networkDidChange', (id) => this.#handleOnMultichainNetworkDidChange(id), ); @@ -1234,62 +1227,62 @@ export class AccountsController extends BaseController< * Registers message handlers for the AccountsController. */ #registerMessageHandlers() { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setSelectedAccount`, this.setSelectedAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:listAccounts`, this.listAccounts.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:listMultichainAccounts`, this.listMultichainAccounts.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setAccountName`, this.setAccountName.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setAccountNameAndSelectAccount`, this.setAccountNameAndSelectAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateAccounts`, this.updateAccounts.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getSelectedAccount`, this.getSelectedAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getSelectedMultichainAccount`, this.getSelectedMultichainAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getAccountByAddress`, this.getAccountByAddress.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getNextAvailableAccountName`, this.getNextAvailableAccountName.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `AccountsController:getAccount`, this.getAccount.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `AccountsController:updateAccountMetadata`, this.updateAccountMetadata.bind(this), ); diff --git a/yarn.lock b/yarn.lock index d034e506bb5..5fadf9cb940 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2443,6 +2443,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.0.0" "@metamask/keyring-internal-api": "npm:^8.1.0" "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/network-controller": "npm:^24.1.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" From 443a6ee5eed0a7d3b25051a3c5270134e489292c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 1 Sep 2025 19:18:13 +0200 Subject: [PATCH 029/247] update changelog --- packages/accounts-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/accounts-controller/CHANGELOG.md b/packages/accounts-controller/CHANGELOG.md index 54b0871af5d..0aed9097df6 100644 --- a/packages/accounts-controller/CHANGELOG.md +++ b/packages/accounts-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6426](https://github.com/MetaMask/core/pull/6426)) + - Previously, `AccountsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [33.0.0] From b25ee8eac974f3ae3d8d37a5aae8409e59bde397 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 1 Sep 2025 19:22:18 +0200 Subject: [PATCH 030/247] refactor `buildMessenger` --- .../src/AccountsController.test.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 5658a2732e7..fd714432f72 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -234,16 +234,12 @@ function setExpectedLastSelectedAsAny( } /** - * Builds a new instance of the Messenger class for the AccountsController. + * Builds a new instance of the Root Messenger. * - * @returns A new instance of the Messenger class for the AccountsController. + * @returns A new instance of the Root Messenger. */ -function buildMessenger() { - return new Messenger< - string, - AllAccountsControllerActions, - AllAccountsControllerEvents - >({ namespace: DISABLE_NAMESPACE }); +function buildMessenger(): RootMessenger { + return new Messenger({ namespace: DISABLE_NAMESPACE }); } /** From 3453a8d26d39e958fb71c95065680326afc09221 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 1 Sep 2025 19:36:03 +0200 Subject: [PATCH 031/247] remove unused variables --- packages/accounts-controller/src/AccountsController.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index fd714432f72..2ed026070a4 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -28,12 +28,8 @@ import type { V4Options } from 'uuid'; import * as uuid from 'uuid'; import type { - AccountsControllerActions, - AccountsControllerEvents, AccountsControllerMessenger, AccountsControllerState, - AllowedActions, - AllowedEvents, } from './AccountsController'; import { AccountsController, EMPTY_ACCOUNT } from './AccountsController'; import { From 3c6828e921a6a9dc8572ba70a0c2d6daa553647a Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 11:51:26 +0200 Subject: [PATCH 032/247] update messenger imports --- .../accounts-controller/src/AccountsController.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 2ed026070a4..b0557d7bad8 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -15,10 +15,11 @@ import { import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { + MOCK_ANY_NAMESPACE, Messenger, - DISABLE_NAMESPACE, type MessengerActions, type MessengerEvents, + type MockAnyNamespace, } from '@metamask/messenger'; import type { NetworkClientId } from '@metamask/network-controller'; import type { SnapControllerState } from '@metamask/snaps-controllers'; @@ -49,7 +50,7 @@ type AllAccountsControllerActions = type AllAccountsControllerEvents = MessengerEvents; type RootMessenger = Messenger< - string, + MockAnyNamespace, AllAccountsControllerActions, AllAccountsControllerEvents >; @@ -235,7 +236,7 @@ function setExpectedLastSelectedAsAny( * @returns A new instance of the Root Messenger. */ function buildMessenger(): RootMessenger { - return new Messenger({ namespace: DISABLE_NAMESPACE }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** From f482a843f7250a8f873d3ccefd0a8b855e43d2d2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 15:06:18 +0200 Subject: [PATCH 033/247] refactor: migrate `gas-fee-controller` to `@metamask/messenger` --- .../src/GasFeeController.test.ts | 81 ++++++++++++------- .../src/GasFeeController.ts | 26 +++--- .../src/StaticIntervalPollingController.ts | 8 ++ packages/polling-controller/src/index.ts | 1 + 4 files changed, 71 insertions(+), 45 deletions(-) diff --git a/packages/gas-fee-controller/src/GasFeeController.test.ts b/packages/gas-fee-controller/src/GasFeeController.test.ts index 53b1f821e82..1283549f0a5 100644 --- a/packages/gas-fee-controller/src/GasFeeController.test.ts +++ b/packages/gas-fee-controller/src/GasFeeController.test.ts @@ -1,16 +1,19 @@ -import { Messenger } from '@metamask/base-controller'; import { ChainId, convertHexToDecimal, toHex, } from '@metamask/controller-utils'; import EthQuery from '@metamask/eth-query'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { NetworkController, NetworkStatus } from '@metamask/network-controller'; import type { - NetworkControllerGetEIP1559CompatibilityAction, - NetworkControllerGetNetworkClientByIdAction, - NetworkControllerGetStateAction, - NetworkControllerNetworkDidChangeEvent, + NetworkControllerMessenger, NetworkState, } from '@metamask/network-controller'; import type { Hex } from '@metamask/utils'; @@ -30,12 +33,11 @@ import { } from './gas-util'; import { GAS_ESTIMATE_TYPES, GasFeeController } from './GasFeeController'; import type { + GasFeeMessenger, GasFeeState, - GasFeeStateChange, GasFeeStateEthGasPrice, GasFeeStateFeeMarket, GasFeeStateLegacy, - GetGasFeeState, } from './GasFeeController'; jest.mock('./determineGasFeeCalculations'); @@ -48,39 +50,46 @@ const mockedDetermineGasFeeCalculations = const name = 'GasFeeController'; -type MainMessenger = Messenger< - | GetGasFeeState - | NetworkControllerGetStateAction - | NetworkControllerGetNetworkClientByIdAction - | NetworkControllerGetEIP1559CompatibilityAction, - GasFeeStateChange | NetworkControllerNetworkDidChangeEvent ->; +type AllGasFeeControllerActions = MessengerActions; +type AllGasFeeControllerEvents = MessengerEvents; + +type AllNetworkControllerActions = MessengerActions; +type AllNetworkControllerEvents = MessengerEvents; + +type AllActions = AllGasFeeControllerActions | AllNetworkControllerActions; +type AllEvents = AllGasFeeControllerEvents | AllNetworkControllerEvents; -const getMessenger = (): MainMessenger => { - return new Messenger(); +type RootMessenger = Messenger; + +const getRootMessenger = (): RootMessenger => { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); }; const setupNetworkController = async ({ - unrestrictedMessenger, + rootMessenger, state, clock, initializeProvider = true, }: { - unrestrictedMessenger: MainMessenger; + rootMessenger: RootMessenger; state: Partial; clock: sinon.SinonFakeTimers; initializeProvider?: boolean; }) => { - const restrictedMessenger = unrestrictedMessenger.getRestricted({ - name: 'NetworkController', - allowedActions: [], - allowedEvents: [], + const networkControllerMessenger = new Messenger< + 'NetworkController', + MessengerActions, + MessengerEvents, + typeof rootMessenger + >({ + namespace: 'NetworkController', + parent: rootMessenger, }); const infuraProjectId = '123'; const networkController = new NetworkController({ - messenger: restrictedMessenger, + messenger: networkControllerMessenger, state, infuraProjectId, getRpcServiceOptions: () => ({ @@ -117,16 +126,26 @@ const setupNetworkController = async ({ return networkController; }; -const getRestrictedMessenger = (messenger: MainMessenger) => { - return messenger.getRestricted({ - name, - allowedActions: [ +const getGasFeeControllerMessenger = (rootMessenger: RootMessenger) => { + const gasFeeControllerMessenger = new Messenger< + 'GasFeeController', + AllGasFeeControllerActions, + AllGasFeeControllerEvents, + typeof rootMessenger + >({ + namespace: 'GasFeeController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: gasFeeControllerMessenger, + actions: [ 'NetworkController:getState', 'NetworkController:getNetworkClientById', 'NetworkController:getEIP1559Compatibility', ], - allowedEvents: ['NetworkController:networkDidChange'], + events: ['NetworkController:networkDidChange'], }); + return gasFeeControllerMessenger; }; /** @@ -282,14 +301,14 @@ describe('GasFeeController', () => { interval?: number; initializeNetworkProvider?: boolean; } = {}) { - const messenger = getMessenger(); + const rootMessenger = getRootMessenger(); networkController = await setupNetworkController({ - unrestrictedMessenger: messenger, + rootMessenger, state: networkControllerState, clock, initializeProvider: initializeNetworkProvider, }); - const restrictedMessenger = getRestrictedMessenger(messenger); + const restrictedMessenger = getGasFeeControllerMessenger(rootMessenger); gasFeeController = new GasFeeController({ getProvider: jest.fn(), getChainId, diff --git a/packages/gas-fee-controller/src/GasFeeController.ts b/packages/gas-fee-controller/src/GasFeeController.ts index c26a08ee28b..d187077ad6f 100644 --- a/packages/gas-fee-controller/src/GasFeeController.ts +++ b/packages/gas-fee-controller/src/GasFeeController.ts @@ -1,14 +1,14 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { convertHexToDecimal, safelyExecute, toHex, } from '@metamask/controller-utils'; import EthQuery from '@metamask/eth-query'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerGetEIP1559CompatibilityAction, @@ -18,7 +18,7 @@ import type { NetworkState, ProviderProxy, } from '@metamask/network-controller'; -import { StaticIntervalPollingController } from '@metamask/polling-controller'; +import { StaticIntervalPollingControllerNext } from '@metamask/polling-controller'; import type { Hex } from '@metamask/utils'; import { v1 as random } from 'uuid'; @@ -240,12 +240,10 @@ type AllowedActions = | NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetEIP1559CompatibilityAction; -type GasFeeMessenger = RestrictedMessenger< +export type GasFeeMessenger = Messenger< typeof name, GasFeeControllerActions | AllowedActions, - GasFeeControllerEvents | NetworkControllerNetworkDidChangeEvent, - AllowedActions['type'], - NetworkControllerNetworkDidChangeEvent['type'] + GasFeeControllerEvents | NetworkControllerNetworkDidChangeEvent >; const defaultState: GasFeeState = { @@ -264,7 +262,7 @@ type GasFeePollingInput = { /** * Controller that retrieves gas fee estimate data and polls for updated data on a set interval */ -export class GasFeeController extends StaticIntervalPollingController()< +export class GasFeeController extends StaticIntervalPollingControllerNext()< typeof name, GasFeeState, GasFeeMessenger @@ -376,14 +374,14 @@ export class GasFeeController extends StaticIntervalPollingController() => StaticIntervalPollingControllerMixin( BaseController, ); + +export const StaticIntervalPollingControllerNext = < + PollingInput extends Json, +>() => + StaticIntervalPollingControllerMixin( + BaseControllerNext, + ); diff --git a/packages/polling-controller/src/index.ts b/packages/polling-controller/src/index.ts index ba1758c443b..f8104b9d5c9 100644 --- a/packages/polling-controller/src/index.ts +++ b/packages/polling-controller/src/index.ts @@ -6,6 +6,7 @@ export { export { StaticIntervalPollingControllerOnly, StaticIntervalPollingController, + StaticIntervalPollingControllerNext, } from './StaticIntervalPollingController'; export type { IPollingController } from './types'; From b1760391c7120b874f9c51cf07e93766bdf10407 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 22:38:46 +0200 Subject: [PATCH 034/247] migrate `NftController` messenger --- .../src/NftController.test.ts | 142 ++++++++++-------- .../assets-controllers/src/NftController.ts | 58 ++++--- 2 files changed, 105 insertions(+), 95 deletions(-) diff --git a/packages/assets-controllers/src/NftController.test.ts b/packages/assets-controllers/src/NftController.test.ts index 3004b667bad..fa172d12986 100644 --- a/packages/assets-controllers/src/NftController.test.ts +++ b/packages/assets-controllers/src/NftController.test.ts @@ -6,7 +6,6 @@ import type { } from '@metamask/accounts-controller'; import type { ApprovalControllerMessenger } from '@metamask/approval-controller'; import { ApprovalController } from '@metamask/approval-controller'; -import { Messenger } from '@metamask/base-controller'; import { IPFS_DEFAULT_GATEWAY_URL, ERC1155, @@ -22,14 +21,18 @@ import { convertHexToDecimal, } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; -import type { - NetworkClientConfiguration, - NetworkClientId, +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; +import { + type NetworkClientConfiguration, + type NetworkClientId, } from '@metamask/network-controller'; -import type { - BulkPhishingDetectionScanResponse, - PhishingControllerBulkScanUrlsAction, -} from '@metamask/phishing-controller'; +import type { BulkPhishingDetectionScanResponse } from '@metamask/phishing-controller'; import { RecommendedAction } from '@metamask/phishing-controller'; import { getDefaultPreferencesState, @@ -55,24 +58,29 @@ import type { Nft, NftControllerState, NftControllerMessenger, - AllowedActions as NftControllerAllowedActions, - AllowedEvents as NftControllerAllowedEvents, NFTStandardType, NftMetadata, } from './NftController'; import { NftController } from './NftController'; import type { Collection } from './NftDetectionController'; import { createMockInternalAccount } from '../../accounts-controller/src/tests/mocks'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildCustomNetworkClientConfiguration, buildMockFindNetworkClientIdByChainId, buildMockGetNetworkClientById, } from '../../network-controller/tests/helpers'; +type AllActions = + | MessengerActions + | MessengerActions; + +type AllEvents = + | MessengerEvents + | MessengerEvents + | AccountsControllerSelectedAccountChangeEvent; + +type RootMessenger = Messenger; + const CRYPTOPUNK_ADDRESS = '0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB'; const ERC721_KUDOSADDRESS = '0x2aEa4Add166EBf38b63d09a75dE1a7b94Aa24163'; const ERC721_KUDOS_TOKEN_ID = '1203'; @@ -223,15 +231,9 @@ function setupController({ defaultSelectedAccount?: InternalAccount; mockGetNetworkClientIdByChainId?: Record; } = {}) { - const messenger = new Messenger< - | ExtractAvailableAction - | NftControllerAllowedActions - | ExtractAvailableAction, - | ExtractAvailableEvent - | NftControllerAllowedEvents - | ExtractAvailableEvent - | AccountsControllerSelectedAccountChangeEvent - >(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const getNetworkClientById = buildMockGetNetworkClientById( mockNetworkClientConfigurationsByNetworkClientId, @@ -328,10 +330,14 @@ function setupController({ mockGetERC1155TokenURI, ); - const approvalControllerMessenger = messenger.getRestricted({ - name: 'ApprovalController', - allowedActions: [], - allowedEvents: [], + const approvalControllerMessenger = new Messenger< + 'ApprovalController', + MessengerActions, + MessengerEvents, + RootMessenger + >({ + namespace: 'ApprovalController', + parent: messenger, }); const approvalController = new ApprovalController({ @@ -347,14 +353,18 @@ function setupController({ ); } - const nftControllerMessenger = messenger.getRestricted< + const nftControllerMessenger = new Messenger< typeof controllerName, - | PhishingControllerBulkScanUrlsAction['type'] - | NftControllerAllowedActions['type'], - NftControllerAllowedEvents['type'] + MessengerActions, + MessengerEvents, + RootMessenger >({ - name: controllerName, - allowedActions: [ + namespace: controllerName, + parent: messenger, + }); + messenger.delegate({ + messenger: nftControllerMessenger, + actions: [ 'ApprovalController:addRequest', 'AccountsController:getSelectedAccount', 'AccountsController:getAccount', @@ -368,7 +378,7 @@ function setupController({ 'NetworkController:findNetworkClientIdByChainId', 'PhishingController:bulkScanUrls', ], - allowedEvents: [ + events: [ 'AccountsController:selectedEvmAccountChange', 'PreferencesController:stateChange', ], @@ -376,7 +386,7 @@ function setupController({ const nftController = new NftController({ onNftAdded: jest.fn(), - messenger: nftControllerMessenger as NftControllerMessenger, + messenger: nftControllerMessenger, ...options, }); @@ -403,6 +413,7 @@ function setupController({ return { nftController, messenger, + nftControllerMessenger, approvalController, triggerPreferencesStateChange, triggerSelectedAccountChange, @@ -647,11 +658,11 @@ describe('NftController', () => { }); it('should error if the user does not own the suggested ERC721 NFT', async function () { - const { nftController, messenger } = setupController({ + const { nftController, nftControllerMessenger } = setupController({ getERC721OwnerOf: jest.fn().mockImplementation(() => '0x12345abcefg'), }); - const callActionSpy = jest.spyOn(messenger, 'call'); + const callActionSpy = jest.spyOn(nftControllerMessenger, 'call'); await expect(() => nftController.watchNft( @@ -686,11 +697,11 @@ describe('NftController', () => { }); it('should error if the user does not own the suggested ERC1155 NFT', async function () { - const { nftController, messenger } = setupController({ + const { nftController, nftControllerMessenger } = setupController({ getERC1155BalanceOf: jest.fn().mockImplementation(() => new BN(0)), }); - const callActionSpy = jest.spyOn(messenger, 'call'); + const callActionSpy = jest.spyOn(nftControllerMessenger, 'call'); await expect(() => nftController.watchNft( @@ -721,7 +732,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -748,7 +759,7 @@ describe('NftController', () => { (v4 as jest.Mock).mockImplementationOnce(() => requestId); const callActionSpy = jest - .spyOn(messenger, 'call') + .spyOn(nftControllerMessenger, 'call') // 1. `AccountsController:getAccount` .mockReturnValueOnce(OWNER_ACCOUNT) // 2. `AssetsContractController:getERC721OwnerOf` @@ -848,7 +859,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -874,7 +885,7 @@ describe('NftController', () => { (v4 as jest.Mock).mockImplementationOnce(() => requestId); const callActionSpy = jest - .spyOn(messenger, 'call') + .spyOn(nftControllerMessenger, 'call') // 1. `AccountsController:getAccount` .mockReturnValueOnce(OWNER_ACCOUNT) // 2. `AssetsContractController:getERC721OwnerOf` @@ -974,7 +985,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -1000,7 +1011,7 @@ describe('NftController', () => { (v4 as jest.Mock).mockImplementationOnce(() => requestId); const callActionSpy = jest - .spyOn(messenger, 'call') + .spyOn(nftControllerMessenger, 'call') // 1. `AccountsController:getAccount` .mockReturnValueOnce(OWNER_ACCOUNT) // 2. `AssetsContractController:getERC721OwnerOf` @@ -1100,7 +1111,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -1127,7 +1138,7 @@ describe('NftController', () => { (v4 as jest.Mock).mockImplementationOnce(() => requestId); const callActionSpy = jest - .spyOn(messenger, 'call') + .spyOn(nftControllerMessenger, 'call') // 1. `AccountsController:getAccount` .mockReturnValueOnce(OWNER_ACCOUNT) // 2. `AssetsContractController:getERC721OwnerOf` @@ -1228,7 +1239,7 @@ describe('NftController', () => { const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -1258,7 +1269,7 @@ describe('NftController', () => { (v4 as jest.Mock).mockImplementationOnce(() => requestId); const callActionSpy = jest - .spyOn(messenger, 'call') + .spyOn(nftControllerMessenger, 'call') // 1. `AccountsController:getAccount` .mockReturnValueOnce(OWNER_ACCOUNT) // 2. `AssetsContractController:getERC721OwnerOf` @@ -1361,20 +1372,23 @@ describe('NftController', () => { }), ); - const { nftController, messenger, triggerPreferencesStateChange } = - setupController({ - getAccount: jest.fn().mockReturnValue(OWNER_ACCOUNT), - getERC721OwnerOf: jest - .fn() - .mockRejectedValue(new Error('Not an ERC721 contract')), - getERC1155BalanceOf: jest.fn().mockResolvedValue(new BN(1)), - getERC721TokenURI: jest - .fn() - .mockRejectedValue(new Error('Not an ERC721 contract')), - getERC1155TokenURI: jest - .fn() - .mockResolvedValue('https://testtokenuri.com'), - }); + const { + nftController, + nftControllerMessenger, + triggerPreferencesStateChange, + } = setupController({ + getAccount: jest.fn().mockReturnValue(OWNER_ACCOUNT), + getERC721OwnerOf: jest + .fn() + .mockRejectedValue(new Error('Not an ERC721 contract')), + getERC1155BalanceOf: jest.fn().mockResolvedValue(new BN(1)), + getERC721TokenURI: jest + .fn() + .mockRejectedValue(new Error('Not an ERC721 contract')), + getERC1155TokenURI: jest + .fn() + .mockResolvedValue('https://testtokenuri.com'), + }); triggerPreferencesStateChange({ ...getDefaultPreferencesState(), isIpfsGatewayEnabled: true, @@ -1387,7 +1401,7 @@ describe('NftController', () => { (v4 as jest.Mock).mockImplementationOnce(() => requestId); const callActionSpy = jest - .spyOn(messenger, 'call') + .spyOn(nftControllerMessenger, 'call') // 1. `AccountsController:getAccount` .mockReturnValueOnce(OWNER_ACCOUNT) // 2. `AssetsContractController:getERC721OwnerOf` diff --git a/packages/assets-controllers/src/NftController.ts b/packages/assets-controllers/src/NftController.ts index b80785ce08b..1b78d1ec7a6 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -5,14 +5,11 @@ import type { AccountsControllerGetSelectedAccountAction, } from '@metamask/accounts-controller'; import type { AddApprovalRequest } from '@metamask/approval-controller'; -import type { - RestrictedMessenger, - ControllerStateChangeEvent, -} from '@metamask/base-controller'; import { BaseController, + type ControllerStateChangeEvent, type ControllerGetStateAction, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { safelyExecute, handleFetch, @@ -29,6 +26,7 @@ import { toHex, } from '@metamask/controller-utils'; import { type InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction, @@ -264,12 +262,10 @@ export type NftControllerEvents = NftControllerStateChangeEvent; /** * The messenger of the {@link NftController}. */ -export type NftControllerMessenger = RestrictedMessenger< +export type NftControllerMessenger = Messenger< typeof controllerName, NftControllerActions | AllowedActions, - NftControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + NftControllerEvents | AllowedEvents >; export const getDefaultNftControllerState = (): NftControllerState => ({ @@ -359,7 +355,7 @@ export class NftController extends BaseController< }, }); - this.#selectedAccountId = this.messagingSystem.call( + this.#selectedAccountId = this.messenger.call( 'AccountsController:getSelectedAccount', ).id; this.#ipfsGateway = ipfsGateway; @@ -368,14 +364,14 @@ export class NftController extends BaseController< this.#isIpfsGatewayEnabled = isIpfsGatewayEnabled; this.#onNftAdded = onNftAdded; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'PreferencesController:stateChange', // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/no-misused-promises this.#onPreferencesControllerStateChange.bind(this), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:selectedEvmAccountChange', // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/no-misused-promises @@ -396,7 +392,7 @@ export class NftController extends BaseController< openSeaEnabled, isIpfsGatewayEnabled, }: PreferencesState) { - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getSelectedAccount', ); this.#selectedAccountId = selectedAccount.id; @@ -703,7 +699,7 @@ export class NftController extends BaseController< ): Promise<[string, string]> { // try ERC721 uri try { - const uri = await this.messagingSystem.call( + const uri = await this.messenger.call( 'AssetsContractController:getERC721TokenURI', contractAddress, tokenId, @@ -716,7 +712,7 @@ export class NftController extends BaseController< // try ERC1155 uri try { - const tokenURI = await this.messagingSystem.call( + const tokenURI = await this.messenger.call( 'AssetsContractController:getERC1155TokenURI', contractAddress, tokenId, @@ -759,7 +755,7 @@ export class NftController extends BaseController< ): Promise { const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -808,12 +804,12 @@ export class NftController extends BaseController< Pick > { const [name, symbol] = await Promise.all([ - this.messagingSystem.call( + this.messenger.call( 'AssetsContractController:getERC721AssetName', contractAddress, networkClientId, ), - this.messagingSystem.call( + this.messenger.call( 'AssetsContractController:getERC721AssetSymbol', contractAddress, networkClientId, @@ -1026,7 +1022,7 @@ export class NftController extends BaseController< const { allNftContracts } = this.state; const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1379,7 +1375,7 @@ export class NftController extends BaseController< ): Promise { // Checks the ownership for ERC-721. try { - const owner = await this.messagingSystem.call( + const owner = await this.messenger.call( 'AssetsContractController:getERC721OwnerOf', nftAddress, tokenId, @@ -1393,7 +1389,7 @@ export class NftController extends BaseController< // Checks the ownership for ERC-1155. try { - const balance = await this.messagingSystem.call( + const balance = await this.messenger.call( 'AssetsContractController:getERC1155BalanceOf', ownerAddress, nftAddress, @@ -1513,7 +1509,7 @@ export class NftController extends BaseController< ); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -1567,7 +1563,7 @@ export class NftController extends BaseController< const unsanitizedResults = await Promise.all( nftsWithChecksumAdr.map(async (nft) => { // Each NFT should have a chainId; convert nft.chainId to networkClientId - const networkClientId = this.messagingSystem.call( + const networkClientId = this.messenger.call( 'NetworkController:findNetworkClientIdByChainId', toHex(nft.chainId as number), ); @@ -1672,7 +1668,7 @@ export class NftController extends BaseController< const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1714,7 +1710,7 @@ export class NftController extends BaseController< const addressToSearch = this.#getAddressOrSelectedAddress(userAddress); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1765,7 +1761,7 @@ export class NftController extends BaseController< const addressToSearch = this.#getAddressOrSelectedAddress(userAddress); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1841,7 +1837,7 @@ export class NftController extends BaseController< const addressToSearch = this.#getAddressOrSelectedAddress(userAddress); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1892,7 +1888,7 @@ export class NftController extends BaseController< const addressToSearch = this.#getAddressOrSelectedAddress(userAddress); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -2063,7 +2059,7 @@ export class NftController extends BaseController< } async _requestApproval(suggestedNftMeta: SuggestedNftMeta) { - return this.messagingSystem.call( + return this.messenger.call( 'ApprovalController:addRequest', { id: suggestedNftMeta.id, @@ -2092,7 +2088,7 @@ export class NftController extends BaseController< } // If the address is not defined (or empty), we fallback to the currently selected account's address - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getAccount', this.#selectedAccountId, ); @@ -2205,7 +2201,7 @@ export class NftController extends BaseController< try { // Use bulkScanUrls to check all URLs at once - const bulkScanResponse = await this.messagingSystem.call( + const bulkScanResponse = await this.messenger.call( 'PhishingController:bulkScanUrls', urlsToCheck, ); From 01cd576bc6a1db03cd2faee1f35c9d5cbd9affce Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 22:39:01 +0200 Subject: [PATCH 035/247] migrate `TokensController` messenger --- .../src/TokensController.test.ts | 66 ++++++++++++------- .../src/TokensController.ts | 40 ++++++----- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 5dee2972864..0c43725a759 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -1,10 +1,9 @@ import { Contract } from '@ethersproject/contracts'; -import type { ApprovalStateChange } from '@metamask/approval-controller'; +import type { ApprovalControllerMessenger } from '@metamask/approval-controller'; import { ApprovalController, type ApprovalControllerState, } from '@metamask/approval-controller'; -import { Messenger } from '@metamask/base-controller'; import contractMaps from '@metamask/contract-metadata'; import { ApprovalType, @@ -14,6 +13,13 @@ import { InfuraNetworkType, } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { NetworkClientConfiguration, NetworkClientId, @@ -27,10 +33,6 @@ import { v1 as uuidV1 } from 'uuid'; import { FakeProvider } from '../../../tests/fake-provider'; import { createMockInternalAccount } from '../../accounts-controller/src/tests/mocks'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildCustomNetworkClientConfiguration, buildMockGetNetworkClientById, @@ -41,8 +43,6 @@ import { TOKEN_END_POINT_API } from './token-service'; import type { Token } from './TokenRatesController'; import { TokensController } from './TokensController'; import type { - AllowedActions, - AllowedEvents, TokensControllerMessenger, TokensControllerState, } from './TokensController'; @@ -55,10 +55,15 @@ jest.mock('uuid', () => ({ jest.mock('./Standards/ERC20Standard'); jest.mock('./Standards/NftStandards/ERC1155/ERC1155Standard'); -type UnrestrictedMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent | ApprovalStateChange ->; +type AllActions = + | MessengerActions + | MessengerActions; + +type AllEvents = + | MessengerEvents + | MessengerEvents; + +type RootMessenger = Messenger; const ContractMock = jest.mocked(Contract); const uuidV1Mock = jest.mocked(uuidV1); @@ -3483,7 +3488,7 @@ type WithControllerCallback = ({ changeNetwork: (networkControllerState: { selectedNetworkClientId: NetworkClientId; }) => void; - messenger: UnrestrictedMessenger; + messenger: RootMessenger; approvalController: ApprovalController; triggerSelectedAccountChange: (internalAccount: InternalAccount) => void; triggerAccountRemoved: (accountAddress: string) => void; @@ -3541,12 +3546,18 @@ async function withController( fn, ] = args.length === 2 ? args : [{}, args[0]]; - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); - const approvalControllerMessenger = messenger.getRestricted({ - name: 'ApprovalController', - allowedActions: [], - allowedEvents: [], + const approvalControllerMessenger = new Messenger< + 'ApprovalController', + MessengerActions, + MessengerEvents, + RootMessenger + >({ + namespace: 'ApprovalController', + parent: messenger, }); const approvalController = new ApprovalController({ messenger: approvalControllerMessenger, @@ -3554,16 +3565,25 @@ async function withController( typesExcludedFromRateLimiting: [ApprovalType.WatchAsset], }); - const restrictedMessenger = messenger.getRestricted({ - name: 'TokensController', - allowedActions: [ + const tokensControllerMessenger = new Messenger< + 'TokensController', + MessengerActions, + MessengerEvents, + RootMessenger + >({ + namespace: 'TokensController', + parent: messenger, + }); + messenger.delegate({ + messenger: tokensControllerMessenger, + actions: [ 'ApprovalController:addRequest', 'NetworkController:getNetworkClientById', 'AccountsController:getAccount', 'AccountsController:getSelectedAccount', 'AccountsController:listAccounts', ], - allowedEvents: [ + events: [ 'NetworkController:networkDidChange', 'NetworkController:stateChange', 'AccountsController:selectedEvmAccountChange', @@ -3601,7 +3621,7 @@ async function withController( // where the provider can possibly be `undefined` if `networkClientId` is // not specified. provider: new FakeProvider(), - messenger: restrictedMessenger, + messenger: tokensControllerMessenger, ...options, }); diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 190930e5995..cdd2cb9cbe4 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -8,11 +8,10 @@ import type { } from '@metamask/accounts-controller'; import type { AddApprovalRequest } from '@metamask/approval-controller'; import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import contractsMap from '@metamask/contract-metadata'; import { toChecksumHexAddress, @@ -27,6 +26,7 @@ import { } from '@metamask/controller-utils'; import type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import { abiERC721 } from '@metamask/metamask-eth-abis'; import type { NetworkClientId, @@ -152,12 +152,10 @@ export type AllowedEvents = /** * The messenger of the {@link TokensController}. */ -export type TokensControllerMessenger = RestrictedMessenger< +export type TokensControllerMessenger = Messenger< typeof controllerName, TokensControllerActions | AllowedActions, - TokensControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokensControllerEvents | AllowedEvents >; export const getDefaultTokensState = (): TokensControllerState => { @@ -218,32 +216,32 @@ export class TokensController extends BaseController< this.#abortController = new AbortController(); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:addDetectedTokens` as const, this.addDetectedTokens.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:addTokens` as const, this.addTokens.bind(this), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:selectedEvmAccountChange', this.#onSelectedAccountChange.bind(this), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:stateChange', this.#onNetworkStateChange.bind(this), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'KeyringController:accountRemoved', (accountAddress: string) => this.#handleOnAccountRemoved(accountAddress), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TokenListController:stateChange', ({ tokensChainsCache }) => { const { allTokens } = this.state; @@ -413,7 +411,7 @@ export class TokensController extends BaseController< const releaseLock = await this.#mutex.acquire(); const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; - const chainIdToUse = this.messagingSystem.call( + const chainIdToUse = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ).configuration.chainId; @@ -499,7 +497,7 @@ export class TokensController extends BaseController< const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; const importedTokensMap: { [key: string]: true } = {}; - const interactingChainId = this.messagingSystem.call( + const interactingChainId = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ).configuration.chainId; @@ -575,7 +573,7 @@ export class TokensController extends BaseController< tokenAddressesToIgnore: string[], networkClientId: NetworkClientId, ) { - const interactingChainId = this.messagingSystem.call( + const interactingChainId = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ).configuration.chainId; @@ -733,7 +731,7 @@ export class TokensController extends BaseController< tokenAddress: string, networkClientId: NetworkClientId, ) { - const chainIdToUse = this.messagingSystem.call( + const chainIdToUse = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ).configuration.chainId; @@ -792,7 +790,7 @@ export class TokensController extends BaseController< #getProvider(networkClientId?: NetworkClientId): Web3Provider { return new Web3Provider( networkClientId - ? this.messagingSystem.call( + ? this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ).provider @@ -1076,7 +1074,7 @@ export class TokensController extends BaseController< } async #requestApproval(suggestedAssetMeta: SuggestedAssetMeta) { - return this.messagingSystem.call( + return this.messenger.call( 'ApprovalController:addRequest', { id: suggestedAssetMeta.id, @@ -1098,12 +1096,12 @@ export class TokensController extends BaseController< } #getSelectedAccount() { - return this.messagingSystem.call('AccountsController:getSelectedAccount'); + return this.messenger.call('AccountsController:getSelectedAccount'); } #getSelectedAddress() { // If the address is not defined (or empty), we fallback to the currently selected account's address - const account = this.messagingSystem.call( + const account = this.messenger.call( 'AccountsController:getAccount', this.#selectedAccountId, ); From b72884fe9b59a92989d64016a9e0c3a1cf9de221 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 22:39:25 +0200 Subject: [PATCH 036/247] migrate `AssetsContractController` messenger --- packages/assets-controllers/package.json | 1 + .../src/AssetsContractController.test.ts | 69 +++++++++++++------ .../src/AssetsContractController.ts | 11 +-- yarn.lock | 1 + 4 files changed, 53 insertions(+), 29 deletions(-) diff --git a/packages/assets-controllers/package.json b/packages/assets-controllers/package.json index bb55e6ab4d7..798a03bb9f6 100644 --- a/packages/assets-controllers/package.json +++ b/packages/assets-controllers/package.json @@ -59,6 +59,7 @@ "@metamask/controller-utils": "^11.12.0", "@metamask/eth-query": "^4.0.0", "@metamask/keyring-api": "^20.1.0", + "@metamask/messenger": "^0.1.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/polling-controller": "^14.0.0", "@metamask/rpc-errors": "^7.0.2", diff --git a/packages/assets-controllers/src/AssetsContractController.test.ts b/packages/assets-controllers/src/AssetsContractController.test.ts index 5638e01c460..8c7320d8ab3 100644 --- a/packages/assets-controllers/src/AssetsContractController.test.ts +++ b/packages/assets-controllers/src/AssetsContractController.test.ts @@ -1,5 +1,4 @@ import { BigNumber } from '@ethersproject/bignumber'; -import { Messenger } from '@metamask/base-controller'; import { BUILT_IN_NETWORKS, ChainId, @@ -8,12 +7,18 @@ import { NetworkType, } from '@metamask/controller-utils'; import HttpProvider from '@metamask/ethjs-provider-http'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Provider, NetworkClientId, - NetworkControllerActions, - NetworkControllerEvents, InfuraNetworkClientConfiguration, + NetworkControllerMessenger, } from '@metamask/network-controller'; import { NetworkController, @@ -30,12 +35,24 @@ import { } from './AssetsContractController'; import { SupportedTokenDetectionNetworks } from './assetsUtil'; import { mockNetwork } from '../../../tests/mock-network'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildInfuraNetworkClientConfiguration } from '../../network-controller/tests/helpers'; +type AllAssetsContractControllerActions = + MessengerActions; + +type AllAssetsContractControllerEvents = + MessengerEvents; + +type AllNetworkControllerActions = MessengerActions; + +type AllNetworkControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllAssetsContractControllerActions | AllNetworkControllerActions, + AllAssetsContractControllerEvents | AllNetworkControllerEvents +>; + const ERC20_UNI_ADDRESS = '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984'; const ERC20_SAI_ADDRESS = '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359'; const ERC20_DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f'; @@ -80,18 +97,19 @@ async function setupAssetContractControllers({ }; let provider: Provider; - const messenger = new Messenger< - | ExtractAvailableAction - | NetworkControllerActions, - | ExtractAvailableEvent - | NetworkControllerEvents - >(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const networkController = new NetworkController({ infuraProjectId, - messenger: messenger.getRestricted({ - name: 'NetworkController', - allowedActions: [], - allowedEvents: [], + messenger: new Messenger< + 'NetworkController', + MessengerActions, + MessengerEvents, + RootMessenger + >({ + namespace: 'NetworkController', + parent: messenger, }), getRpcServiceOptions: () => ({ fetch, @@ -121,15 +139,24 @@ async function setupAssetContractControllers({ }), ); - const assetsContractMessenger = messenger.getRestricted({ - name: 'AssetsContractController', - allowedActions: [ + const assetsContractMessenger = new Messenger< + 'AssetsContractController', + MessengerActions, + MessengerEvents, + RootMessenger + >({ + namespace: 'AssetsContractController', + parent: messenger, + }); + messenger.delegate({ + messenger: assetsContractMessenger, + actions: [ 'NetworkController:getNetworkClientById', 'NetworkController:getNetworkConfigurationByNetworkClientId', 'NetworkController:getSelectedNetworkClient', 'NetworkController:getState', ], - allowedEvents: [ + events: [ 'PreferencesController:stateChange', 'NetworkController:networkDidChange', ], diff --git a/packages/assets-controllers/src/AssetsContractController.ts b/packages/assets-controllers/src/AssetsContractController.ts index 70403278730..2b903890858 100644 --- a/packages/assets-controllers/src/AssetsContractController.ts +++ b/packages/assets-controllers/src/AssetsContractController.ts @@ -2,11 +2,8 @@ import type { BigNumber } from '@ethersproject/bignumber'; import { Contract } from '@ethersproject/contracts'; import { Web3Provider } from '@ethersproject/providers'; -import type { - ActionConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; import { IPFS_DEFAULT_GATEWAY_URL } from '@metamask/controller-utils'; +import type { Messenger, ActionConstraint } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction, @@ -205,12 +202,10 @@ export type AllowedEvents = /** * The messenger of the {@link AssetsContractController}. */ -export type AssetsContractControllerMessenger = RestrictedMessenger< +export type AssetsContractControllerMessenger = Messenger< typeof name, AssetsContractControllerActions | AllowedActions, - AssetsContractControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AssetsContractControllerEvents | AllowedEvents >; export type StakedBalance = string | undefined; diff --git a/yarn.lock b/yarn.lock index e5cd8da65bd..274855637c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2588,6 +2588,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.0.0" "@metamask/keyring-internal-api": "npm:^8.1.0" "@metamask/keyring-snap-client": "npm:^7.0.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/multichain-account-service": "npm:^0.6.0" "@metamask/network-controller": "npm:^24.1.0" From 31c7ee09599751b130d985cd8b3a26d6b48aecb6 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 18:11:57 +0200 Subject: [PATCH 037/247] update changelogs --- packages/assets-controllers/CHANGELOG.md | 5 +++++ packages/gas-fee-controller/CHANGELOG.md | 2 ++ 2 files changed, 7 insertions(+) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 55d0645be09..032457253b1 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Migrate `AssetsContractController`, `NftController`, and `TokensController` to new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, the controllers accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [74.3.2] ### Changed diff --git a/packages/gas-fee-controller/CHANGELOG.md b/packages/gas-fee-controller/CHANGELOG.md index 376fe9c201b..45205d6c7a6 100644 --- a/packages/gas-fee-controller/CHANGELOG.md +++ b/packages/gas-fee-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `GasFeeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.2.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) - Bump `@metamask/controller-utils` from `^11.10.0` to `^11.12.0` ([#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303)) - Bump `@metamask/utils` from `^11.2.0` to `^11.4.2` ([#6054](https://github.com/MetaMask/core/pull/6054)) From 1051a2eab8d1e2d8e96c9f4a85791a872160f9da Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 23:30:26 +0200 Subject: [PATCH 038/247] fix `NetworkController` tests --- packages/network-controller/tests/NetworkController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/network-controller/tests/NetworkController.test.ts b/packages/network-controller/tests/NetworkController.test.ts index 48961d5cb28..3384dfac124 100644 --- a/packages/network-controller/tests/NetworkController.test.ts +++ b/packages/network-controller/tests/NetworkController.test.ts @@ -2241,7 +2241,7 @@ describe('NetworkController', () => { }, infuraProjectId, }, - async ({ controller, messenger }) => { + async ({ controller, networkControllerMessenger }) => { const fakeProvider = buildFakeProvider([ // Called during provider initialization { @@ -2265,7 +2265,7 @@ describe('NetworkController', () => { const lookupNetworkPromise = controller.lookupNetwork(); const error = new Error('oops'); jest - .spyOn(messenger, 'unsubscribe') + .spyOn(networkControllerMessenger, 'unsubscribe') .mockImplementation((eventType) => { // This is okay. // eslint-disable-next-line jest/no-conditional-in-test From eeade8f6a307ae162c55bcbdc9b523ffa10f9e1f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 23:34:58 +0200 Subject: [PATCH 039/247] update eslint thresholds --- eslint-warning-thresholds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index 44a46f64a62..8cf94d98bc7 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -82,7 +82,7 @@ }, "packages/assets-controllers/src/TokensController.test.ts": { "import-x/namespace": 1, - "import-x/order": 4, + "import-x/order": 3, "jest/no-conditional-in-test": 2 }, "packages/assets-controllers/src/TokensController.ts": { From edff4d4c9bba8883865250846eb0eeb52ea37340 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 18:20:01 +0200 Subject: [PATCH 040/247] remove `StaticIntervalPollingControllerNext` --- packages/gas-fee-controller/src/GasFeeController.ts | 4 ++-- .../src/StaticIntervalPollingController.ts | 10 +--------- packages/polling-controller/src/index.ts | 1 - 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/gas-fee-controller/src/GasFeeController.ts b/packages/gas-fee-controller/src/GasFeeController.ts index d187077ad6f..6e223f1ac5b 100644 --- a/packages/gas-fee-controller/src/GasFeeController.ts +++ b/packages/gas-fee-controller/src/GasFeeController.ts @@ -18,7 +18,7 @@ import type { NetworkState, ProviderProxy, } from '@metamask/network-controller'; -import { StaticIntervalPollingControllerNext } from '@metamask/polling-controller'; +import { StaticIntervalPollingController } from '@metamask/polling-controller'; import type { Hex } from '@metamask/utils'; import { v1 as random } from 'uuid'; @@ -262,7 +262,7 @@ type GasFeePollingInput = { /** * Controller that retrieves gas fee estimate data and polls for updated data on a set interval */ -export class GasFeeController extends StaticIntervalPollingControllerNext()< +export class GasFeeController extends StaticIntervalPollingController()< typeof name, GasFeeState, GasFeeMessenger diff --git a/packages/polling-controller/src/StaticIntervalPollingController.ts b/packages/polling-controller/src/StaticIntervalPollingController.ts index 34f130ff96f..e71f3e44417 100644 --- a/packages/polling-controller/src/StaticIntervalPollingController.ts +++ b/packages/polling-controller/src/StaticIntervalPollingController.ts @@ -1,5 +1,4 @@ -import { BaseController } from '@metamask/base-controller'; -import { BaseController as BaseControllerNext } from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import type { Json } from '@metamask/utils'; import { @@ -90,10 +89,3 @@ export const StaticIntervalPollingController = () => StaticIntervalPollingControllerMixin( BaseController, ); - -export const StaticIntervalPollingControllerNext = < - PollingInput extends Json, ->() => - StaticIntervalPollingControllerMixin( - BaseControllerNext, - ); diff --git a/packages/polling-controller/src/index.ts b/packages/polling-controller/src/index.ts index f8104b9d5c9..ba1758c443b 100644 --- a/packages/polling-controller/src/index.ts +++ b/packages/polling-controller/src/index.ts @@ -6,7 +6,6 @@ export { export { StaticIntervalPollingControllerOnly, StaticIntervalPollingController, - StaticIntervalPollingControllerNext, } from './StaticIntervalPollingController'; export type { IPollingController } from './types'; From 3ec10e109d95a301a29a10f1cf6102a2598c70c2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 18:20:30 +0200 Subject: [PATCH 041/247] migrate `bridge-controller` messenger --- packages/bridge-controller/package.json | 1 + .../src/bridge-controller.ts | 40 +++++++++---------- packages/bridge-controller/src/types.ts | 27 ++++++++----- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/packages/bridge-controller/package.json b/packages/bridge-controller/package.json index 3761fea64ca..5c41768c341 100644 --- a/packages/bridge-controller/package.json +++ b/packages/bridge-controller/package.json @@ -56,6 +56,7 @@ "@metamask/controller-utils": "^11.12.0", "@metamask/gas-fee-controller": "^24.0.0", "@metamask/keyring-api": "^20.1.0", + "@metamask/messenger": "^0.1.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/multichain-network-controller": "^0.12.0", "@metamask/polling-controller": "^14.0.0", diff --git a/packages/bridge-controller/src/bridge-controller.ts b/packages/bridge-controller/src/bridge-controller.ts index c3b145910c8..8407b7247a7 100644 --- a/packages/bridge-controller/src/bridge-controller.ts +++ b/packages/bridge-controller/src/bridge-controller.ts @@ -1,7 +1,7 @@ import type { BigNumber } from '@ethersproject/bignumber'; import { Contract } from '@ethersproject/contracts'; import { Web3Provider } from '@ethersproject/providers'; -import type { StateMetadata } from '@metamask/base-controller'; +import type { StateMetadata } from '@metamask/base-controller/next'; import type { TraceCallback } from '@metamask/controller-utils'; import { abiERC20 } from '@metamask/metamask-eth-abis'; import type { NetworkClientId } from '@metamask/network-controller'; @@ -213,31 +213,31 @@ export class BridgeController extends StaticIntervalPollingController fn?.()) as TraceCallback); // Register action handlers - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:setChainIntervalLength`, this.setChainIntervalLength.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:updateBridgeQuoteRequestParams`, this.updateBridgeQuoteRequestParams.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:resetState`, this.resetState.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:getBridgeERC20Allowance`, this.getBridgeERC20Allowance.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:trackUnifiedSwapBridgeEvent`, this.trackUnifiedSwapBridgeEvent.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:stopPollingForQuotes`, this.stopPollingForQuotes.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_CONTROLLER_NAME}:fetchQuotes`, this.fetchQuotes.bind(this), ); @@ -334,7 +334,7 @@ export class BridgeController extends StaticIntervalPollingController => { - const bridgeFeatureFlags = getBridgeFeatureFlags(this.messagingSystem); + const bridgeFeatureFlags = getBridgeFeatureFlags(this.messenger); // If featureId is specified, retrieve the quoteRequestOverrides for that featureId const quoteRequestOverrides = featureId ? bridgeFeatureFlags.quoteRequestOverrides?.[featureId] @@ -385,9 +385,9 @@ export class BridgeController extends StaticIntervalPollingController { return { - ...this.messagingSystem.call('MultichainAssetsRatesController:getState'), - ...this.messagingSystem.call('CurrencyRateController:getState'), - ...this.messagingSystem.call('TokenRatesController:getState'), + ...this.messenger.call('MultichainAssetsRatesController:getState'), + ...this.messenger.call('CurrencyRateController:getState'), + ...this.messenger.call('TokenRatesController:getState'), ...this.state, }; }; @@ -437,7 +437,7 @@ export class BridgeController extends StaticIntervalPollingController { const { state } = this; const { srcChainId } = state.quoteRequest; - const bridgeFeatureFlags = getBridgeFeatureFlags(this.messagingSystem); + const bridgeFeatureFlags = getBridgeFeatureFlags(this.messenger); const refreshRateOverride = srcChainId ? bridgeFeatureFlags.chains[formatChainIdToCaip(srcChainId)]?.refreshRate @@ -608,7 +608,7 @@ export class BridgeController extends StaticIntervalPollingController; + +export type BridgeControllerStateChangeEvent = ControllerStateChangeEvent< + typeof BRIDGE_CONTROLLER_NAME, + BridgeControllerState +>; + // Maps to BridgeController function names export type BridgeControllerActions = + | BridgeControllerGetStateAction | BridgeControllerAction | BridgeControllerAction | BridgeControllerAction @@ -336,10 +348,7 @@ export type BridgeControllerActions = | BridgeControllerAction | BridgeControllerAction; -export type BridgeControllerEvents = ControllerStateChangeEvent< - typeof BRIDGE_CONTROLLER_NAME, - BridgeControllerState ->; +export type BridgeControllerEvents = BridgeControllerStateChangeEvent; export type AllowedActions = | AccountsControllerGetSelectedMultichainAccountAction @@ -356,10 +365,8 @@ export type AllowedEvents = never; /** * The messenger for the BridgeController. */ -export type BridgeControllerMessenger = RestrictedMessenger< +export type BridgeControllerMessenger = Messenger< typeof BRIDGE_CONTROLLER_NAME, BridgeControllerActions | AllowedActions, - BridgeControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + BridgeControllerEvents | AllowedEvents >; From e795b14b4120cc3c9c5f354f1bf8601d30f2ecee Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 22:31:38 +0200 Subject: [PATCH 042/247] migrate `BridgeStatusController` messenger --- .../src/bridge-status-controller.test.ts | 98 +++++++++++-------- .../src/bridge-status-controller.ts | 63 ++++++------ .../bridge-status-controller/src/types.ts | 8 +- .../src/utils/transaction.test.ts | 18 ++-- .../src/utils/transaction.ts | 22 ++--- 5 files changed, 107 insertions(+), 102 deletions(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index f95b11fc3da..cb6a79f0d03 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -1,10 +1,7 @@ /* eslint-disable jest/no-conditional-in-test */ /* eslint-disable jest/no-restricted-matchers */ -import type { AccountsControllerActions } from '@metamask/accounts-controller'; -import { Messenger } from '@metamask/base-controller'; import type { - BridgeControllerActions, - BridgeControllerEvents, + BridgeControllerMessenger, TxData, } from '@metamask/bridge-controller'; import { @@ -16,13 +13,18 @@ import { } from '@metamask/bridge-controller'; import { ChainId } from '@metamask/bridge-controller'; import { ActionTypes, FeeType } from '@metamask/bridge-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { TransactionType, TransactionStatus, } from '@metamask/transaction-controller'; import type { - TransactionControllerActions, - TransactionControllerEvents, TransactionMeta, TransactionParams, } from '@metamask/transaction-controller'; @@ -35,10 +37,6 @@ import { DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE, MAX_ATTEMPTS, } from './constants'; -import type { - BridgeStatusControllerActions, - BridgeStatusControllerEvents, -} from './types'; import { type BridgeId, type StartPollingForBridgeTxStatusArgsSerialized, @@ -52,6 +50,22 @@ import * as transactionUtils from './utils/transaction'; import { flushPromises } from '../../../tests/helpers'; import { CHAIN_IDS } from '../../bridge-controller/src/constants/chains'; +type AllBridgeStatusControllerActions = + MessengerActions; + +type AllBridgeStatusControllerEvents = + MessengerEvents; + +type AllBridgeControllerActions = MessengerActions; + +type AllBridgeControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllBridgeStatusControllerActions | AllBridgeControllerActions, + AllBridgeStatusControllerEvents | AllBridgeControllerEvents +>; + jest.mock('uuid', () => ({ v4: () => 'test-uuid-1234', })); @@ -3292,19 +3306,16 @@ describe('BridgeStatusController', () => { }); describe('subscription handlers', () => { - let mockBridgeStatusMessenger: jest.Mocked; + let mockMessenger: RootMessenger; + let mockBridgeStatusMessenger: Messenger< + 'BridgeStatusController', + MessengerActions, + MessengerEvents, + RootMessenger + >; let mockTrackEventFn: jest.Mock; let bridgeStatusController: BridgeStatusController; - let mockMessenger: Messenger< - | BridgeStatusControllerActions - | TransactionControllerActions - | BridgeControllerActions - | AccountsControllerActions, - | BridgeStatusControllerEvents - | TransactionControllerEvents - | BridgeControllerEvents - >; let mockFetchFn: jest.Mock; const consoleFn = console.warn; let consoleFnSpy: jest.SpyInstance; @@ -3314,37 +3325,38 @@ describe('BridgeStatusController', () => { jest.clearAllMocks(); // eslint-disable-next-line no-empty-function consoleFnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); - mockMessenger = new Messenger< - | BridgeStatusControllerActions - | TransactionControllerActions - | BridgeControllerActions - | AccountsControllerActions, - | BridgeStatusControllerEvents - | TransactionControllerEvents - | BridgeControllerEvents - >(); - - jest.spyOn(mockMessenger, 'call').mockImplementation((..._args) => { - return Promise.resolve(); - }); - - mockBridgeStatusMessenger = mockMessenger.getRestricted({ - name: BRIDGE_STATUS_CONTROLLER_NAME, - allowedActions: [ + mockMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); + mockBridgeStatusMessenger = new Messenger({ + namespace: BRIDGE_STATUS_CONTROLLER_NAME, + parent: mockMessenger, + }); + mockMessenger.delegate({ + messenger: mockBridgeStatusMessenger, + actions: [ 'TransactionController:getState', 'BridgeController:trackUnifiedSwapBridgeEvent', 'AccountsController:getAccountByAddress', ], - allowedEvents: [ + events: [ 'TransactionController:transactionFailed', 'TransactionController:transactionConfirmed', ], - }) as never; + }); + + jest + .spyOn(mockBridgeStatusMessenger, 'call') + .mockImplementation((..._args) => { + return Promise.resolve(); + }); - const mockBridgeMessenger = mockMessenger.getRestricted({ - name: 'BridgeController', - allowedActions: [], - allowedEvents: [], + const mockBridgeMessenger = new Messenger< + 'BridgeController', + MessengerActions, + MessengerEvents, + RootMessenger + >({ + namespace: 'BridgeController', + parent: mockMessenger, }); mockTrackEventFn = jest.fn(); new BridgeController({ diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 2194c1dcb27..7fdc0f711eb 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -165,23 +165,23 @@ export class BridgeStatusController extends StaticIntervalPollingController fn?.()) as TraceCallback); // Register action handlers - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_STATUS_CONTROLLER_NAME}:startPollingForBridgeTxStatus`, this.startPollingForBridgeTxStatus.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_STATUS_CONTROLLER_NAME}:wipeBridgeStatus`, this.wipeBridgeStatus.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_STATUS_CONTROLLER_NAME}:resetState`, this.resetState.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_STATUS_CONTROLLER_NAME}:submitTx`, this.submitTx.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_STATUS_CONTROLLER_NAME}:restartPollingForFailedAttempts`, this.restartPollingForFailedAttempts.bind(this), ); @@ -189,7 +189,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { const { type, status, id } = transactionMeta; @@ -221,7 +221,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { const { type, id, chainId } = transactionMeta; @@ -277,10 +277,10 @@ export class BridgeStatusController extends StaticIntervalPollingController } | { signature: string }; @@ -774,12 +774,9 @@ export class BridgeStatusController extends StaticIntervalPollingController['result'], ): Promise => { const transactionHash = await hashPromise; - const finalTransactionMeta: TransactionMeta | undefined = - this.messagingSystem - .call('TransactionController:getState') - .transactions.find( - (tx: TransactionMeta) => tx.hash === transactionHash, - ); + const finalTransactionMeta: TransactionMeta | undefined = this.messenger + .call('TransactionController:getState') + .transactions.find((tx: TransactionMeta) => tx.hash === transactionHash); if (!finalTransactionMeta) { throw new Error( 'Failed to submit cross-chain swap tx: txMeta for txHash was not found', @@ -848,7 +845,7 @@ export class BridgeStatusController extends StaticIntervalPollingController => { const actionId = generateActionId().toString(); - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getAccountByAddress', trade.from, ); @@ -858,7 +855,7 @@ export class BridgeStatusController extends StaticIntervalPollingController & QuoteMetadata, ) => { const resetApproval = await getUSDTAllowanceResetTx( - this.messagingSystem, + this.messenger, quoteResponse, ); if (resetApproval) { @@ -915,7 +912,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { - const { gasFeeEstimates } = this.messagingSystem.call( + const { gasFeeEstimates } = this.messenger.call( 'GasFeeController:getState', ); const { estimates: txGasFeeEstimates } = await this.#estimateGasFeeFn({ @@ -951,11 +948,11 @@ export class BridgeStatusController extends StaticIntervalPollingController[0], - 'messagingSystem' | 'estimateGasFeeFn' + 'messenger' | 'estimateGasFeeFn' >, ) => { const transactionParams = await getAddTransactionBatchParams({ - messagingSystem: this.messagingSystem, + messenger: this.messenger, estimateGasFeeFn: this.#estimateGasFeeFn, ...args, }); @@ -976,7 +973,7 @@ export class BridgeStatusController extends StaticIntervalPollingController & QuoteMetadata, isStxEnabledOnClient: boolean, ): Promise> => { - this.messagingSystem.call('BridgeController:stopPollingForQuotes'); + this.messenger.call('BridgeController:stopPollingForQuotes'); const selectedAccount = this.#getMultichainSelectedAccount(); if (!selectedAccount) { @@ -1091,7 +1088,7 @@ export class BridgeStatusController extends StaticIntervalPollingController id === txMetaId); @@ -1222,7 +1219,7 @@ export class BridgeStatusController extends StaticIntervalPollingController; diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 16b9a44428c..6efb7760301 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1349,7 +1349,7 @@ describe('Bridge Status Controller Transaction Utils', () => { const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, isBridgeTx: true, trade: mockQuoteResponse.trade, approval: mockQuoteResponse.approval, @@ -1372,7 +1372,7 @@ describe('Bridge Status Controller Transaction Utils', () => { const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, estimateGasFeeFn: jest.fn().mockResolvedValue({}), @@ -1395,7 +1395,7 @@ describe('Bridge Status Controller Transaction Utils', () => { const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, isBridgeTx: true, trade: mockQuoteResponse.trade, resetApproval: mockQuoteResponse.resetApproval, @@ -1474,7 +1474,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1518,7 +1518,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1552,7 +1552,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1590,7 +1590,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1632,7 +1632,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1662,7 +1662,7 @@ describe('Bridge Status Controller Transaction Utils', () => { // Test with bridge transaction (not swap) findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, diff --git a/packages/bridge-status-controller/src/utils/transaction.ts b/packages/bridge-status-controller/src/utils/transaction.ts index 5e81afbf42f..05ee5fed206 100644 --- a/packages/bridge-status-controller/src/utils/transaction.ts +++ b/packages/bridge-status-controller/src/utils/transaction.ts @@ -246,7 +246,7 @@ export const toBatchTxParams = ( }; export const getAddTransactionBatchParams = async ({ - messagingSystem, + messenger, isBridgeTx, approval, resetApproval, @@ -263,7 +263,7 @@ export const getAddTransactionBatchParams = async ({ requireApproval = false, estimateGasFeeFn, }: { - messagingSystem: BridgeStatusControllerMessenger; + messenger: BridgeStatusControllerMessenger; isBridgeTx: boolean; trade: TxData; quoteResponse: Omit & QuoteMetadata; @@ -273,7 +273,7 @@ export const getAddTransactionBatchParams = async ({ requireApproval?: boolean; }) => { const isGasless = gasIncluded || gasless7702; - const selectedAccount = messagingSystem.call( + const selectedAccount = messenger.call( 'AccountsController:getAccountByAddress', trade.from, ); @@ -283,7 +283,7 @@ export const getAddTransactionBatchParams = async ({ ); } const hexChainId = formatChainIdToHex(trade.chainId); - const networkClientId = messagingSystem.call( + const networkClientId = messenger.call( 'NetworkController:findNetworkClientIdByChainId', hexChainId, ); @@ -295,7 +295,7 @@ export const getAddTransactionBatchParams = async ({ if (resetApproval) { const gasFees = await calculateGasFees( disable7702, - messagingSystem, + messenger, estimateGasFeeFn, resetApproval, networkClientId, @@ -312,7 +312,7 @@ export const getAddTransactionBatchParams = async ({ if (approval) { const gasFees = await calculateGasFees( disable7702, - messagingSystem, + messenger, estimateGasFeeFn, approval, networkClientId, @@ -328,7 +328,7 @@ export const getAddTransactionBatchParams = async ({ } const gasFees = await calculateGasFees( disable7702, - messagingSystem, + messenger, estimateGasFeeFn, trade, networkClientId, @@ -358,19 +358,17 @@ export const getAddTransactionBatchParams = async ({ }; export const findAndUpdateTransactionsInBatch = ({ - messagingSystem, + messenger, updateTransactionFn, batchId, txDataByType, }: { - messagingSystem: BridgeStatusControllerMessenger; + messenger: BridgeStatusControllerMessenger; updateTransactionFn: typeof TransactionController.prototype.updateTransaction; batchId: string; txDataByType: { [key in TransactionType]?: string }; }) => { - const txs = messagingSystem.call( - 'TransactionController:getState', - ).transactions; + const txs = messenger.call('TransactionController:getState').transactions; const txBatch: { approvalMeta?: TransactionMeta; tradeMeta?: TransactionMeta; From 19c7d6aee70ba44b8bcccf426f87ee5144b17295 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 21:58:21 +0200 Subject: [PATCH 043/247] migrate `TokenDetectionController` messenger --- .../src/TokenDetectionController.test.ts | 56 +++++++++++++---- .../src/TokenDetectionController.ts | 63 +++++++++---------- 2 files changed, 74 insertions(+), 45 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index f112e984cd2..8667f7516cb 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -1,5 +1,4 @@ import type { AddApprovalRequest } from '@metamask/approval-controller'; -import { Messenger } from '@metamask/base-controller'; import { ChainId, NetworkType, @@ -8,6 +7,13 @@ import { } from '@metamask/controller-utils'; import type { KeyringControllerState } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + MessengerActions, + MessengerEvents, + MockAnyNamespace, +} from '@metamask/messenger'; import { getDefaultNetworkControllerState, RpcEndpointType, @@ -150,23 +156,48 @@ const mockNetworkConfigurations: Record = { }, }; -type MainMessenger = Messenger< - AllowedActions | AddApprovalRequest, - AllowedEvents +type AllTokenDetectionControllerActions = + MessengerActions; + +type AllTokenDetectionControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllTokenDetectionControllerActions, + AllTokenDetectionControllerEvents >; +/** + * Builds a root messenger for testing. + * + * @returns The root messenger. + */ +function buildRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); +} + /** * Builds a messenger that `TokenDetectionController` can use to communicate with other controllers. * - * @param messenger - The main messenger. - * @returns The restricted messenger. + * @param messenger - The root messenger. + * @returns The controller messenger. */ function buildTokenDetectionControllerMessenger( - messenger: MainMessenger = new Messenger(), + messenger = buildRootMessenger(), ): TokenDetectionControllerMessenger { - return messenger.getRestricted({ - name: controllerName, - allowedActions: [ + const tokenDetectionControllerMessenger = new Messenger< + 'TokenDetectionController', + AllTokenDetectionControllerActions, + AllTokenDetectionControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: messenger, + }); + messenger.delegate({ + messenger: tokenDetectionControllerMessenger, + actions: [ 'AccountsController:getAccount', 'AccountsController:getSelectedAccount', 'KeyringController:getState', @@ -180,7 +211,7 @@ function buildTokenDetectionControllerMessenger( 'TokensController:addTokens', 'NetworkController:findNetworkClientIdByChainId', ], - allowedEvents: [ + events: [ 'AccountsController:selectedEvmAccountChange', 'KeyringController:lock', 'KeyringController:unlock', @@ -190,6 +221,7 @@ function buildTokenDetectionControllerMessenger( 'TransactionController:transactionConfirmed', ], }); + return tokenDetectionControllerMessenger; } const mockMultiChainAccountsService = () => { @@ -3625,7 +3657,7 @@ async function withController( ): Promise { const [{ ...rest }, fn] = args.length === 2 ? args : [{}, args[0]]; const { options, isKeyringUnlocked, mocks } = rest; - const messenger = new Messenger(); + const messenger = buildRootMessenger(); const mockGetAccount = jest.fn(); messenger.registerActionHandler( diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index c577af329a2..399a9e83c5a 100644 --- a/packages/assets-controllers/src/TokenDetectionController.ts +++ b/packages/assets-controllers/src/TokenDetectionController.ts @@ -4,10 +4,9 @@ import type { AccountsControllerSelectedEvmAccountChangeEvent, } from '@metamask/accounts-controller'; import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import contractMap from '@metamask/contract-metadata'; import { ASSET_TYPES, @@ -21,6 +20,7 @@ import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerFindNetworkClientIdByChainIdAction, @@ -150,12 +150,10 @@ export type AllowedEvents = | PreferencesControllerStateChangeEvent | TransactionControllerTransactionConfirmedEvent; -export type TokenDetectionControllerMessenger = RestrictedMessenger< +export type TokenDetectionControllerMessenger = Messenger< typeof controllerName, TokenDetectionControllerActions | AllowedActions, - TokenDetectionControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokenDetectionControllerEvents | AllowedEvents >; /** The input to start polling for the {@link TokenDetectionController} */ @@ -269,7 +267,7 @@ export class TokenDetectionController extends StaticIntervalPollingController { + this.messenger.subscribe('KeyringController:unlock', async () => { this.#isUnlocked = true; await this.#restartTokenDetection(); }); - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.#isUnlocked = false; this.#stopPolling(); }); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TokenListController:stateChange', // TODO: Either fix this lint violation or explain why it's necessary to ignore. // eslint-disable-next-line @typescript-eslint/no-misused-promises @@ -388,7 +385,7 @@ export class TokenDetectionController extends StaticIntervalPollingController { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); @@ -429,7 +426,7 @@ export class TokenDetectionController extends StaticIntervalPollingController { await this.detectTokens({ @@ -526,10 +523,10 @@ export class TokenDetectionController extends StaticIntervalPollingController Date: Wed, 3 Sep 2025 21:52:53 +0200 Subject: [PATCH 044/247] migrate `DeFiPositionsController` messenger --- .../DeFiPositionsController.test.ts | 48 +++++++++++++------ .../DeFiPositionsController.ts | 22 ++++----- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts index 937d4626697..40378e76dda 100644 --- a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts +++ b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts @@ -1,4 +1,11 @@ import { BtcAccountType } from '@metamask/keyring-api'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import * as calculateDefiMetrics from './calculate-defi-metrics'; import type { DeFiPositionsControllerMessenger } from './DeFiPositionsController'; @@ -10,11 +17,6 @@ import * as fetchPositions from './fetch-positions'; import * as groupDeFiPositions from './group-defi-positions'; import { flushPromises } from '../../../../tests/helpers'; import { createMockInternalAccount } from '../../../accounts-controller/src/tests/mocks'; -import { Messenger } from '../../../base-controller/src/Messenger'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; import type { InternalAccount, TransactionMeta, @@ -35,9 +37,16 @@ const OWNER_ACCOUNTS = [ }), ]; -type MainMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent +type AllDefiPositionsControllerActions = + MessengerActions; + +type AllDefiPositionsControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllDefiPositionsControllerActions, + AllDefiPositionsControllerEvents >; /** @@ -64,7 +73,9 @@ function setupController({ mockCalculateDefiMetrics?: jest.Mock; mockTrackEvent?: jest.Mock; } = {}) { - const messenger: MainMessenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const mockListAccounts = jest.fn().mockReturnValue(OWNER_ACCOUNTS); messenger.registerActionHandler( @@ -72,10 +83,19 @@ function setupController({ mockListAccounts, ); - const restrictedMessenger = messenger.getRestricted({ - name: 'DeFiPositionsController', - allowedActions: ['AccountsController:listAccounts'], - allowedEvents: [ + const defiPositionControllerMessenger = new Messenger< + 'DeFiPositionsController', + AllDefiPositionsControllerActions, + AllDefiPositionsControllerEvents, + RootMessenger + >({ + namespace: 'DeFiPositionsController', + parent: messenger, + }); + messenger.delegate({ + messenger: defiPositionControllerMessenger, + actions: ['AccountsController:listAccounts'], + events: [ 'KeyringController:unlock', 'KeyringController:lock', 'TransactionController:transactionConfirmed', @@ -104,7 +124,7 @@ function setupController({ groupDeFiPositionsSpy.mockImplementation(mockGroupDeFiPositions); const controller = new DeFiPositionsController({ - messenger: restrictedMessenger, + messenger: defiPositionControllerMessenger, isEnabled, trackEvent: mockTrackEvent, }); diff --git a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts index 0d31dabd664..612c86fd582 100644 --- a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts +++ b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts @@ -5,11 +5,11 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, StateMetadata, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import type { KeyringControllerUnlockEvent } from '@metamask/keyring-controller'; import type { KeyringControllerLockEvent } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import { StaticIntervalPollingController } from '@metamask/polling-controller'; import type { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller'; import type { Hex } from '@metamask/utils'; @@ -119,12 +119,10 @@ export type AllowedEvents = /** * The messenger of the {@link DeFiPositionsController}. */ -export type DeFiPositionsControllerMessenger = RestrictedMessenger< +export type DeFiPositionsControllerMessenger = Messenger< typeof controllerName, DeFiPositionsControllerActions | AllowedActions, - DeFiPositionsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + DeFiPositionsControllerEvents | AllowedEvents >; /** @@ -172,15 +170,15 @@ export class DeFiPositionsController extends StaticIntervalPollingController()< this.#fetchPositions = buildPositionFetcher(); this.#isEnabled = isEnabled; - this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.messenger.subscribe('KeyringController:unlock', () => { this.startPolling(null); }); - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.stopAllPolling(); }); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:transactionConfirmed', async (transactionMeta) => { if (!this.#isEnabled()) { @@ -191,7 +189,7 @@ export class DeFiPositionsController extends StaticIntervalPollingController()< }, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountAdded', async (account) => { if (!this.#isEnabled() || !account.type.startsWith('eip155:')) { @@ -210,9 +208,7 @@ export class DeFiPositionsController extends StaticIntervalPollingController()< return; } - const accounts = this.messagingSystem.call( - 'AccountsController:listAccounts', - ); + const accounts = this.messenger.call('AccountsController:listAccounts'); const initialResult: { accountAddress: string; From ae155459be41defec6bd2a9542ea47033798b729 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 21:53:06 +0200 Subject: [PATCH 045/247] migrate `MultichainAssetsRatesController` messenger --- .../MultichainAssetsRatesController.test.ts | 91 ++++++++++++++----- .../MultichainAssetsRatesController.ts | 36 +++----- 2 files changed, 82 insertions(+), 45 deletions(-) diff --git a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts index 135ce11da97..b22ccb79e5c 100644 --- a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts +++ b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts @@ -1,21 +1,40 @@ -import { Messenger } from '@metamask/base-controller'; import { SolScope } from '@metamask/keyring-api'; import { SolMethod } from '@metamask/keyring-api'; import { SolAccountType } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { KeyringClient } from '@metamask/keyring-snap-client'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { OnAssetHistoricalPriceResponse } from '@metamask/snaps-sdk'; import { useFakeTimers } from 'sinon'; import { v4 as uuidv4 } from 'uuid'; import { MultichainAssetsRatesController } from '.'; import { + type MultichainAssetsRatesControllerMessenger, type AllowedActions, type AllowedEvents, } from './MultichainAssetsRatesController'; import { advanceTime } from '../../../../tests/helpers'; +type AllMultichainAssetsRateControllerActions = + MessengerActions; + +type AllMultichainAssetsRateControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllMultichainAssetsRateControllerActions, + AllMultichainAssetsRateControllerEvents +>; + // A fake non‑EVM account (with Snap metadata) that meets the controller’s criteria. const fakeNonEvmAccount: InternalAccount = { id: 'account1', @@ -118,7 +137,9 @@ const setupController = ({ >; accountsAssets?: InternalAccount[]; } = {}) => { - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); messenger.registerActionHandler( 'MultichainAssetsController:getState', @@ -155,16 +176,25 @@ const setupController = ({ currentCurrency: 'USD', })); - const multichainAssetsRatesControllerMessenger = messenger.getRestricted({ - name: 'MultichainAssetsRatesController', - allowedActions: [ + const multichainAssetsRatesControllerMessenger: Messenger< + 'MultichainAssetsRatesController', + AllMultichainAssetsRateControllerActions, + AllMultichainAssetsRateControllerEvents, + RootMessenger + > = new Messenger({ + namespace: 'MultichainAssetsRatesController', + parent: messenger, + }); + messenger.delegate({ + messenger: multichainAssetsRatesControllerMessenger, + actions: [ 'AccountsController:listMultichainAccounts', 'SnapController:handleRequest', 'CurrencyRateController:getState', 'MultichainAssetsController:getState', 'AccountsController:getSelectedMultichainAccount', ], - allowedEvents: [ + events: [ 'AccountsController:accountAdded', 'KeyringController:lock', 'KeyringController:unlock', @@ -913,7 +943,9 @@ describe('MultichainAssetsRatesController', () => { }; // Set up controller with custom accounts and assets configuration - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); // Mock MultichainAssetsController state with one account having no assets messenger.registerActionHandler( @@ -970,24 +1002,35 @@ describe('MultichainAssetsRatesController', () => { snapHandler, ); + const multichainAssetsRatesControllerMessenger = new Messenger< + 'MultichainAssetsRatesController', + AllMultichainAssetsRateControllerActions, + AllMultichainAssetsRateControllerEvents, + RootMessenger + >({ + namespace: 'MultichainAssetsRatesController', + parent: messenger, + }); + messenger.delegate({ + messenger: multichainAssetsRatesControllerMessenger, + actions: [ + 'MultichainAssetsController:getState', + 'AccountsController:listMultichainAccounts', + 'AccountsController:getSelectedMultichainAccount', + 'CurrencyRateController:getState', + 'SnapController:handleRequest', + ], + events: [ + 'KeyringController:lock', + 'KeyringController:unlock', + 'AccountsController:accountAdded', + 'CurrencyRateController:stateChange', + 'MultichainAssetsController:accountAssetListUpdated', + ], + }); + const controller = new MultichainAssetsRatesController({ - messenger: messenger.getRestricted({ - name: 'MultichainAssetsRatesController', - allowedActions: [ - 'MultichainAssetsController:getState', - 'AccountsController:listMultichainAccounts', - 'AccountsController:getSelectedMultichainAccount', - 'CurrencyRateController:getState', - 'SnapController:handleRequest', - ], - allowedEvents: [ - 'KeyringController:lock', - 'KeyringController:unlock', - 'AccountsController:accountAdded', - 'CurrencyRateController:stateChange', - 'MultichainAssetsController:accountAssetListUpdated', - ], - }), + messenger: multichainAssetsRatesControllerMessenger, }); await controller.updateAssetsRates(); diff --git a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts index e0177b32b16..d2e2217566e 100644 --- a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts +++ b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts @@ -4,16 +4,16 @@ import type { AccountsControllerGetSelectedMultichainAccountAction, } from '@metamask/accounts-controller'; import type { - RestrictedMessenger, ControllerStateChangeEvent, ControllerGetStateAction, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { type CaipAssetType, isEvmAccountType } from '@metamask/keyring-api'; import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import { StaticIntervalPollingController } from '@metamask/polling-controller'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import type { @@ -144,12 +144,10 @@ export type AllowedEvents = /** * Messenger type for the MultichainAssetsRatesController. */ -export type MultichainAssetsRatesControllerMessenger = RestrictedMessenger< +export type MultichainAssetsRatesControllerMessenger = Messenger< typeof controllerName, MultichainAssetsRatesControllerActions | AllowedActions, - MultichainAssetsRatesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + MultichainAssetsRatesControllerEvents | AllowedEvents >; /** @@ -195,7 +193,7 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro * @param options - Constructor options. * @param options.interval - The polling interval in milliseconds. * @param options.state - The initial state. - * @param options.messenger - A reference to the messaging system. + * @param options.messenger - A reference to the messenger. */ constructor({ interval = 18000, @@ -219,22 +217,22 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro this.setIntervalLength(interval); // Subscribe to keyring lock/unlock events. - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.#isUnlocked = false; }); - this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.messenger.subscribe('KeyringController:unlock', () => { this.#isUnlocked = true; }); - ({ accountsAssets: this.#accountsAssets } = this.messagingSystem.call( + ({ accountsAssets: this.#accountsAssets } = this.messenger.call( 'MultichainAssetsController:getState', )); - ({ currentCurrency: this.#currentCurrency } = this.messagingSystem.call( + ({ currentCurrency: this.#currentCurrency } = this.messenger.call( 'CurrencyRateController:getState', )); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'CurrencyRateController:stateChange', async (currentCurrency: string) => { this.#currentCurrency = currentCurrency; @@ -244,7 +242,7 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro currencyRateControllerState.currentCurrency, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'MultichainAssetsController:accountAssetListUpdated', async ({ assets }) => { const newAccountAssets = Object.entries(assets).map( @@ -295,9 +293,7 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro * @returns An array of internal accounts. */ #listMultichainAccounts(): InternalAccount[] { - return this.messagingSystem.call( - 'AccountsController:listMultichainAccounts', - ); + return this.messenger.call('AccountsController:listMultichainAccounts'); } /** @@ -425,11 +421,9 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro const selectedAccount = account ?? - this.messagingSystem.call( - 'AccountsController:getSelectedMultichainAccount', - ); + this.messenger.call('AccountsController:getSelectedMultichainAccount'); try { - const historicalPricesResponse = await this.messagingSystem.call( + const historicalPricesResponse = await this.messenger.call( 'SnapController:handleRequest', { snapId: selectedAccount?.metadata.snap?.id as SnapId, @@ -660,7 +654,7 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro | undefined > { try { - return (await this.messagingSystem.call('SnapController:handleRequest', { + return (await this.messenger.call('SnapController:handleRequest', { snapId, origin: 'metamask', handler, From c70832cf12fb4a356fa4bf50eceb702b9e94ac24 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 21:53:20 +0200 Subject: [PATCH 046/247] migrate `AccountTrackerController` messenger --- .../src/AccountTrackerController.test.ts | 54 ++++++++++++------- .../src/AccountTrackerController.ts | 47 ++++++++-------- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/packages/assets-controllers/src/AccountTrackerController.test.ts b/packages/assets-controllers/src/AccountTrackerController.test.ts index 0feb16c36de..befaedbae97 100644 --- a/packages/assets-controllers/src/AccountTrackerController.test.ts +++ b/packages/assets-controllers/src/AccountTrackerController.test.ts @@ -1,6 +1,12 @@ -import { Messenger } from '@metamask/base-controller'; import { query, toChecksumHexAddress } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { type NetworkClientId, type NetworkClientConfiguration, @@ -10,25 +16,29 @@ import { getDefaultPreferencesState } from '@metamask/preferences-controller'; import BN from 'bn.js'; import { useFakeTimers, type SinonFakeTimers } from 'sinon'; -import type { - AccountTrackerControllerMessenger, - AllowedActions, - AllowedEvents, -} from './AccountTrackerController'; +import type { AccountTrackerControllerMessenger } from './AccountTrackerController'; import { AccountTrackerController } from './AccountTrackerController'; import { getTokenBalancesForMultipleAddresses } from './multicall'; import { FakeProvider } from '../../../tests/fake-provider'; import { advanceTime } from '../../../tests/helpers'; import { createMockInternalAccount } from '../../accounts-controller/src/tests/mocks'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildCustomNetworkClientConfiguration, buildMockGetNetworkClientById, } from '../../network-controller/tests/helpers'; +type AllAccountTrackerControllerActions = + MessengerActions; + +type AllAccountTrackerControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllAccountTrackerControllerActions, + AllAccountTrackerControllerEvents +>; + jest.mock('@metamask/controller-utils', () => { return { ...jest.requireActual('@metamask/controller-utils'), @@ -1209,10 +1219,9 @@ async function withController( testFunction, ] = args.length === 2 ? args : [{}, args[0]]; - const messenger = new Messenger< - ExtractAvailableAction | AllowedActions, - ExtractAvailableEvent | AllowedEvents - >(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const mockGetSelectedAccount = jest.fn().mockReturnValue(selectedAccount); messenger.registerActionHandler( @@ -1318,16 +1327,25 @@ async function withController( mockNetworkState, ); - const accountTrackerMessenger = messenger.getRestricted({ - name: 'AccountTrackerController', - allowedActions: [ + const accountTrackerMessenger = new Messenger< + 'AccountTrackerController', + AllAccountTrackerControllerActions, + AllAccountTrackerControllerEvents, + RootMessenger + >({ + namespace: 'AccountTrackerController', + parent: messenger, + }); + messenger.delegate({ + messenger: accountTrackerMessenger, + actions: [ 'NetworkController:getNetworkClientById', 'NetworkController:getState', 'PreferencesController:getState', 'AccountsController:getSelectedAccount', 'AccountsController:listAccounts', ], - allowedEvents: ['AccountsController:selectedEvmAccountChange'], + events: ['AccountsController:selectedEvmAccountChange'], }); const triggerSelectedAccountChange = (account: InternalAccount) => { diff --git a/packages/assets-controllers/src/AccountTrackerController.ts b/packages/assets-controllers/src/AccountTrackerController.ts index 907f99b6098..294f8e5ad4f 100644 --- a/packages/assets-controllers/src/AccountTrackerController.ts +++ b/packages/assets-controllers/src/AccountTrackerController.ts @@ -8,14 +8,14 @@ import type { import type { ControllerStateChangeEvent, ControllerGetStateAction, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { query, safelyExecuteWithTimeout, toChecksumHexAddress, } from '@metamask/controller-utils'; import EthQuery from '@metamask/eth-query'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClient, NetworkClientId, @@ -194,12 +194,10 @@ export type AllowedEvents = /** * The messenger of the {@link AccountTrackerController}. */ -export type AccountTrackerControllerMessenger = RestrictedMessenger< +export type AccountTrackerControllerMessenger = Messenger< typeof controllerName, AccountTrackerControllerActions | AllowedActions, - AccountTrackerControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AccountTrackerControllerEvents | AllowedEvents >; /** The input to start polling for the {@link AccountTrackerController} */ @@ -229,7 +227,7 @@ export class AccountTrackerController extends StaticIntervalPollingController { if (newAddress !== prevAddress) { @@ -307,12 +305,12 @@ export class AccountTrackerController extends StaticIntervalPollingController toChecksumHexAddress(internalAccount.address), @@ -366,12 +364,12 @@ export class AccountTrackerController extends StaticIntervalPollingController { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); const cfg = networkConfigurationsByChainId[chainId]; const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex]; - const client = this.messagingSystem.call( + const client = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -379,12 +377,12 @@ export class AccountTrackerController extends StaticIntervalPollingController { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); const cfg = networkConfigurationsByChainId[chainId]; const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex]; - return this.messagingSystem.call( + return this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -400,13 +398,12 @@ export class AccountTrackerController extends StaticIntervalPollingController Date: Wed, 3 Sep 2025 21:53:31 +0200 Subject: [PATCH 047/247] migrate `CurrencyRateController` messenger --- .../src/CurrencyRateController.test.ts | 102 ++++++++++-------- .../src/CurrencyRateController.ts | 12 +-- 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/packages/assets-controllers/src/CurrencyRateController.test.ts b/packages/assets-controllers/src/CurrencyRateController.test.ts index c927e9ca400..a713f387f6f 100644 --- a/packages/assets-controllers/src/CurrencyRateController.test.ts +++ b/packages/assets-controllers/src/CurrencyRateController.test.ts @@ -1,32 +1,43 @@ -import { Messenger } from '@metamask/base-controller'; import { ChainId, NetworkType, NetworksTicker, } from '@metamask/controller-utils'; -import type { NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import nock from 'nock'; import { useFakeTimers } from 'sinon'; import { advanceTime } from '../../../tests/helpers'; -import type { - CurrencyRateStateChange, - GetCurrencyRateState, -} from './CurrencyRateController'; +import type { CurrencyRateMessenger } from './CurrencyRateController'; import { CurrencyRateController } from './CurrencyRateController'; -const name = 'CurrencyRateController' as const; +const namespace = 'CurrencyRateController' as const; + +type AllCurrencyRateControllerActions = MessengerActions; + +type AllCurrencyRateControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllCurrencyRateControllerActions, + AllCurrencyRateControllerEvents +>; /** - * Constructs a restricted messenger. + * Constructs a messenger for CurrencyRateController. * - * @returns A restricted messenger. + * @returns A controller messenger. */ -function getRestrictedMessenger() { - const messenger = new Messenger< - GetCurrencyRateState | NetworkControllerGetNetworkClientByIdAction, - CurrencyRateStateChange - >(); +function getCurrencyRateControllerMessenger(): CurrencyRateMessenger { + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); messenger.registerActionHandler( 'NetworkController:getNetworkClientById', jest.fn().mockImplementation((networkClientId) => { @@ -52,14 +63,19 @@ function getRestrictedMessenger() { } }), ); - return messenger.getRestricted< - typeof name, - NetworkControllerGetNetworkClientByIdAction['type'] + const currencyRateControllerMessenger = new Messenger< + typeof namespace, + AllCurrencyRateControllerActions, + AllCurrencyRateControllerEvents, + RootMessenger >({ - name, - allowedActions: ['NetworkController:getNetworkClientById'], - allowedEvents: [], + namespace, + }); + messenger.delegate({ + messenger: currencyRateControllerMessenger, + actions: ['NetworkController:getNetworkClientById'], }); + return currencyRateControllerMessenger; } const getStubbedDate = () => { @@ -77,7 +93,7 @@ describe('CurrencyRateController', () => { }); it('should set default state', () => { - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger }); expect(controller.state).toStrictEqual({ @@ -95,7 +111,7 @@ describe('CurrencyRateController', () => { }); it('should initialize with initial state', () => { - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const existingState = { currentCurrency: 'rep' }; const controller = new CurrencyRateController({ messenger, @@ -118,7 +134,7 @@ describe('CurrencyRateController', () => { it('should not poll before being started', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 100, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -150,7 +166,7 @@ describe('CurrencyRateController', () => { usd: 22, }, }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 100, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -188,7 +204,7 @@ describe('CurrencyRateController', () => { it('should not poll after being stopped', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 100, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -214,7 +230,7 @@ describe('CurrencyRateController', () => { it('should poll correctly after being started, stopped, and started again', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 100, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -245,7 +261,7 @@ describe('CurrencyRateController', () => { const fetchMultiExchangeRateStub = jest .fn() .mockResolvedValue({ eth: { [currentCurrency]: 10, usd: 111 } }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 10, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -297,7 +313,7 @@ describe('CurrencyRateController', () => { }, }; }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ fetchMultiExchangeRate: fetchMultiExchangeRateStub, messenger, @@ -337,7 +353,7 @@ describe('CurrencyRateController', () => { eth: { [currentCurrency]: 10, usd: 11 }, btc: { [currentCurrency]: 10, usd: 11 }, }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 10, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -394,7 +410,7 @@ describe('CurrencyRateController', () => { it('should add usd rate to state when includeUsdRate is configured true', async () => { const fetchMultiExchangeRateStub = jest.fn().mockResolvedValue({}); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ includeUsdRate: true, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -418,7 +434,7 @@ describe('CurrencyRateController', () => { .get('/data/pricemulti?fsyms=ETH&tsyms=xyz') .reply(200, { ETH: { XYZ: 2000.42 } }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -450,7 +466,7 @@ describe('CurrencyRateController', () => { }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -480,7 +496,7 @@ describe('CurrencyRateController', () => { }, }, }; - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state }); // Error should still be thrown @@ -505,7 +521,7 @@ describe('CurrencyRateController', () => { POL: { XYZ: 0.3 }, }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -548,7 +564,7 @@ describe('CurrencyRateController', () => { }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -576,7 +592,7 @@ describe('CurrencyRateController', () => { describe('useExternalServices', () => { it('should not fetch exchange rates when useExternalServices is false', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => false, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -600,7 +616,7 @@ describe('CurrencyRateController', () => { it('should not poll when useExternalServices is false', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => false, interval: 100, @@ -623,7 +639,7 @@ describe('CurrencyRateController', () => { it('should not fetch exchange rates when useExternalServices is false even with multiple currencies', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => false, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -647,7 +663,7 @@ describe('CurrencyRateController', () => { it('should not fetch exchange rates when useExternalServices is false even with testnet currencies', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => false, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -671,7 +687,7 @@ describe('CurrencyRateController', () => { it('should not fetch exchange rates when useExternalServices is false even with includeUsdRate true', async () => { const fetchMultiExchangeRateStub = jest.fn(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => false, includeUsdRate: true, @@ -699,7 +715,7 @@ describe('CurrencyRateController', () => { const fetchMultiExchangeRateStub = jest .fn() .mockResolvedValue({ eth: { usd: 2000, eur: 1800 } }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => true, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -731,7 +747,7 @@ describe('CurrencyRateController', () => { const fetchMultiExchangeRateStub = jest .fn() .mockResolvedValue({ eth: { usd: 2000, gbp: 1600 } }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ fetchMultiExchangeRate: fetchMultiExchangeRateStub, messenger, @@ -761,7 +777,7 @@ describe('CurrencyRateController', () => { const fetchMultiExchangeRateStub = jest .fn() .mockRejectedValue(new Error('API Error')); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ useExternalServices: () => false, fetchMultiExchangeRate: fetchMultiExchangeRateStub, diff --git a/packages/assets-controllers/src/CurrencyRateController.ts b/packages/assets-controllers/src/CurrencyRateController.ts index ccff37886a2..12811f5b43e 100644 --- a/packages/assets-controllers/src/CurrencyRateController.ts +++ b/packages/assets-controllers/src/CurrencyRateController.ts @@ -1,12 +1,12 @@ import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { TESTNET_TICKER_SYMBOLS, FALL_BACK_VS_CURRENCY, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller'; import { StaticIntervalPollingController } from '@metamask/polling-controller'; import { Mutex } from 'async-mutex'; @@ -51,12 +51,10 @@ export type CurrencyRateControllerActions = GetCurrencyRateState; type AllowedActions = NetworkControllerGetNetworkClientByIdAction; -type CurrencyRateMessenger = RestrictedMessenger< +export type CurrencyRateMessenger = Messenger< typeof name, CurrencyRateControllerActions | AllowedActions, - CurrencyRateControllerEvents, - AllowedActions['type'], - never + CurrencyRateControllerEvents >; const metadata = { @@ -103,7 +101,7 @@ export class CurrencyRateController extends StaticIntervalPollingController Date: Wed, 3 Sep 2025 21:53:41 +0200 Subject: [PATCH 048/247] migrate `TokenBalancesController` messenger --- .../src/TokenBalancesController.test.ts | 57 ++++++++++++++----- .../src/TokenBalancesController.ts | 36 ++++++------ 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 637eda6a0a0..bc43e93f4b8 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -1,6 +1,12 @@ -import { Messenger } from '@metamask/base-controller'; import { toHex } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { NetworkState } from '@metamask/network-controller'; import type { PreferencesState } from '@metamask/preferences-controller'; import { CHAIN_IDS } from '@metamask/transaction-controller'; @@ -13,6 +19,7 @@ import type { AllowedEvents, TokenBalancesControllerActions, TokenBalancesControllerEvents, + TokenBalancesControllerMessenger, TokenBalancesControllerState, } from './TokenBalancesController'; import { TokenBalancesController } from './TokenBalancesController'; @@ -21,6 +28,18 @@ import { advanceTime } from '../../../tests/helpers'; import { createMockInternalAccount } from '../../accounts-controller/src/tests/mocks'; import type { RpcEndpoint } from '../../network-controller/src/NetworkController'; +type AllTokenBalancesControllerActions = + MessengerActions; + +type AllTokenBalancesControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllTokenBalancesControllerActions, + AllTokenBalancesControllerEvents +>; + // Mock safelyExecuteWithTimeout jest.mock('@metamask/controller-utils', () => ({ ...jest.requireActual('@metamask/controller-utils'), @@ -46,14 +65,22 @@ const setupController = ({ tokens?: Partial; listAccounts?: InternalAccount[]; } = {}) => { - const messenger = new Messenger< - TokenBalancesControllerActions | AllowedActions, - TokenBalancesControllerEvents | AllowedEvents - >(); - - const tokenBalancesMessenger = messenger.getRestricted({ - name: 'TokenBalancesController', - allowedActions: [ + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); + + const tokenBalancesControllerMessenger = new Messenger< + 'TokenBalancesController', + AllTokenBalancesControllerActions, + AllTokenBalancesControllerEvents, + RootMessenger + >({ + namespace: 'TokenBalancesController', + parent: messenger, + }); + messenger.delegate({ + messenger: tokenBalancesControllerMessenger, + actions: [ 'NetworkController:getState', 'NetworkController:getNetworkClientById', 'PreferencesController:getState', @@ -63,7 +90,7 @@ const setupController = ({ 'AccountTrackerController:updateNativeBalances', 'AccountTrackerController:updateStakedBalances', ], - allowedEvents: [ + events: [ 'NetworkController:stateChange', 'PreferencesController:stateChange', 'TokensController:stateChange', @@ -131,7 +158,7 @@ const setupController = ({ }), ); const controller = new TokenBalancesController({ - messenger: tokenBalancesMessenger, + messenger: tokenBalancesControllerMessenger, ...config, }); const updateSpy = jest.spyOn(controller, 'update' as never); @@ -140,6 +167,7 @@ const setupController = ({ controller, updateSpy, messenger, + tokenBalancesControllerMessenger, }; }; @@ -1134,7 +1162,7 @@ describe('TokenBalancesController', () => { const accountAddress = '0x1111111111111111111111111111111111111111'; const chainId = '0x1'; - const { controller, messenger } = setupController({ + const { controller, tokenBalancesControllerMessenger } = setupController({ config: { useAccountsAPI: false, allowExternalServices: () => true }, tokens: { allTokens: { @@ -1150,7 +1178,10 @@ describe('TokenBalancesController', () => { }); // Set up spy for event publishing - const publishSpy = jest.spyOn(messenger, 'publish'); + const publishSpy = jest.spyOn( + tokenBalancesControllerMessenger, + 'publish', + ); jest .spyOn(multicall, 'getTokenBalancesForMultipleAddresses') diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 0555a044451..87577b9e8a1 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -6,8 +6,7 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { BNToHex, isValidHexAddress, @@ -15,6 +14,7 @@ import { toHex, } from '@metamask/controller-utils'; import type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, @@ -104,12 +104,10 @@ export type AllowedEvents = | NetworkControllerStateChangeEvent | KeyringControllerAccountRemovedEvent; -export type TokenBalancesControllerMessenger = RestrictedMessenger< +export type TokenBalancesControllerMessenger = Messenger< typeof CONTROLLER, TokenBalancesControllerActions | AllowedActions, - TokenBalancesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokenBalancesControllerEvents | AllowedEvents >; export type TokenBalancesControllerOptions = { @@ -186,13 +184,13 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ this.setIntervalLength(interval); // initial token state & subscriptions - const { allTokens, allDetectedTokens } = this.messagingSystem.call( + const { allTokens, allDetectedTokens } = this.messenger.call( 'TokensController:getState', ); this.#allTokens = allTokens; this.#detectedTokens = allDetectedTokens; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TokensController:stateChange', (tokensState: TokensControllerState) => { this.#onTokensChanged(tokensState).catch((error) => { @@ -200,11 +198,11 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ }); }, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:stateChange', this.#onNetworkChanged, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'KeyringController:accountRemoved', this.#onAccountRemoved, ); @@ -220,12 +218,12 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ } readonly #getProvider = (chainId: ChainIdHex): Web3Provider => { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); const cfg = networkConfigurationsByChainId[chainId]; const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex]; - const client = this.messagingSystem.call( + const client = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -233,12 +231,12 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ }; readonly #getNetworkClient = (chainId: ChainIdHex) => { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); const cfg = networkConfigurationsByChainId[chainId]; const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex]; - return this.messagingSystem.call( + return this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -254,12 +252,10 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ return; } - const { address: selected } = this.messagingSystem.call( + const { address: selected } = this.messenger.call( 'AccountsController:getSelectedAccount', ); - const allAccounts = this.messagingSystem.call( - 'AccountsController:listAccounts', - ); + const allAccounts = this.messenger.call('AccountsController:listAccounts'); const aggregated: ProcessedBalance[] = []; let remainingChains = [...targetChains]; @@ -365,7 +361,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ balance: balance.value ? BNToHex(balance.value) : '0x0', })); - this.messagingSystem.call( + this.messenger.call( 'AccountTrackerController:updateNativeBalances', balanceUpdates, ); @@ -392,7 +388,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ stakedBalance: balance.value ? toHex(balance.value) : '0x0', })); - this.messagingSystem.call( + this.messenger.call( 'AccountTrackerController:updateStakedBalances', stakedBalanceUpdates, ); From 20420e577423c716b12c5fc8f7aada0718e51621 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 21:56:16 +0200 Subject: [PATCH 049/247] migrate `TokenRatesController` messenger --- .../src/TokenRatesController.test.ts | 52 +++++++++++++------ .../src/TokenRatesController.ts | 20 ++++--- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/packages/assets-controllers/src/TokenRatesController.test.ts b/packages/assets-controllers/src/TokenRatesController.test.ts index 4ae02821024..4d1639ddbcf 100644 --- a/packages/assets-controllers/src/TokenRatesController.test.ts +++ b/packages/assets-controllers/src/TokenRatesController.test.ts @@ -1,5 +1,3 @@ -import type { AddApprovalRequest } from '@metamask/approval-controller'; -import { Messenger } from '@metamask/base-controller'; import { ChainId, InfuraNetworkType, @@ -8,6 +6,13 @@ import { toHex, } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { NetworkClientConfiguration, NetworkClientId, @@ -36,8 +41,6 @@ import type { } from './token-prices-service/abstract-token-prices-service'; import { controllerName, TokenRatesController } from './TokenRatesController'; import type { - AllowedActions, - AllowedEvents, Token, TokenRatesControllerMessenger, TokenRatesControllerState, @@ -51,35 +54,52 @@ const defaultSelectedAccount = createMockInternalAccount({ }); const mockTokenAddress = '0x0000000000000000000000000000000000000010'; -type MainMessenger = Messenger< - AllowedActions | AddApprovalRequest, - AllowedEvents +type AllTokenRatesControllerActions = + MessengerActions; + +type AllTokenRatesControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllTokenRatesControllerActions, + AllTokenRatesControllerEvents >; /** * Builds a messenger that `TokenRatesController` can use to communicate with other controllers. * - * @param messenger - The main messenger. - * @returns The restricted messenger. + * @param messenger - The root messenger. + * @returns The controller messenger. */ function buildTokenRatesControllerMessenger( - messenger: MainMessenger = new Messenger(), + messenger: RootMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }), ): TokenRatesControllerMessenger { - return messenger.getRestricted({ - name: controllerName, - allowedActions: [ + const tokenRatesControllerMessenger = new Messenger< + 'TokenRatesController', + AllTokenRatesControllerActions, + AllTokenRatesControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: messenger, + }); + messenger.delegate({ + messenger: tokenRatesControllerMessenger, + actions: [ 'TokensController:getState', 'NetworkController:getNetworkClientById', 'NetworkController:getState', 'AccountsController:getAccount', 'AccountsController:getSelectedAccount', ], - allowedEvents: [ + events: [ 'TokensController:stateChange', 'NetworkController:stateChange', 'AccountsController:selectedEvmAccountChange', ], }); + return tokenRatesControllerMessenger; } describe('TokenRatesController', () => { @@ -2735,7 +2755,9 @@ async function withController( mockTokensControllerState, mockNetworkState, } = rest; - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const mockTokensState = jest.fn(); messenger.registerActionHandler( diff --git a/packages/assets-controllers/src/TokenRatesController.ts b/packages/assets-controllers/src/TokenRatesController.ts index e6785ab97c7..d66f734d190 100644 --- a/packages/assets-controllers/src/TokenRatesController.ts +++ b/packages/assets-controllers/src/TokenRatesController.ts @@ -6,13 +6,13 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { safelyExecute, toChecksumHexAddress, FALL_BACK_VS_CURRENCY, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, @@ -157,12 +157,10 @@ export type TokenRatesControllerEvents = TokenRatesControllerStateChangeEvent; /** * The messenger of the {@link TokenRatesController} for communication. */ -export type TokenRatesControllerMessenger = RestrictedMessenger< +export type TokenRatesControllerMessenger = Messenger< typeof controllerName, TokenRatesControllerActions | AllowedActions, - TokenRatesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokenRatesControllerEvents | AllowedEvents >; /** @@ -293,7 +291,7 @@ export class TokenRatesController extends StaticIntervalPollingController { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); From 1afff1ac7575ad9b09ad400de92636a70bb20351 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 22:08:00 +0200 Subject: [PATCH 050/247] migrate `TokenListController` messenger --- .../src/TokenListController.test.ts | 53 +++++++++++++------ .../src/TokenListController.ts | 14 +++-- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/packages/assets-controllers/src/TokenListController.test.ts b/packages/assets-controllers/src/TokenListController.test.ts index 5acfe9f7eda..774aa995052 100644 --- a/packages/assets-controllers/src/TokenListController.test.ts +++ b/packages/assets-controllers/src/TokenListController.test.ts @@ -1,4 +1,3 @@ -import { Messenger } from '@metamask/base-controller'; import { ChainId, NetworkType, @@ -6,16 +5,19 @@ import { toHex, InfuraNetworkType, } from '@metamask/controller-utils'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { NetworkState } from '@metamask/network-controller'; import type { Hex } from '@metamask/utils'; import nock from 'nock'; import * as sinon from 'sinon'; import { advanceTime } from '../../../tests/helpers'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildCustomNetworkClientConfiguration, buildInfuraNetworkClientConfiguration, @@ -29,7 +31,7 @@ import type { } from './TokenListController'; import { TokenListController } from './TokenListController'; -const name = 'TokenListController'; +const namespace = 'TokenListController'; const timestamp = Date.now(); const sampleMainnetTokenList = [ @@ -470,21 +472,40 @@ const expiredCacheExistingState: TokenListState = { preventPollingOnNetworkRestart: false, }; -type MainMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent +type AllTokenListControllerActions = + MessengerActions; + +type AllTokenListControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllTokenListControllerActions, + AllTokenListControllerEvents >; -const getMessenger = (): MainMessenger => { - return new Messenger(); +const getMessenger = (): RootMessenger => { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); }; -const getRestrictedMessenger = (messenger: MainMessenger) => { - return messenger.getRestricted({ - name, - allowedActions: ['NetworkController:getNetworkClientById'], - allowedEvents: ['NetworkController:stateChange'], +const getRestrictedMessenger = ( + messenger: RootMessenger, +): TokenListControllerMessenger => { + const tokenListControllerMessenger = new Messenger< + typeof namespace, + AllTokenListControllerActions, + AllTokenListControllerEvents, + RootMessenger + >({ + namespace, + parent: messenger, + }); + messenger.delegate({ + messenger: tokenListControllerMessenger, + actions: ['NetworkController:getNetworkClientById'], + events: ['NetworkController:stateChange'], }); + return tokenListControllerMessenger; }; describe('TokenListController', () => { diff --git a/packages/assets-controllers/src/TokenListController.ts b/packages/assets-controllers/src/TokenListController.ts index f7957c3be80..38119a729ed 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -1,9 +1,9 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { safelyExecute } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerStateChangeEvent, NetworkState, @@ -68,12 +68,10 @@ type AllowedActions = NetworkControllerGetNetworkClientByIdAction; type AllowedEvents = NetworkControllerStateChangeEvent; -export type TokenListControllerMessenger = RestrictedMessenger< +export type TokenListControllerMessenger = Messenger< typeof name, TokenListControllerActions | AllowedActions, - TokenListControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokenListControllerEvents | AllowedEvents >; const metadata = { @@ -163,7 +161,7 @@ export class TokenListController extends StaticIntervalPollingController Date: Wed, 3 Sep 2025 21:59:03 +0200 Subject: [PATCH 051/247] fix `TokenDetectionController` test --- .../src/TokenDetectionController.test.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index 8667f7516cb..8b7f0fd3efa 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -1,4 +1,3 @@ -import type { AddApprovalRequest } from '@metamask/approval-controller'; import { ChainId, NetworkType, @@ -10,9 +9,9 @@ import type { InternalAccount } from '@metamask/keyring-internal-api'; import { MOCK_ANY_NAMESPACE, Messenger, - MessengerActions, - MessengerEvents, - MockAnyNamespace, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, } from '@metamask/messenger'; import { getDefaultNetworkControllerState, @@ -44,11 +43,7 @@ import { } from './multi-chain-accounts-service/mocks/mock-get-balances'; import { MOCK_GET_SUPPORTED_NETWORKS_RESPONSE } from './multi-chain-accounts-service/mocks/mock-get-supported-networks'; import { TOKEN_END_POINT_API } from './token-service'; -import type { - AllowedActions, - AllowedEvents, - TokenDetectionControllerMessenger, -} from './TokenDetectionController'; +import type { TokenDetectionControllerMessenger } from './TokenDetectionController'; import { STATIC_MAINNET_TOKEN_LIST, TokenDetectionController, @@ -3758,12 +3753,15 @@ async function withController( .mockResolvedValue(undefined), ); - const callActionSpy = jest.spyOn(messenger, 'call'); + const tokenDetectionControllerMessenger = + buildTokenDetectionControllerMessenger(messenger); + + const callActionSpy = jest.spyOn(tokenDetectionControllerMessenger, 'call'); const controller = new TokenDetectionController({ getBalancesInSingleCall: jest.fn(), trackMetaMetricsEvent: jest.fn(), - messenger: buildTokenDetectionControllerMessenger(messenger), + messenger: tokenDetectionControllerMessenger, useAccountsAPI: false, platform: 'extension', ...options, From f4b79ec1b277effd1c234d4bbd0cf92c4810a372 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 21:59:08 +0200 Subject: [PATCH 052/247] update lockfile --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index f6f7362761a..c1c8f7344be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2739,6 +2739,7 @@ __metadata: "@metamask/eth-json-rpc-provider": "npm:^4.1.8" "@metamask/gas-fee-controller": "npm:^24.0.0" "@metamask/keyring-api": "npm:^20.1.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/multichain-network-controller": "npm:^0.12.0" "@metamask/network-controller": "npm:^24.1.0" From 459aaac87379f627dde2c27172a2b2c22f469881 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 23:41:03 +0200 Subject: [PATCH 053/247] migrate `EarnController` messenger --- packages/earn-controller/package.json | 1 + .../src/EarnController.test.ts | 61 ++++++++++++------- .../earn-controller/src/EarnController.ts | 22 +++---- yarn.lock | 1 + 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/packages/earn-controller/package.json b/packages/earn-controller/package.json index 15b03e2b1f9..52b9949b26e 100644 --- a/packages/earn-controller/package.json +++ b/packages/earn-controller/package.json @@ -51,6 +51,7 @@ "@ethersproject/providers": "^5.7.0", "@metamask/base-controller": "^8.2.0", "@metamask/controller-utils": "^11.12.0", + "@metamask/messenger": "^0.1.0", "@metamask/stake-sdk": "^3.2.1", "reselect": "^5.1.1" }, diff --git a/packages/earn-controller/src/EarnController.test.ts b/packages/earn-controller/src/EarnController.test.ts index 488b24fcefb..c39b25059c8 100644 --- a/packages/earn-controller/src/EarnController.test.ts +++ b/packages/earn-controller/src/EarnController.test.ts @@ -1,7 +1,13 @@ /* eslint-disable jest/no-conditional-in-test */ import type { AccountsController } from '@metamask/accounts-controller'; -import { Messenger } from '@metamask/base-controller'; import { toHex } from '@metamask/controller-utils'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + MessengerActions, + MessengerEvents, + MockAnyNamespace, +} from '@metamask/messenger'; import { getDefaultNetworkControllerState } from '@metamask/network-controller'; import { EarnSdk, @@ -17,10 +23,6 @@ import { EarnController, type EarnControllerState, type EarnControllerMessenger, - type EarnControllerEvents, - type EarnControllerActions, - type AllowedActions, - type AllowedEvents, DEFAULT_POOLED_STAKING_CHAIN_STATE, } from './EarnController'; import type { TransactionMeta } from '../../transaction-controller/src'; @@ -29,6 +31,16 @@ import { TransactionType, } from '../../transaction-controller/src'; +type AllEarnControllerActions = MessengerActions; + +type AllEarnControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllEarnControllerActions, + AllEarnControllerEvents +>; + jest.mock('@metamask/stake-sdk', () => ({ EarnSdk: { create: jest.fn().mockImplementation(() => ({ @@ -81,39 +93,45 @@ jest.mock('@metamask/stake-sdk', () => ({ })); /** - * Builds a new instance of the Messenger class for the AccountsController. + * Builds a new instance of the root messenger. * - * @returns A new instance of the Messenger class for the AccountsController. + * @returns A new instance of the root messenger. */ -function buildMessenger() { - return new Messenger< - EarnControllerActions | AllowedActions, - EarnControllerEvents | AllowedEvents - >(); +function buildMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** - * Constructs the messenger which is restricted to relevant EarnController - * actions and events. + * Constructs the messenger for EarnController. * - * @param rootMessenger - The root messenger to restrict. + * @param rootMessenger - The root messenger to set as parent. * @returns The restricted messenger. */ function getEarnControllerMessenger( rootMessenger = buildMessenger(), ): EarnControllerMessenger { - return rootMessenger.getRestricted({ - name: 'EarnController', - allowedActions: [ + const earnControllerMessenger = new Messenger< + 'EarnController', + AllEarnControllerActions, + AllEarnControllerEvents, + RootMessenger + >({ + namespace: 'EarnController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: earnControllerMessenger, + actions: [ 'NetworkController:getNetworkClientById', 'AccountsController:getSelectedAccount', ], - allowedEvents: [ + events: [ 'NetworkController:networkDidChange', 'AccountsController:selectedAccountChange', 'TransactionController:transactionConfirmed', ], }); + return earnControllerMessenger; } type InternalAccount = ReturnType; @@ -1457,10 +1475,7 @@ describe('EarnController', () => { describe('On transaction confirmed', () => { let controller: EarnController; - let messenger: Messenger< - EarnControllerActions | AllowedActions, - EarnControllerEvents | AllowedEvents - >; + let messenger: RootMessenger; beforeEach(async () => { const earnController = await setupController(); diff --git a/packages/earn-controller/src/EarnController.ts b/packages/earn-controller/src/EarnController.ts index 21fbc5423c2..ff0449f758d 100644 --- a/packages/earn-controller/src/EarnController.ts +++ b/packages/earn-controller/src/EarnController.ts @@ -6,11 +6,11 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { convertHexToDecimal, toHex } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerNetworkDidChangeEvent, @@ -263,12 +263,10 @@ export type AllowedEvents = * The messenger which is restricted to actions and events accessed by * EarnController. */ -export type EarnControllerMessenger = RestrictedMessenger< +export type EarnControllerMessenger = Messenger< typeof controllerName, EarnControllerActions | AllowedActions, - EarnControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + EarnControllerEvents | AllowedEvents >; // === CONTROLLER DEFINITION === @@ -334,7 +332,7 @@ export class EarnController extends BaseController< this.refreshLendingData().catch(console.error); // Listen for network changes - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:networkDidChange', (networkControllerState) => { this.#selectedNetworkClientId = @@ -355,7 +353,7 @@ export class EarnController extends BaseController< ); // Listen for account changes - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:selectedAccountChange', (account) => { const address = account?.address; @@ -374,7 +372,7 @@ export class EarnController extends BaseController< ); // Listen for confirmed staking transactions - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:transactionConfirmed', (transactionMeta) => { /** @@ -414,7 +412,7 @@ export class EarnController extends BaseController< * @param networkClientId - The network client id to initialize the Earn SDK for. */ async #initializeSDK(networkClientId: string) { - const networkClient = this.messagingSystem.call( + const networkClient = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -455,7 +453,7 @@ export class EarnController extends BaseController< * @returns The current account. */ #getCurrentAccount() { - return this.messagingSystem.call('AccountsController:getSelectedAccount'); + return this.messenger.call('AccountsController:getSelectedAccount'); } /** diff --git a/yarn.lock b/yarn.lock index 196bd14cdb0..49b3d37ba59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3034,6 +3034,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" "@metamask/controller-utils": "npm:^11.12.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/network-controller": "npm:^24.1.0" "@metamask/stake-sdk": "npm:^3.2.1" "@metamask/transaction-controller": "npm:^60.2.0" From 5ccd23a505a81950168d7f2b0e2dbb5db4bb5de3 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 3 Sep 2025 18:56:49 +0200 Subject: [PATCH 054/247] update changelog --- packages/earn-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/earn-controller/CHANGELOG.md b/packages/earn-controller/CHANGELOG.md index 8618350664e..796df09d52e 100644 --- a/packages/earn-controller/CHANGELOG.md +++ b/packages/earn-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6445](https://github.com/MetaMask/core/pull/6445)) + - Previously, `EarnController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [6.0.0] From b7ebf4edac591fc36d96e3d74f92cbadf3b6cfcc Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 11:37:22 +0200 Subject: [PATCH 055/247] update changelogs --- packages/assets-controllers/CHANGELOG.md | 9 +++++++++ packages/bridge-controller/CHANGELOG.md | 2 ++ packages/bridge-status-controller/CHANGELOG.md | 5 +++++ packages/polling-controller/CHANGELOG.md | 2 ++ 4 files changed, 18 insertions(+) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 032457253b1..d10d9d99600 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Migrate the following controllers to the new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - `AccountTrackerController` + - `CurrencyRateController` + - `DeFiPositionController` + - `MultichainAssetsRatesController` + - `TokenBalancesController` + - `TokenDetectionController` + - `TokenListController` + - `TokenRatesController` - **BREAKING:** Migrate `AssetsContractController`, `NftController`, and `TokensController` to new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - Previously, the controllers accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index 24acd0f3d81..1e576d00771 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `BridgeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - **BREAKING** Rename QuotesError and InputSourceDestinationSwitched events to match segment schema ([#6447](https://github.com/MetaMask/core/pull/6447)) ## [41.4.0] diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index 5076c5c31bb..108f7037a75 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `BridgeStatusController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [41.0.0] ### Fixed diff --git a/packages/polling-controller/CHANGELOG.md b/packages/polling-controller/CHANGELOG.md index 60f094cd4d6..20a0e0c675b 100644 --- a/packages/polling-controller/CHANGELOG.md +++ b/packages/polling-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` for `StaticIntervalPollingController` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `StaticIntervalPollingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.2.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) - Bump `@metamask/controller-utils` from `^11.10.0` to `^11.12.0` ([#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303)) - Bump `@metamask/utils` from `^11.2.0` to `^11.4.2` ([#6054](https://github.com/MetaMask/core/pull/6054)) From 7417eb5b597286bb9e54562c55c2482c282844a2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 12:51:08 +0200 Subject: [PATCH 056/247] migrate `EnsController` messenger --- packages/ens-controller/package.json | 1 + .../ens-controller/src/EnsController.test.ts | 126 ++++++++++-------- packages/ens-controller/src/EnsController.ts | 30 +++-- yarn.lock | 1 + 4 files changed, 92 insertions(+), 66 deletions(-) diff --git a/packages/ens-controller/package.json b/packages/ens-controller/package.json index 5ce60419ec5..afc6992dd6e 100644 --- a/packages/ens-controller/package.json +++ b/packages/ens-controller/package.json @@ -50,6 +50,7 @@ "@ethersproject/providers": "^5.7.0", "@metamask/base-controller": "^8.2.0", "@metamask/controller-utils": "^11.12.0", + "@metamask/messenger": "^0.1.0", "@metamask/utils": "^11.4.2", "punycode": "^2.1.1" }, diff --git a/packages/ens-controller/src/EnsController.test.ts b/packages/ens-controller/src/EnsController.test.ts index 9643074a27d..9c1102632b4 100644 --- a/packages/ens-controller/src/EnsController.test.ts +++ b/packages/ens-controller/src/EnsController.test.ts @@ -1,20 +1,22 @@ import * as providersModule from '@ethersproject/providers'; -import { Messenger } from '@metamask/base-controller'; import { toChecksumHexAddress, toHex, InfuraNetworkType, } from '@metamask/controller-utils'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { type NetworkController, type NetworkState, getDefaultNetworkControllerState, } from '@metamask/network-controller'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildMockGetNetworkClientById, buildCustomNetworkClientConfiguration, @@ -23,7 +25,6 @@ import { EnsController, DEFAULT_ENS_NETWORK_MAP } from './EnsController'; import type { EnsControllerState, EnsControllerMessenger, - AllowedActions, } from './EnsController'; const defaultState: EnsControllerState = { @@ -54,9 +55,14 @@ jest.mock('@ethersproject/providers', () => { }; }); +type AllEnsControllerActions = MessengerActions; + +type AllEnsControllerEvents = MessengerEvents; + type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + MockAnyNamespace, + AllEnsControllerActions, + AllEnsControllerEvents >; const ZERO_X_ERROR_ADDRESS = '0x'; @@ -76,27 +82,24 @@ const name = 'EnsController'; /** * Constructs the root messenger. * - * @returns A restricted messenger. + * @returns A root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger< - ExtractAvailableAction | AllowedActions, - ExtractAvailableEvent | never - >(); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** - * Constructs the messenger restricted to EnsController actions and events. + * Constructs the messenger for EnsController actions and events. * - * @param rootMessenger - The root messenger to base the restricted messenger + * @param rootMessenger - The root messenger to base the controller messenger * off of. * @param getNetworkClientByIdMock - Optional mock version of `getNetworkClientById`. - * @returns A restricted messenger. + * @returns A controller messenger for EnsController. */ -function getRestrictedMessenger( +function getEnsControllerMessenger( rootMessenger: RootMessenger, getNetworkClientByIdMock?: NetworkController['getNetworkClientById'], -) { +): EnsControllerMessenger { const mockNetworkState = jest.fn().mockReturnValue({ ...getDefaultNetworkControllerState(), selectedNetworkClientId: InfuraNetworkType.mainnet, @@ -115,14 +118,23 @@ function getRestrictedMessenger( getNetworkClientByIdMock, ); - return rootMessenger.getRestricted<'EnsController', AllowedActions['type']>({ - name, - allowedActions: [ + const ensControllerMessenger = new Messenger< + 'EnsController', + AllEnsControllerActions, + AllEnsControllerEvents, + RootMessenger + >({ + namespace: name, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: ensControllerMessenger, + actions: [ 'NetworkController:getNetworkClientById', 'NetworkController:getState', ], - allowedEvents: [], }); + return ensControllerMessenger; } /** @@ -137,7 +149,7 @@ function getProvider() { describe('EnsController', () => { it('should set default state', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -146,7 +158,7 @@ describe('EnsController', () => { it('should return registry address for `.`', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -159,7 +171,7 @@ describe('EnsController', () => { it('should not return registry address for unrecognized chains', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -168,7 +180,7 @@ describe('EnsController', () => { it('should add a new ENS entry and return true', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -182,7 +194,7 @@ describe('EnsController', () => { it('should clear ensResolutionsByAddress state propery when resetState is called', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, state: { @@ -203,7 +215,7 @@ describe('EnsController', () => { it('should clear ensResolutionsByAddress state propery on networkDidChange', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, state: { @@ -224,7 +236,7 @@ describe('EnsController', () => { it('should add a new ENS entry with null address and return true', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -238,7 +250,7 @@ describe('EnsController', () => { it('should update an ENS entry and return true', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -253,7 +265,7 @@ describe('EnsController', () => { it('should update an ENS entry with null address and return true', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -268,7 +280,7 @@ describe('EnsController', () => { it('should not update an ENS entry if the address is the same (valid address) and return false', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -283,7 +295,7 @@ describe('EnsController', () => { it('should not update an ENS entry if the address is the same (null) and return false', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -298,7 +310,7 @@ describe('EnsController', () => { it('should add multiple ENS entries and update without side effects', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -325,7 +337,7 @@ describe('EnsController', () => { it('should get ENS default registry by chainId when asking for `.`', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -339,7 +351,7 @@ describe('EnsController', () => { it('should get ENS entry by chainId and ensName', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -353,7 +365,7 @@ describe('EnsController', () => { it('should return null when getting nonexistent name', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -363,7 +375,7 @@ describe('EnsController', () => { it('should return null when getting nonexistent chainId', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -373,7 +385,7 @@ describe('EnsController', () => { it('should throw on attempt to set invalid ENS entry: chainId', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -388,7 +400,7 @@ describe('EnsController', () => { it('should throw on attempt to set invalid ENS entry: ENS name', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -400,7 +412,7 @@ describe('EnsController', () => { it('should throw on attempt to set invalid ENS entry: address', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -414,7 +426,7 @@ describe('EnsController', () => { it('should remove an ENS entry and return true', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -425,7 +437,7 @@ describe('EnsController', () => { it('should remove chain entries completely when all entries are removed', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -440,7 +452,7 @@ describe('EnsController', () => { it('should return false if an ENS entry was NOT deleted due to unsafe input', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -451,7 +463,7 @@ describe('EnsController', () => { it('should return false if an ENS entry was NOT deleted', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -467,7 +479,7 @@ describe('EnsController', () => { it('should add multiple ENS entries and remove without side effects', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -489,7 +501,7 @@ describe('EnsController', () => { it('should clear all ENS entries', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -506,7 +518,7 @@ describe('EnsController', () => { describe('reverseResolveName', () => { it('should return undefined when eth provider is not defined', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ens = new EnsController({ messenger: ensControllerMessenger, }); @@ -515,7 +527,7 @@ describe('EnsController', () => { it('should return undefined when network is loading', async function () { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -535,7 +547,7 @@ describe('EnsController', () => { chainId: '0x9999999', }), }); - const ensControllerMessenger = getRestrictedMessenger( + const ensControllerMessenger = getEnsControllerMessenger( rootMessenger, getNetworkClientById, ); @@ -553,7 +565,7 @@ describe('EnsController', () => { it('should only resolve an ENS name once', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(address1); jest @@ -577,7 +589,7 @@ describe('EnsController', () => { it('should fail if lookupAddress through an error', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest.spyOn(ethProvider, 'lookupAddress').mockRejectedValue('error'); jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); @@ -596,7 +608,7 @@ describe('EnsController', () => { it('should fail if lookupAddress returns a null value', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest.spyOn(ethProvider, 'lookupAddress').mockResolvedValue(null); jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); @@ -615,7 +627,7 @@ describe('EnsController', () => { it('should fail if resolveName through an error', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest .spyOn(ethProvider, 'lookupAddress') @@ -637,7 +649,7 @@ describe('EnsController', () => { it('should fail if resolveName returns a null value', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(null); jest @@ -659,7 +671,7 @@ describe('EnsController', () => { it('should fail if registred address is zero x error address', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest .spyOn(ethProvider, 'resolveName') @@ -683,7 +695,7 @@ describe('EnsController', () => { it('should fail if the name is registered to a different address than the reverse resolved', async () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const ethProvider = new providersModule.Web3Provider(getProvider()); jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(address2); diff --git a/packages/ens-controller/src/EnsController.ts b/packages/ens-controller/src/EnsController.ts index 1dba71cb6ae..5690bb08a55 100644 --- a/packages/ens-controller/src/EnsController.ts +++ b/packages/ens-controller/src/EnsController.ts @@ -1,6 +1,9 @@ import { Web3Provider } from '@ethersproject/providers'; -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import type { ChainId } from '@metamask/controller-utils'; import { normalizeEnsName, @@ -11,6 +14,7 @@ import { convertHexToDecimal, toHex, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, @@ -69,16 +73,24 @@ export type EnsControllerState = { ensResolutionsByAddress: { [key: string]: string }; }; +export type EnsControllerActions = ControllerGetStateAction< + typeof name, + EnsControllerState +>; + +export type EnsControllerEvents = ControllerStateChangeEvent< + typeof name, + EnsControllerState +>; + export type AllowedActions = | NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction; -export type EnsControllerMessenger = RestrictedMessenger< +export type EnsControllerMessenger = Messenger< typeof name, - AllowedActions, - never, - AllowedActions['type'], - never + EnsControllerActions | AllowedActions, + EnsControllerEvents >; const metadata = { @@ -278,7 +290,7 @@ export class EnsController extends BaseController< } #setDefaultEthProvider(registriesByChainId?: Record) { - const { selectedNetworkClientId } = this.messagingSystem.call( + const { selectedNetworkClientId } = this.messenger.call( 'NetworkController:getState', ); this.#setEthProvider(selectedNetworkClientId, registriesByChainId); @@ -291,7 +303,7 @@ export class EnsController extends BaseController< const { configuration: { chainId: currentChainId }, provider, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', selectedNetworkClientId, ); diff --git a/yarn.lock b/yarn.lock index 6ba685274fa..a124074cdbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3103,6 +3103,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" "@metamask/controller-utils": "npm:^11.12.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/network-controller": "npm:^24.1.0" "@metamask/utils": "npm:^11.4.2" "@types/jest": "npm:^27.4.1" From 6a944a21482c7bcf9e88d4f94bcdd282eb9ca180 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 12:52:50 +0200 Subject: [PATCH 057/247] update changelog --- packages/ens-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/ens-controller/CHANGELOG.md b/packages/ens-controller/CHANGELOG.md index ed612d7bed9..49db20572f3 100644 --- a/packages/ens-controller/CHANGELOG.md +++ b/packages/ens-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6459](https://github.com/MetaMask/core/pull/6460)) + - Previously, `EnsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.2.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) - Bump `@metamask/controller-utils` from `^11.11.0` to `^11.12.0` ([#6303](https://github.com/MetaMask/core/pull/6303)) From 23736c088969a545977e729acad28daf49423fb1 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 12:55:32 +0200 Subject: [PATCH 058/247] migrate `DelegationController` messenger --- packages/delegation-controller/package.json | 1 + .../src/DelegationController.test.ts | 52 +++++++++++++------ .../src/DelegationController.ts | 8 +-- packages/delegation-controller/src/types.ts | 10 ++-- yarn.lock | 1 + 5 files changed, 46 insertions(+), 26 deletions(-) diff --git a/packages/delegation-controller/package.json b/packages/delegation-controller/package.json index 6968425777c..e9967127901 100644 --- a/packages/delegation-controller/package.json +++ b/packages/delegation-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0", "@metamask/utils": "^11.4.2" }, "devDependencies": { diff --git a/packages/delegation-controller/src/DelegationController.test.ts b/packages/delegation-controller/src/DelegationController.test.ts index c1420d70716..802884f7200 100644 --- a/packages/delegation-controller/src/DelegationController.test.ts +++ b/packages/delegation-controller/src/DelegationController.test.ts @@ -1,9 +1,11 @@ -import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller'; -import { Messenger } from '@metamask/base-controller'; +import { SignTypedDataVersion } from '@metamask/keyring-controller'; import { - type KeyringControllerSignTypedMessageAction, - SignTypedDataVersion, -} from '@metamask/keyring-controller'; + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { hexToNumber } from '@metamask/utils'; import { ROOT_AUTHORITY } from './constants'; @@ -11,7 +13,7 @@ import { controllerName, DelegationController } from './DelegationController'; import type { Address, Delegation, - DelegationControllerEvents, + DelegationControllerMessenger, DelegationControllerState, DelegationEntry, DeleGatorEnvironment, @@ -19,6 +21,18 @@ import type { } from './types'; import { toDelegationStruct } from './utils'; +type AllDelegationControllerActions = + MessengerActions; + +type AllDelegationControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllDelegationControllerActions, + AllDelegationControllerEvents +>; + const FROM_MOCK = '0x2234567890123456789012345678901234567890' as Address; const SIGNATURE_HASH_MOCK = '0x123ABC'; @@ -60,11 +74,9 @@ class TestDelegationController extends DelegationController { * @returns The mock messenger instance plus individual mock functions for each action. */ function createMessengerMock() { - const messenger = new Messenger< - | KeyringControllerSignTypedMessageAction - | AccountsControllerGetSelectedAccountAction, - DelegationControllerEvents - >(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const accountsControllerGetSelectedAccountMock = jest.fn(); const keyringControllerSignTypedMessageMock = jest.fn(); @@ -84,19 +96,27 @@ function createMessengerMock() { keyringControllerSignTypedMessageMock, ); - const restrictedMessenger = messenger.getRestricted({ - name: `${controllerName}`, - allowedActions: [ + const delegationControllerMessenger = new Messenger< + 'DelegationController', + AllDelegationControllerActions, + AllDelegationControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: messenger, + }); + messenger.delegate({ + messenger: delegationControllerMessenger, + actions: [ 'AccountsController:getSelectedAccount', 'KeyringController:signTypedMessage', ], - allowedEvents: [], }); return { accountsControllerGetSelectedAccountMock, keyringControllerSignTypedMessageMock, - messenger: restrictedMessenger, + messenger: delegationControllerMessenger, }; } diff --git a/packages/delegation-controller/src/DelegationController.ts b/packages/delegation-controller/src/DelegationController.ts index 805c12dd946..df6be61f564 100644 --- a/packages/delegation-controller/src/DelegationController.ts +++ b/packages/delegation-controller/src/DelegationController.ts @@ -1,5 +1,5 @@ -import type { StateMetadata } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import type { StateMetadata } from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; import { hexToNumber } from '@metamask/utils'; @@ -113,7 +113,7 @@ export class DelegationController extends BaseController< // TODO:: Replace with `SignatureController:newUnsignedTypedMessage`. // Waiting on confirmations team to implement this. - const signature: string = await this.messagingSystem.call( + const signature: string = await this.messenger.call( 'KeyringController:signTypedMessage', data, SignTypedDataVersion.V4, @@ -152,7 +152,7 @@ export class DelegationController extends BaseController< * @returns A list of delegation entries that match the filter. */ list(filter?: DelegationFilter) { - const account = this.messagingSystem.call( + const account = this.messenger.call( 'AccountsController:getSelectedAccount', ); const requester = account.address as Address; diff --git a/packages/delegation-controller/src/types.ts b/packages/delegation-controller/src/types.ts index 20c73de1578..453a826be01 100644 --- a/packages/delegation-controller/src/types.ts +++ b/packages/delegation-controller/src/types.ts @@ -2,9 +2,9 @@ import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accou import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import type { KeyringControllerSignTypedMessageAction } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { controllerName, @@ -149,10 +149,8 @@ type AllowedActions = type AllowedEvents = never; -export type DelegationControllerMessenger = RestrictedMessenger< +export type DelegationControllerMessenger = Messenger< typeof controllerName, DelegationControllerActions | AllowedActions, - DelegationControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + DelegationControllerEvents | AllowedEvents >; diff --git a/yarn.lock b/yarn.lock index 6ba685274fa..8ab643c21e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3009,6 +3009,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" "@metamask/keyring-controller": "npm:^23.0.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/utils": "npm:^11.4.2" "@ts-bridge/cli": "npm:^0.6.1" "@types/jest": "npm:^27.4.1" From 2b596b6ad2938f05b7a8ffca2309692169cfa5aa Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 12:42:02 +0200 Subject: [PATCH 059/247] update changelog --- packages/delegation-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/delegation-controller/CHANGELOG.md b/packages/delegation-controller/CHANGELOG.md index 08f81ddb390..4057e3c7ea6 100644 --- a/packages/delegation-controller/CHANGELOG.md +++ b/packages/delegation-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed ++- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6459](https://github.com/MetaMask/core/pull/6459)) ++ - Previously, `DelegationController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [0.7.0] From 8ab585c66e231018452b91a1e2af45ea4634586e Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 13:02:37 +0200 Subject: [PATCH 060/247] migrate `GatorPermissionsController` messenger --- .../gator-permissions-controller/package.json | 1 + .../src/GatorPermissionContoller.test.ts | 83 ++++++++++++------- .../src/GatorPermissionsController.ts | 20 ++--- yarn.lock | 1 + 4 files changed, 63 insertions(+), 42 deletions(-) diff --git a/packages/gator-permissions-controller/package.json b/packages/gator-permissions-controller/package.json index b0031ad3ae8..9b66a863d8b 100644 --- a/packages/gator-permissions-controller/package.json +++ b/packages/gator-permissions-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.1.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/utils": "^11.4.2" diff --git a/packages/gator-permissions-controller/src/GatorPermissionContoller.test.ts b/packages/gator-permissions-controller/src/GatorPermissionContoller.test.ts index 4730e5490ff..3f4293e9987 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionContoller.test.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionContoller.test.ts @@ -1,4 +1,10 @@ -import { Messenger } from '@metamask/base-controller'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { HandleSnapRequest, HasSnap } from '@metamask/snaps-controllers'; import type { SnapId } from '@metamask/snaps-sdk'; import type { Hex } from '@metamask/utils'; @@ -19,10 +25,6 @@ import type { StoredGatorPermission, PermissionTypes, } from './types'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; const MOCK_CHAIN_ID_1: Hex = '0xaa36a7'; const MOCK_CHAIN_ID_2: Hex = '0x1'; @@ -72,7 +74,7 @@ describe('GatorPermissionsController', () => { describe('constructor', () => { it('creates GatorPermissionsController with default state', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect(controller.state.isGatorPermissionsEnabled).toBe(false); @@ -102,7 +104,7 @@ describe('GatorPermissionsController', () => { }; const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), state: customState, }); @@ -117,7 +119,7 @@ describe('GatorPermissionsController', () => { it('creates GatorPermissionsController with default config', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect(controller.permissionsProviderSnapId).toBe( @@ -129,7 +131,7 @@ describe('GatorPermissionsController', () => { it('isFetchingGatorPermissions is false on initialization', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), state: { isFetchingGatorPermissions: true, }, @@ -142,7 +144,7 @@ describe('GatorPermissionsController', () => { describe('disableGatorPermissions', () => { it('disables gator permissions successfully', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.enableGatorPermissions(); @@ -166,7 +168,7 @@ describe('GatorPermissionsController', () => { describe('fetchAndUpdateGatorPermissions', () => { it('fetches and updates gator permissions successfully', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.enableGatorPermissions(); @@ -215,7 +217,7 @@ describe('GatorPermissionsController', () => { it('throws error when gator permissions are not enabled', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.disableGatorPermissions(); @@ -231,7 +233,7 @@ describe('GatorPermissionsController', () => { }); const controller = new GatorPermissionsController({ - messenger: getMessenger(rootMessenger), + messenger: getGatorPermissionsControllerMessenger(rootMessenger), }); await controller.enableGatorPermissions(); @@ -253,7 +255,7 @@ describe('GatorPermissionsController', () => { }); const controller = new GatorPermissionsController({ - messenger: getMessenger(rootMessenger), + messenger: getGatorPermissionsControllerMessenger(rootMessenger), }); await controller.enableGatorPermissions(); @@ -277,7 +279,7 @@ describe('GatorPermissionsController', () => { }); const controller = new GatorPermissionsController({ - messenger: getMessenger(rootMessenger), + messenger: getGatorPermissionsControllerMessenger(rootMessenger), }); await controller.enableGatorPermissions(); @@ -293,7 +295,7 @@ describe('GatorPermissionsController', () => { describe('gatorPermissionsMap getter tests', () => { it('returns parsed gator permissions map', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); const { gatorPermissionsMap } = controller; @@ -331,7 +333,7 @@ describe('GatorPermissionsController', () => { }; const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), state: { gatorPermissionsMapSerialized: JSON.stringify(mockState), }, @@ -345,7 +347,7 @@ describe('GatorPermissionsController', () => { describe('message handlers tests', () => { it('registers all message handlers', () => { - const messenger = getMessenger(); + const messenger = getGatorPermissionsControllerMessenger(); const mockRegisterActionHandler = jest.spyOn( messenger, 'registerActionHandler', @@ -373,7 +375,7 @@ describe('GatorPermissionsController', () => { describe('enableGatorPermissions', () => { it('enables gator permissions successfully', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.enableGatorPermissions(); @@ -386,15 +388,23 @@ describe('GatorPermissionsController', () => { /** * The union of actions that the root messenger allows. */ -type RootAction = ExtractAvailableAction; +type AllGatorPermissionsControllerActions = + MessengerActions; /** * The union of events that the root messenger allows. */ -type RootEvent = ExtractAvailableEvent; +type AllGatorPermissionsControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllGatorPermissionsControllerActions, + AllGatorPermissionsControllerEvents +>; /** - * Constructs the unrestricted messenger. This can be used to call actions and + * Constructs the root messenger. This can be used to call actions and * publish events within the tests for this controller. * * @param args - The arguments to this function. @@ -418,8 +428,10 @@ function getRootMessenger({ }: { snapControllerHandleRequestActionHandler?: HandleSnapRequest['handler']; snapControllerHasActionHandler?: HasSnap['handler']; -} = {}): Messenger { - const rootMessenger = new Messenger(); +} = {}): RootMessenger { + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); rootMessenger.registerActionHandler( 'SnapController:handleRequest', @@ -433,18 +445,27 @@ function getRootMessenger({ } /** - * Constructs the messenger which is restricted to relevant SampleGasPricesController + * Constructs the messenger supporting relevant SampleGasPricesController * actions and events. * * @param rootMessenger - The root messenger to restrict. - * @returns The restricted messenger. + * @returns The controller messenger. */ -function getMessenger( +function getGatorPermissionsControllerMessenger( rootMessenger = getRootMessenger(), ): GatorPermissionsControllerMessenger { - return rootMessenger.getRestricted({ - name: 'GatorPermissionsController', - allowedActions: ['SnapController:handleRequest', 'SnapController:has'], - allowedEvents: [], + const gatorPermissionsControllerMessenger = new Messenger< + 'GatorPermissionsController', + AllGatorPermissionsControllerActions, + AllGatorPermissionsControllerEvents, + RootMessenger + >({ + namespace: 'GatorPermissionsController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: gatorPermissionsControllerMessenger, + actions: ['SnapController:handleRequest', 'SnapController:has'], }); + return gatorPermissionsControllerMessenger; } diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.ts index 9b5b02864d1..685e7d78827 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.ts @@ -1,10 +1,10 @@ import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { HandleSnapRequest, HasSnap } from '@metamask/snaps-controllers'; import type { SnapId } from '@metamask/snaps-sdk'; import { HandlerType } from '@metamask/snaps-utils'; @@ -189,12 +189,10 @@ type AllowedEvents = GatorPermissionsControllerStateChangeEvent; /** * Messenger type for the GatorPermissionsController. */ -export type GatorPermissionsControllerMessenger = RestrictedMessenger< +export type GatorPermissionsControllerMessenger = Messenger< typeof controllerName, GatorPermissionsControllerActions | AllowedActions, - GatorPermissionsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + GatorPermissionsControllerEvents | AllowedEvents >; /** @@ -246,17 +244,17 @@ export default class GatorPermissionsController extends BaseController< } #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:fetchAndUpdateGatorPermissions`, this.fetchAndUpdateGatorPermissions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:enableGatorPermissions`, this.enableGatorPermissions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:disableGatorPermissions`, this.disableGatorPermissions.bind(this), ); @@ -286,7 +284,7 @@ export default class GatorPermissionsController extends BaseController< snapId: SnapId; }): Promise[] | null> { try { - const response = (await this.messagingSystem.call( + const response = (await this.messenger.call( 'SnapController:handleRequest', { snapId, diff --git a/yarn.lock b/yarn.lock index 6ba685274fa..613364a7624 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3566,6 +3566,7 @@ __metadata: "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" "@metamask/snaps-utils": "npm:^11.0.0" From 02153ccb796dcba7e6b5eadd0d32af4ae043c2b8 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 13:04:56 +0200 Subject: [PATCH 061/247] update changelog --- packages/gator-permissions-controller/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gator-permissions-controller/CHANGELOG.md b/packages/gator-permissions-controller/CHANGELOG.md index eff38c93eb3..2030da84b33 100644 --- a/packages/gator-permissions-controller/CHANGELOG.md +++ b/packages/gator-permissions-controller/CHANGELOG.md @@ -9,6 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial release ([#6033](https://github.com/MetaMask/core/pull/6033)) +- Initial release ([#6033](https://github.com/MetaMask/core/pull/6033), [#6461](https://github.com/MetaMask/core/pull/6461)) [Unreleased]: https://github.com/MetaMask/core/ From d909cfd30a1d3be5558c05e4bb8d7c972cdd8b86 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 13:33:02 +0200 Subject: [PATCH 062/247] migrate `ErrorReportingService` messenger --- packages/error-reporting-service/package.json | 2 +- .../src/error-reporting-service.test.ts | 16 +++++-- .../src/error-reporting-service.ts | 46 +++++++++++++------ yarn.lock | 2 +- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/packages/error-reporting-service/package.json b/packages/error-reporting-service/package.json index 7ccb06e4ba0..66272391fb3 100644 --- a/packages/error-reporting-service/package.json +++ b/packages/error-reporting-service/package.json @@ -47,7 +47,7 @@ "since-latest-release": "../../scripts/since-latest-release.sh" }, "dependencies": { - "@metamask/base-controller": "^8.2.0" + "@metamask/messenger": "^0.1.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/error-reporting-service/src/error-reporting-service.test.ts b/packages/error-reporting-service/src/error-reporting-service.test.ts index 1b3afe292d7..47ddaeb2faf 100644 --- a/packages/error-reporting-service/src/error-reporting-service.test.ts +++ b/packages/error-reporting-service/src/error-reporting-service.test.ts @@ -1,4 +1,8 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { captureException as sentryCaptureException } from '@sentry/core'; import type { ErrorReportingServiceMessenger } from './error-reporting-service'; @@ -68,9 +72,11 @@ describe('ErrorReportingService', () => { * @returns The messenger. */ function buildMessenger(): ErrorReportingServiceMessenger { - return new Messenger().getRestricted({ - name: 'ErrorReportingService', - allowedActions: [], - allowedEvents: [], + return new Messenger< + 'ErrorReportingService', + MessengerActions, + MessengerEvents + >({ + namespace: 'ErrorReportingService', }); } diff --git a/packages/error-reporting-service/src/error-reporting-service.ts b/packages/error-reporting-service/src/error-reporting-service.ts index 447b1605d28..d448c0cd286 100644 --- a/packages/error-reporting-service/src/error-reporting-service.ts +++ b/packages/error-reporting-service/src/error-reporting-service.ts @@ -1,4 +1,4 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; /** * The action which can be used to report an error. @@ -37,12 +37,10 @@ type AllowedEvents = never; * The messenger restricted to actions and events that * {@link ErrorReportingService} needs to access. */ -export type ErrorReportingServiceMessenger = RestrictedMessenger< +export type ErrorReportingServiceMessenger = Messenger< 'ErrorReportingService', ErrorReportingServiceActions | AllowedActions, - ErrorReportingServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + ErrorReportingServiceEvents | AllowedEvents >; /** @@ -69,12 +67,10 @@ type ErrorReportingServiceOptions = { * * // Define the messenger type for the controller. * type AllowedActions = ErrorReportingServiceCaptureExceptionAction; - * type ExampleControllerMessenger = RestrictedMessenger< + * type ExampleControllerMessenger = Messenger< * 'ExampleController', * AllowedActions, * never, - * AllowedActions['type'], - * never * >; * * // Define the controller. @@ -86,7 +82,7 @@ type ErrorReportingServiceOptions = { * doSomething() { * // Imagine that we do something that produces an error and we want to * // report the error. - * this.messagingSystem.call( + * this.messenger.call( * 'ErrorReportingService:captureException', * new Error('Something went wrong'), * ); @@ -99,23 +95,43 @@ type ErrorReportingServiceOptions = { * import { ErrorReportingService } from '@metamask/error-reporting-service'; * import { ExampleController } from './example-controller'; * + * type AllActions = MessengerActions; + * + * type AllEvents = MessengerEvents; + * + * type RootMessenger = Messenger<'Root', AllActions, AllEvents>; + * * // Create a global messenger. * const globalMessenger = new Messenger(); * * // Register handler for the `ErrorReportingService:captureException` * // action in the global messenger. - * const errorReportingServiceMessenger = globalMessenger.getRestricted({ - * allowedActions: [], - * allowedEvents: [], + * const errorReportingServiceMessenger = new Messenger< + * 'ErrorReportingService', + * MessengerActions, + * MessengerEvents, + * RootMessenger + * >({ + * namespace: 'ErrorReportingService', + * parent: globalMessenger, * }); * const errorReportingService = new ErrorReportingService({ * messenger: errorReportingServiceMessenger, * captureException, * }); * - * const exampleControllerMessenger = globalMessenger.getRestricted({ - * allowedActions: ['ErrorReportingService:captureException'], - * allowedEvents: [], + * const exampleControllerMessenger = new Messenger< + * 'ExampleController', + * MessengerActions, + * MessengerEvents, + * RootMessenger + * >({ + * namespace: 'ExampleController', + * parent: globalMessenger, + * }); + * globalMessenger.delegate({ + * messenger: exampleControllerMessenger, + * actions: ['ErrorReportingService:captureException'], * }); * const exampleController = new ExampleController({ * messenger: exampleControllerMessenger, diff --git a/yarn.lock b/yarn.lock index 6ba685274fa..7af3647d0e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3123,7 +3123,7 @@ __metadata: resolution: "@metamask/error-reporting-service@workspace:packages/error-reporting-service" dependencies: "@metamask/auto-changelog": "npm:^3.4.4" - "@metamask/base-controller": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.1.0" "@sentry/core": "npm:^9.22.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" From 74e5fca886e16ff7b0a36e64c20c944f952df46a Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 13:34:37 +0200 Subject: [PATCH 063/247] update changelog --- packages/error-reporting-service/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/error-reporting-service/CHANGELOG.md b/packages/error-reporting-service/CHANGELOG.md index fb0ca3842d8..5a2c19a49b3 100644 --- a/packages/error-reporting-service/CHANGELOG.md +++ b/packages/error-reporting-service/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6462](https://github.com/MetaMask/core/pull/6462)) + - Previously, `ErrorReportingService` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.2.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) ## [2.0.0] From 00a34183a9bca194659816251fdd2d443eed437c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 14:15:14 +0200 Subject: [PATCH 064/247] migrate `LoggingController` messenger --- packages/logging-controller/package.json | 1 + .../src/LoggingController.test.ts | 86 +++++++++++-------- .../src/LoggingController.ts | 17 ++-- yarn.lock | 1 + 4 files changed, 57 insertions(+), 48 deletions(-) diff --git a/packages/logging-controller/package.json b/packages/logging-controller/package.json index 9ef63776e3d..6db801c145f 100644 --- a/packages/logging-controller/package.json +++ b/packages/logging-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.2.0", "@metamask/controller-utils": "^11.12.0", + "@metamask/messenger": "^0.1.0", "uuid": "^8.3.2" }, "devDependencies": { diff --git a/packages/logging-controller/src/LoggingController.test.ts b/packages/logging-controller/src/LoggingController.test.ts index 929cbb42f98..7a4323a1b6a 100644 --- a/packages/logging-controller/src/LoggingController.test.ts +++ b/packages/logging-controller/src/LoggingController.test.ts @@ -1,7 +1,13 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import * as uuid from 'uuid'; -import type { LoggingControllerActions } from './LoggingController'; +import type { LoggingControllerMessenger } from './LoggingController'; import { LoggingController } from './LoggingController'; import { LogType } from './logTypes'; import { SigningMethod, SigningStage } from './logTypes/EthSignLog'; @@ -15,44 +21,56 @@ jest.mock('uuid', () => { }; }); -const name = 'LoggingController'; +type AllLoggingControllerActions = MessengerActions; + +type AllLoggingControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllLoggingControllerActions, + AllLoggingControllerEvents +>; + +const namespace = 'LoggingController'; /** - * Constructs a unrestricted messenger. + * Constructs a root messenger instance. * - * @returns A unrestricted messenger. + * @returns A root messenger. */ -function getUnrestrictedMessenger() { - return new Messenger(); +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** - * Constructs a restricted messenger. + * Constructs a messenger instance for LoggingController. * - * @param messenger - An optional unrestricted messenger - * @returns A restricted messenger. + * @param messenger - An optional root messenger + * @returns A controller messenger. */ -function getRestrictedMessenger(messenger = getUnrestrictedMessenger()) { - return messenger.getRestricted({ - name, - allowedActions: [], - allowedEvents: [], +function getLoggingControllerMessenger(messenger = getRootMessenger()) { + return new Messenger< + typeof namespace, + AllLoggingControllerActions, + AllLoggingControllerEvents, + RootMessenger + >({ + namespace, + parent: messenger, }); } describe('LoggingController', () => { it('action: LoggingController:add with generic log', async () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); expect( - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/await-thenable - await unrestricted.call('LoggingController:add', { + rootMessenger.call('LoggingController:add', { type: LogType.GenericLog, data: `Generic log`, }), @@ -70,17 +88,15 @@ describe('LoggingController', () => { }); it('action: LoggingController:add for a signing request', async () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); expect( - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/await-thenable - await unrestricted.call('LoggingController:add', { + rootMessenger.call('LoggingController:add', { type: LogType.EthSignLog, data: { signingMethod: SigningMethod.PersonalSign, @@ -106,8 +122,8 @@ describe('LoggingController', () => { }); it('action: LoggingController:add prevents possible collision of ids', async () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const uuidV1Spy = jest.spyOn(uuid, 'v1'); const controller = new LoggingController({ @@ -115,9 +131,7 @@ describe('LoggingController', () => { }); expect( - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/await-thenable - await unrestricted.call('LoggingController:add', { + rootMessenger.call('LoggingController:add', { type: LogType.GenericLog, data: `Generic log`, }), @@ -128,9 +142,7 @@ describe('LoggingController', () => { uuidV1Spy.mockReturnValueOnce(id); expect( - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/await-thenable - await unrestricted.call('LoggingController:add', { + rootMessenger.call('LoggingController:add', { type: LogType.GenericLog, data: `Generic log 2`, }), @@ -159,17 +171,15 @@ describe('LoggingController', () => { }); it('internal method: clear', async () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); expect( - // TODO: Either fix this lint violation or explain why it's necessary to ignore. - // eslint-disable-next-line @typescript-eslint/await-thenable - await unrestricted.call('LoggingController:add', { + rootMessenger.call('LoggingController:add', { type: LogType.EthSignLog, data: { signingMethod: SigningMethod.PersonalSign, diff --git a/packages/logging-controller/src/LoggingController.ts b/packages/logging-controller/src/LoggingController.ts index 384108d78fa..670ba7175ab 100644 --- a/packages/logging-controller/src/LoggingController.ts +++ b/packages/logging-controller/src/LoggingController.ts @@ -1,9 +1,9 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import { v1 as random } from 'uuid'; import type { Log } from './logTypes'; @@ -54,12 +54,10 @@ export type LoggingControllerStateChangeEvent = ControllerStateChangeEvent< export type LoggingControllerEvents = LoggingControllerStateChangeEvent; -export type LoggingControllerMessenger = RestrictedMessenger< +export type LoggingControllerMessenger = Messenger< typeof name, LoggingControllerActions, - LoggingControllerEvents, - never, - never + LoggingControllerEvents >; const metadata = { @@ -102,9 +100,8 @@ export class LoggingController extends BaseController< }, }); - this.messagingSystem.registerActionHandler( - `${name}:add` as const, - (log: Log) => this.add(log), + this.messenger.registerActionHandler(`${name}:add` as const, (log: Log) => + this.add(log), ); } diff --git a/yarn.lock b/yarn.lock index 6ba685274fa..298af981cdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3748,6 +3748,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" "@metamask/controller-utils": "npm:^11.12.0" + "@metamask/messenger": "npm:^0.1.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" From 388cc9caaba544e64a9e9907702e7472b9d7061d Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 4 Sep 2025 14:17:31 +0200 Subject: [PATCH 065/247] update changelog --- packages/logging-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/logging-controller/CHANGELOG.md b/packages/logging-controller/CHANGELOG.md index 0ee73907649..d10afe772a4 100644 --- a/packages/logging-controller/CHANGELOG.md +++ b/packages/logging-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6463](https://github.com/MetaMask/core/pull/6463)) + - Previously, `LoggingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.0` to `^8.2.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355)) - Bump `@metamask/controller-utils` from `^11.5.0` to `^11.12.0` ([#5439](https://github.com/MetaMask/core/pull/5439), [#5583](https://github.com/MetaMask/core/pull/5583), [#5765](https://github.com/MetaMask/core/pull/5765), [#5812](https://github.com/MetaMask/core/pull/5812), [#5935](https://github.com/MetaMask/core/pull/5935), [#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303)) From 1da4a3efaa68ee458724a25b4161a1d8cc47a34a Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 9 Sep 2025 22:58:47 +0200 Subject: [PATCH 066/247] refactor: migrate MultichainNetworkController to @metamask/messenger --- .../CHANGELOG.md | 2 + .../package.json | 1 + .../MultichainNetworkController.test.ts | 70 +++++++++++++------ .../MultichainNetworkController.ts | 32 ++++----- .../src/constants.ts | 2 +- .../src/types.ts | 24 ++----- yarn.lock | 1 + 7 files changed, 74 insertions(+), 58 deletions(-) diff --git a/packages/multichain-network-controller/CHANGELOG.md b/packages/multichain-network-controller/CHANGELOG.md index 9afbecfd441..16079aec6da 100644 --- a/packages/multichain-network-controller/CHANGELOG.md +++ b/packages/multichain-network-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6543](https://github.com/MetaMask/core/pull/6543)) + - Previously, `MultichainNetworkController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.3.0` ([#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465)) ## [0.12.0] diff --git a/packages/multichain-network-controller/package.json b/packages/multichain-network-controller/package.json index a1482641888..ad159469f91 100644 --- a/packages/multichain-network-controller/package.json +++ b/packages/multichain-network-controller/package.json @@ -51,6 +51,7 @@ "@metamask/controller-utils": "^11.12.0", "@metamask/keyring-api": "^20.1.0", "@metamask/keyring-internal-api": "^8.1.0", + "@metamask/messenger": "^0.2.0", "@metamask/superstruct": "^3.1.0", "@metamask/utils": "^11.4.2", "@solana/addresses": "^2.0.0", diff --git a/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.test.ts b/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.test.ts index 763046dcd49..18611cea5d1 100644 --- a/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.test.ts +++ b/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.test.ts @@ -1,4 +1,3 @@ -import { Messenger } from '@metamask/base-controller'; import { InfuraNetworkType } from '@metamask/controller-utils'; import type { AnyAccountType } from '@metamask/keyring-api'; import { @@ -12,6 +11,13 @@ import { EthScope, TrxAccountType, } from '@metamask/keyring-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { NetworkControllerGetStateAction, NetworkControllerSetActiveNetworkAction, @@ -26,13 +32,7 @@ import { createMockInternalAccount } from '../../tests/utils'; import { type ActiveNetworksResponse } from '../api/accounts-api'; import { getDefaultMultichainNetworkControllerState } from '../constants'; import type { AbstractMultichainNetworkService } from '../MultichainNetworkService/AbstractMultichainNetworkService'; -import { - type AllowedActions, - type AllowedEvents, - type MultichainNetworkControllerAllowedActions, - type MultichainNetworkControllerAllowedEvents, - MULTICHAIN_NETWORK_CONTROLLER_NAME, -} from '../types'; +import type { MultichainNetworkControllerMessenger } from '../types'; // We exclude the generic account type, since it's used for testing purposes. type TestKeyringAccountType = Exclude< @@ -56,6 +56,31 @@ function createMockNetworkService( }; } +const controllerName = 'MultichainNetworkController'; + +type AllMultichainNetworkControllerActions = + MessengerActions; + +type AllMultichainNetworkControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllMultichainNetworkControllerActions, + AllMultichainNetworkControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Setup a test controller instance. * @@ -103,12 +128,7 @@ function setupController({ >; mockNetworkService?: AbstractMultichainNetworkService; } = {}) { - const messenger = new Messenger< - MultichainNetworkControllerAllowedActions, - MultichainNetworkControllerAllowedEvents - >(); - - const publishSpy = jest.spyOn(messenger, 'publish'); + const messenger = getRootMessenger(); // Register action handlers const mockGetNetworkState = @@ -168,13 +188,19 @@ function setupController({ mockFindNetworkClientIdByChainId, ); - const controllerMessenger = messenger.getRestricted< - typeof MULTICHAIN_NETWORK_CONTROLLER_NAME, - AllowedActions['type'], - AllowedEvents['type'] + const controllerMessenger = new Messenger< + typeof controllerName, + AllMultichainNetworkControllerActions, + AllMultichainNetworkControllerEvents, + RootMessenger >({ - name: MULTICHAIN_NETWORK_CONTROLLER_NAME, - allowedActions: [ + namespace: controllerName, + parent: messenger, + }); + + messenger.delegate({ + messenger: controllerMessenger, + actions: [ 'NetworkController:setActiveNetwork', 'NetworkController:getState', 'NetworkController:removeNetwork', @@ -182,7 +208,7 @@ function setupController({ 'NetworkController:findNetworkClientIdByChainId', 'AccountsController:listMultichainAccounts', ], - allowedEvents: ['AccountsController:selectedAccountChange'], + events: ['AccountsController:selectedAccountChange'], }); const defaultNetworkService = createMockNetworkService(); @@ -224,6 +250,8 @@ function setupController({ messenger.publish('AccountsController:selectedAccountChange', mockAccount); }; + const publishSpy = jest.spyOn(controllerMessenger, 'publish'); + return { messenger, controller, diff --git a/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.ts b/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.ts index bf4b30678e3..ff3e53f83a0 100644 --- a/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.ts +++ b/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.ts @@ -1,4 +1,4 @@ -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import { isEvmAccountType } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { NetworkClientId } from '@metamask/network-controller'; @@ -76,7 +76,7 @@ export class MultichainNetworkController extends BaseController< * @param id - The client ID of the EVM network to set active. */ async #setActiveEvmNetwork(id: NetworkClientId): Promise { - const { selectedNetworkClientId } = this.messagingSystem.call( + const { selectedNetworkClientId } = this.messenger.call( 'NetworkController:getState', ); @@ -97,12 +97,12 @@ export class MultichainNetworkController extends BaseController< // Only notify the network controller if the selected evm network is different if (shouldNotifyNetworkChange) { - await this.messagingSystem.call('NetworkController:setActiveNetwork', id); + await this.messenger.call('NetworkController:setActiveNetwork', id); } // Only publish the networkDidChange event if either the EVM network is different or we're switching between EVM and non-EVM networks if (shouldSetEvmActive || shouldNotifyNetworkChange) { - this.messagingSystem.publish( + this.messenger.publish( 'MultichainNetworkController:networkDidChange', id, ); @@ -129,10 +129,7 @@ export class MultichainNetworkController extends BaseController< }); // Notify listeners that the network changed - this.messagingSystem.publish( - 'MultichainNetworkController:networkDidChange', - id, - ); + this.messenger.publish('MultichainNetworkController:networkDidChange', id); } /** @@ -164,7 +161,7 @@ export class MultichainNetworkController extends BaseController< async getNetworksWithTransactionActivityByAccounts(): Promise { // TODO: We are filtering out non-EVN accounts for now // Support for non-EVM networks will be added in the coming weeks - const evmAccounts = this.messagingSystem + const evmAccounts = this.messenger .call('AccountsController:listMultichainAccounts') .filter((account) => isEvmAccountType(account.type)); @@ -196,7 +193,7 @@ export class MultichainNetworkController extends BaseController< */ async #removeEvmNetwork(chainId: CaipChainId): Promise { const hexChainId = convertEvmCaipToHexChainId(chainId); - const selectedChainId = this.messagingSystem.call( + const selectedChainId = this.messenger.call( 'NetworkController:getSelectedChainId', ); @@ -209,18 +206,15 @@ export class MultichainNetworkController extends BaseController< // If a non-EVM network is selected, we can delete the currently EVM selected network, but // we automatically switch to EVM mainnet. const ethereumMainnetHexChainId = '0x1'; // TODO: Should probably be a constant. - const clientId = this.messagingSystem.call( + const clientId = this.messenger.call( 'NetworkController:findNetworkClientIdByChainId', ethereumMainnetHexChainId, ); - await this.messagingSystem.call( - 'NetworkController:setActiveNetwork', - clientId, - ); + await this.messenger.call('NetworkController:setActiveNetwork', clientId); } - this.messagingSystem.call('NetworkController:removeNetwork', hexChainId); + this.messenger.call('NetworkController:removeNetwork', hexChainId); } /** @@ -297,7 +291,7 @@ export class MultichainNetworkController extends BaseController< */ #subscribeToMessageEvents() { // Handle network switch when account is changed - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:selectedAccountChange', (account) => this.#handleOnSelectedAccountChange(account), ); @@ -307,11 +301,11 @@ export class MultichainNetworkController extends BaseController< * Registers message handlers. */ #registerMessageHandlers() { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'MultichainNetworkController:setActiveNetwork', this.setActiveNetwork.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'MultichainNetworkController:getNetworksWithTransactionActivityByAccounts', this.getNetworksWithTransactionActivityByAccounts.bind(this), ); diff --git a/packages/multichain-network-controller/src/constants.ts b/packages/multichain-network-controller/src/constants.ts index 779b07e0e5e..3b2c06e240a 100644 --- a/packages/multichain-network-controller/src/constants.ts +++ b/packages/multichain-network-controller/src/constants.ts @@ -1,4 +1,4 @@ -import { type StateMetadata } from '@metamask/base-controller'; +import { type StateMetadata } from '@metamask/base-controller/next'; import { type CaipChainId, BtcScope, diff --git a/packages/multichain-network-controller/src/types.ts b/packages/multichain-network-controller/src/types.ts index 0132b9ccd18..068ab6f171b 100644 --- a/packages/multichain-network-controller/src/types.ts +++ b/packages/multichain-network-controller/src/types.ts @@ -2,8 +2,7 @@ import type { AccountsControllerListMultichainAccountsAction } from '@metamask/a import { type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import type { BtcScope, CaipAssetType, @@ -12,6 +11,7 @@ import type { TrxScope, } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkStatus, NetworkControllerSetActiveNetworkAction, @@ -178,7 +178,7 @@ export type MultichainNetworkControllerEvents = /** * Actions that this controller is allowed to call. */ -export type AllowedActions = +type AllowedActions = | NetworkControllerGetStateAction | NetworkControllerSetActiveNetworkAction | AccountsControllerListMultichainAccountsAction @@ -195,23 +195,13 @@ export type AccountsControllerSelectedAccountChangeEvent = { /** * Events that this controller is allowed to subscribe. */ -export type AllowedEvents = AccountsControllerSelectedAccountChangeEvent; - -export type MultichainNetworkControllerAllowedActions = - | MultichainNetworkControllerActions - | AllowedActions; - -export type MultichainNetworkControllerAllowedEvents = - | MultichainNetworkControllerEvents - | AllowedEvents; +type AllowedEvents = AccountsControllerSelectedAccountChangeEvent; /** * Messenger type for the MultichainNetworkController. */ -export type MultichainNetworkControllerMessenger = RestrictedMessenger< +export type MultichainNetworkControllerMessenger = Messenger< typeof MULTICHAIN_NETWORK_CONTROLLER_NAME, - MultichainNetworkControllerAllowedActions, - MultichainNetworkControllerAllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + MultichainNetworkControllerActions | AllowedActions, + MultichainNetworkControllerEvents | AllowedEvents >; diff --git a/yarn.lock b/yarn.lock index 0cf2a57b624..b36ec887d73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3895,6 +3895,7 @@ __metadata: "@metamask/keyring-api": "npm:^20.1.0" "@metamask/keyring-controller": "npm:^23.0.0" "@metamask/keyring-internal-api": "npm:^8.1.0" + "@metamask/messenger": "npm:^0.2.0" "@metamask/network-controller": "npm:^24.1.0" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.4.2" From f22848c363c045ff7c32ae76bf5c06accc44596c Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 10 Sep 2025 00:31:00 +0200 Subject: [PATCH 067/247] refactor: migrate MultichainAccountService to @metamask/messenger --- .../multichain-account-service/CHANGELOG.md | 5 + .../multichain-account-service/package.json | 1 + .../src/MultichainAccountService.test.ts | 126 ++++++++++-------- .../src/providers/EvmAccountProvider.test.ts | 18 +-- .../src/providers/SolAccountProvider.test.ts | 18 +-- .../src/tests/messenger.ts | 71 ++++++---- .../multichain-account-service/src/types.ts | 12 +- yarn.lock | 1 + 8 files changed, 131 insertions(+), 121 deletions(-) diff --git a/packages/multichain-account-service/CHANGELOG.md b/packages/multichain-account-service/CHANGELOG.md index 5c43ecc56a6..259dfe8031f 100644 --- a/packages/multichain-account-service/CHANGELOG.md +++ b/packages/multichain-account-service/CHANGELOG.md @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `multichainAccountGroupCreated` event emitted from wallet level when new groups are created. - Add `multichainAccountGroupUpdated` event emitted from wallet level when groups are synchronized. +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6544](https://github.com/MetaMask/core/pull/6544)) + - Previously, `MultichainAccountService` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [0.7.0] ### Added diff --git a/packages/multichain-account-service/package.json b/packages/multichain-account-service/package.json index ed2d142c934..2a7abd11350 100644 --- a/packages/multichain-account-service/package.json +++ b/packages/multichain-account-service/package.json @@ -53,6 +53,7 @@ "@metamask/keyring-internal-api": "^8.1.0", "@metamask/keyring-snap-client": "^7.0.0", "@metamask/keyring-utils": "^3.1.0", + "@metamask/messenger": "^0.2.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/superstruct": "^3.1.0", diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index 1b2562dda1f..dad09cfe618 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.test.ts @@ -1,6 +1,5 @@ /* eslint-disable jsdoc/require-jsdoc */ -import type { Messenger } from '@metamask/base-controller'; import type { KeyringAccount } from '@metamask/keyring-api'; import { EthAccountType, SolAccountType } from '@metamask/keyring-api'; import { KeyringTypes, type KeyringObject } from '@metamask/keyring-controller'; @@ -27,14 +26,9 @@ import { makeMockAccountProvider, mockAsInternalAccount, setupAccountProvider, + type RootMessenger, } from './tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, - MultichainAccountServiceMessenger, -} from './types'; +import type { MultichainAccountServiceMessenger } from './types'; // Mock providers. jest.mock('./providers/EvmAccountProvider', () => { @@ -80,22 +74,17 @@ function mockAccountProvider( } function setup({ - messenger = getRootMessenger(), + rootMessenger = getRootMessenger(), keyrings = [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], accounts, }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + rootMessenger?: RootMessenger; keyrings?: KeyringObject[]; accounts?: KeyringAccount[]; } = {}): { service: MultichainAccountService; - messenger: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + rootMessenger: RootMessenger; + messenger: MultichainAccountServiceMessenger; mocks: Mocks; } { const mocks: Mocks = { @@ -118,7 +107,7 @@ function setup({ keyrings: mocks.KeyringController.keyrings, })); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'KeyringController:getState', mocks.KeyringController.getState, ); @@ -128,7 +117,7 @@ function setup({ () => accounts, ); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:listMultichainAccounts', mocks.AccountsController.listMultichainAccounts, ); @@ -147,12 +136,20 @@ function setup({ ); } + const messenger = getMultichainAccountServiceMessenger(rootMessenger); + const service = new MultichainAccountService({ - messenger: getMultichainAccountServiceMessenger(messenger), + messenger, }); + service.init(); - return { service, messenger, mocks }; + return { + service, + rootMessenger, + messenger, + mocks, + }; } describe('MultichainAccountService', () => { @@ -365,7 +362,7 @@ describe('MultichainAccountService', () => { it('syncs the appropriate wallet and update reverse mapping on AccountsController:accountAdded', () => { const accounts = [account1, account3]; // No `account2` for now. - const { service, messenger, mocks } = setup({ accounts, keyrings }); + const { service, rootMessenger, mocks } = setup({ accounts, keyrings }); const wallet1 = service.getMultichainAccountWallet({ entropySource: entropy1, @@ -374,7 +371,7 @@ describe('MultichainAccountService', () => { // Now we're adding `account2`. mocks.EvmAccountProvider.accounts = [account1, account2]; - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(account2), ); @@ -405,7 +402,7 @@ describe('MultichainAccountService', () => { .get(); const accounts = [account1]; // No `otherAccount1` for now. - const { service, messenger, mocks } = setup({ accounts, keyrings }); + const { service, rootMessenger, mocks } = setup({ accounts, keyrings }); const wallet1 = service.getMultichainAccountWallet({ entropySource: entropy1, @@ -414,7 +411,7 @@ describe('MultichainAccountService', () => { // Now we're adding `account2`. mocks.EvmAccountProvider.accounts = [account1, otherAccount1]; - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(otherAccount1), ); @@ -446,12 +443,12 @@ describe('MultichainAccountService', () => { .get(); const accounts = [account1]; // No `otherAccount1` for now. - const { messenger, mocks } = setup({ accounts, keyrings }); + const { rootMessenger, messenger, mocks } = setup({ accounts, keyrings }); const publishSpy = jest.spyOn(messenger, 'publish'); // Now we're adding `otherAccount1` to an existing group. mocks.EvmAccountProvider.accounts = [account1, otherAccount1]; - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(otherAccount1), ); @@ -465,7 +462,7 @@ describe('MultichainAccountService', () => { it('creates new detected wallets and update reverse mapping on AccountsController:accountAdded', () => { const accounts = [account1, account2]; // No `account3` for now (associated with "Wallet 2"). - const { service, messenger, mocks } = setup({ + const { service, rootMessenger, mocks } = setup({ accounts, keyrings: [keyring1], }); @@ -483,7 +480,7 @@ describe('MultichainAccountService', () => { // Now we're adding `account3`. mocks.KeyringController.keyrings = [keyring1, keyring2]; mocks.EvmAccountProvider.accounts = [account1, account2, account3]; - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(account3), ); @@ -507,7 +504,10 @@ describe('MultichainAccountService', () => { it('ignores non-BIP-44 accounts on AccountsController:accountAdded', () => { const accounts = [account1]; - const { service, messenger } = setup({ accounts, keyrings }); + const { service, rootMessenger } = setup({ + accounts, + keyrings, + }); const wallet1 = service.getMultichainAccountWallet({ entropySource: entropy1, @@ -517,7 +517,7 @@ describe('MultichainAccountService', () => { expect(oldMultichainAccounts[0].getAccounts()).toHaveLength(1); // Now we're publishing a new account that is not BIP-44 compatible. - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(MOCK_SNAP_ACCOUNT_2), ); @@ -529,7 +529,7 @@ describe('MultichainAccountService', () => { it('syncs the appropriate wallet and update reverse mapping on AccountsController:accountRemoved', () => { const accounts = [account1, account2]; - const { service, messenger, mocks } = setup({ accounts, keyrings }); + const { service, rootMessenger, mocks } = setup({ accounts, keyrings }); const wallet1 = service.getMultichainAccountWallet({ entropySource: entropy1, @@ -538,7 +538,7 @@ describe('MultichainAccountService', () => { // Now we're removing `account2`. mocks.EvmAccountProvider.accounts = [account1]; - messenger.publish('AccountsController:accountRemoved', account2.id); + rootMessenger.publish('AccountsController:accountRemoved', account2.id); expect(wallet1.getMultichainAccountGroups()).toHaveLength(1); const walletAndMultichainAccount2 = service.getAccountContext( @@ -572,7 +572,9 @@ describe('MultichainAccountService', () => { .withGroupIndex(0) .get(); - const { service, messenger } = setup({ accounts: [mockEvmAccount] }); + const { service, messenger } = setup({ + accounts: [mockEvmAccount], + }); const publishSpy = jest.spyOn(messenger, 'publish'); const nextGroup = await service.createNextMultichainAccountGroup({ @@ -626,7 +628,9 @@ describe('MultichainAccountService', () => { .withGroupIndex(0) .get(); - const { service, messenger } = setup({ accounts: [mockEvmAccount] }); + const { service, messenger } = setup({ + accounts: [mockEvmAccount], + }); const publishSpy = jest.spyOn(messenger, 'publish'); const group = await service.createMultichainAccountGroup({ @@ -747,9 +751,9 @@ describe('MultichainAccountService', () => { describe('actions', () => { it('gets a multichain account with MultichainAccountService:getMultichainAccount', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { messenger } = setup({ accounts }); + const { rootMessenger } = setup({ accounts }); - const group = messenger.call( + const group = rootMessenger.call( 'MultichainAccountService:getMultichainAccountGroup', { entropySource: MOCK_HD_KEYRING_1.metadata.id, groupIndex: 0 }, ); @@ -758,9 +762,9 @@ describe('MultichainAccountService', () => { it('gets multichain accounts with MultichainAccountService:getMultichainAccounts', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { messenger } = setup({ accounts }); + const { rootMessenger } = setup({ accounts }); - const groups = messenger.call( + const groups = rootMessenger.call( 'MultichainAccountService:getMultichainAccountGroups', { entropySource: MOCK_HD_KEYRING_1.metadata.id }, ); @@ -769,9 +773,9 @@ describe('MultichainAccountService', () => { it('gets multichain account wallet with MultichainAccountService:getMultichainAccountWallet', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { messenger } = setup({ accounts }); + const { rootMessenger } = setup({ accounts }); - const wallet = messenger.call( + const wallet = rootMessenger.call( 'MultichainAccountService:getMultichainAccountWallet', { entropySource: MOCK_HD_KEYRING_1.metadata.id }, ); @@ -780,9 +784,9 @@ describe('MultichainAccountService', () => { it('gets multichain account wallet with MultichainAccountService:getMultichainAccountWallets', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { messenger } = setup({ accounts }); + const { rootMessenger } = setup({ accounts }); - const wallets = messenger.call( + const wallets = rootMessenger.call( 'MultichainAccountService:getMultichainAccountWallets', ); expect(wallets.length).toBeGreaterThan(0); @@ -790,9 +794,9 @@ describe('MultichainAccountService', () => { it('create the next multichain account group with MultichainAccountService:createNextMultichainAccountGroup', async () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { messenger } = setup({ accounts }); + const { rootMessenger } = setup({ accounts }); - const nextGroup = await messenger.call( + const nextGroup = await rootMessenger.call( 'MultichainAccountService:createNextMultichainAccountGroup', { entropySource: MOCK_HD_KEYRING_1.metadata.id }, ); @@ -803,9 +807,9 @@ describe('MultichainAccountService', () => { it('creates a multichain account group with MultichainAccountService:createMultichainAccountGroup', async () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { messenger } = setup({ accounts }); + const { rootMessenger } = setup({ accounts }); - const firstGroup = await messenger.call( + const firstGroup = await rootMessenger.call( 'MultichainAccountService:createMultichainAccountGroup', { entropySource: MOCK_HD_KEYRING_1.metadata.id, @@ -827,11 +831,11 @@ describe('MultichainAccountService', () => { .withEntropySource(MOCK_HD_KEYRING_2.metadata.id) .withGroupIndex(0) .get(); - const { messenger, mocks } = setup({ + const { rootMessenger, mocks } = setup({ accounts: [mockEvmAccount1, mockSolAccount1], }); - await messenger.call( + await rootMessenger.call( 'MultichainAccountService:alignWallet', MOCK_HD_KEYRING_1.metadata.id, ); @@ -852,11 +856,11 @@ describe('MultichainAccountService', () => { .withEntropySource(MOCK_HD_KEYRING_2.metadata.id) .withGroupIndex(0) .get(); - const { messenger, mocks } = setup({ + const { rootMessenger, mocks } = setup({ accounts: [mockEvmAccount1, mockSolAccount1], }); - await messenger.call('MultichainAccountService:alignWallets'); + await rootMessenger.call('MultichainAccountService:alignWallets'); expect(mocks.EvmAccountProvider.createAccounts).toHaveBeenCalledWith({ entropySource: MOCK_HD_KEYRING_2.metadata.id, @@ -869,17 +873,19 @@ describe('MultichainAccountService', () => { }); it('sets basic functionality with MultichainAccountService:setBasicFunctionality', async () => { - const { messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1] }); + const { rootMessenger } = setup({ + accounts: [MOCK_HD_ACCOUNT_1], + }); // This tests the action handler registration expect( - await messenger.call( + await rootMessenger.call( 'MultichainAccountService:setBasicFunctionality', true, ), ).toBeUndefined(); expect( - await messenger.call( + await rootMessenger.call( 'MultichainAccountService:setBasicFunctionality', false, ), @@ -887,11 +893,11 @@ describe('MultichainAccountService', () => { }); it('gets alignment progress with MultichainAccountService:getIsAlignmentInProgress', () => { - const { messenger } = setup({ + const { rootMessenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], }); - const isInProgress = messenger.call( + const isInProgress = rootMessenger.call( 'MultichainAccountService:getIsAlignmentInProgress', ); @@ -920,11 +926,13 @@ describe('MultichainAccountService', () => { let solProvider: SolAccountProvider; beforeEach(() => { - const { messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1] }); + const { rootMessenger } = setup({ + accounts: [MOCK_HD_ACCOUNT_1], + }); // Create actual SolAccountProvider instance for wrapping solProvider = new SolAccountProvider( - getMultichainAccountServiceMessenger(messenger), + getMultichainAccountServiceMessenger(rootMessenger), ); // Spy on the provider methods @@ -935,7 +943,7 @@ describe('MultichainAccountService', () => { jest.spyOn(solProvider, 'isAccountCompatible'); wrapper = new AccountProviderWrapper( - getMultichainAccountServiceMessenger(messenger), + getMultichainAccountServiceMessenger(rootMessenger), solProvider, ); }); diff --git a/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts b/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts index b75b7e80221..b4efb05cbe3 100644 --- a/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts @@ -1,4 +1,3 @@ -import type { Messenger } from '@metamask/base-controller'; import { type KeyringMetadata } from '@metamask/keyring-controller'; import type { EthKeyring, @@ -17,13 +16,8 @@ import { MOCK_HD_ACCOUNT_2, MOCK_HD_KEYRING_1, MockAccountBuilder, + type RootMessenger, } from '../tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from '../types'; class MockEthKeyring implements EthKeyring { readonly type = 'MockEthKeyring'; @@ -90,17 +84,11 @@ function setup({ messenger = getRootMessenger(), accounts = [], }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[]; } = {}): { provider: EvmAccountProvider; - messenger: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger: RootMessenger; keyring: MockEthKeyring; mocks: { getAccountByAddress: jest.Mock; diff --git a/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts b/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts index 52720d5d62e..7b8a9167512 100644 --- a/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/SolAccountProvider.test.ts @@ -1,5 +1,4 @@ import { isBip44Account } from '@metamask/account-api'; -import type { Messenger } from '@metamask/base-controller'; import type { SnapKeyring } from '@metamask/eth-snap-keyring'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { @@ -17,13 +16,8 @@ import { MOCK_SOL_ACCOUNT_1, MOCK_SOL_DISCOVERED_ACCOUNT_1, MockAccountBuilder, + type RootMessenger, } from '../tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from '../types'; class MockSolanaKeyring { readonly type = 'MockSolanaKeyring'; @@ -94,17 +88,11 @@ function setup({ messenger = getRootMessenger(), accounts = [], }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[]; } = {}): { provider: AccountProviderWrapper; - messenger: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger: RootMessenger; keyring: MockSolanaKeyring; mocks: { handleRequest: jest.Mock; diff --git a/packages/multichain-account-service/src/tests/messenger.ts b/packages/multichain-account-service/src/tests/messenger.ts index 92922839293..cb3c40903a9 100644 --- a/packages/multichain-account-service/src/tests/messenger.ts +++ b/packages/multichain-account-service/src/tests/messenger.ts @@ -1,42 +1,57 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, - MultichainAccountServiceMessenger, -} from '../types'; +import type { MultichainAccountServiceMessenger } from '../types'; + +type AllMultichainAccountServiceActions = + MessengerActions; + +type AllMultichainAccountServiceEvents = + MessengerEvents; + +export type RootMessenger = Messenger< + MockAnyNamespace, + AllMultichainAccountServiceActions, + AllMultichainAccountServiceEvents +>; /** - * Creates a new root messenger instance for testing. + * Creates and returns a root messenger for testing * - * @returns A new Messenger instance. + * @returns A messenger instance */ -export function getRootMessenger() { - return new Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >(); +export function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); } /** * Retrieves a restricted messenger for the MultichainAccountService. * - * @param messenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). + * @param rootMessenger - The root messenger instance. Defaults to a new Messenger created by getRootMessenger(). * @returns The restricted messenger for the MultichainAccountService. */ export function getMultichainAccountServiceMessenger( - messenger: ReturnType, + rootMessenger: RootMessenger, ): MultichainAccountServiceMessenger { - return messenger.getRestricted({ - name: 'MultichainAccountService', - allowedEvents: [ - 'KeyringController:stateChange', - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - ], - allowedActions: [ + const messenger = new Messenger< + 'MultichainAccountService', + AllMultichainAccountServiceActions, + AllMultichainAccountServiceEvents, + RootMessenger + >({ + namespace: 'MultichainAccountService', + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: [ 'AccountsController:getAccount', 'AccountsController:getAccountByAddress', 'AccountsController:listMultichainAccounts', @@ -46,5 +61,11 @@ export function getMultichainAccountServiceMessenger( 'NetworkController:findNetworkClientIdByChainId', 'NetworkController:getNetworkClientById', ], + events: [ + 'KeyringController:stateChange', + 'AccountsController:accountAdded', + 'AccountsController:accountRemoved', + ], }); + return messenger; } diff --git a/packages/multichain-account-service/src/types.ts b/packages/multichain-account-service/src/types.ts index 3249f063772..ed0a1990f64 100644 --- a/packages/multichain-account-service/src/types.ts +++ b/packages/multichain-account-service/src/types.ts @@ -9,13 +9,13 @@ import type { AccountsControllerGetAccountByAddressAction, AccountsControllerListMultichainAccountsAction, } from '@metamask/accounts-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { KeyringAccount } from '@metamask/keyring-api'; import type { KeyringControllerGetStateAction, KeyringControllerStateChangeEvent, KeyringControllerWithKeyringAction, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerFindNetworkClientIdByChainIdAction, NetworkControllerGetNetworkClientByIdAction, @@ -115,7 +115,7 @@ export type MultichainAccountServiceEvents = * All actions registered by other modules that {@link MultichainAccountService} * calls. */ -export type AllowedActions = +type AllowedActions = | AccountsControllerListMultichainAccountsAction | AccountsControllerGetAccountAction | AccountsControllerGetAccountByAddressAction @@ -129,7 +129,7 @@ export type AllowedActions = * All events published by other modules that {@link MultichainAccountService} * subscribes to. */ -export type AllowedEvents = +type AllowedEvents = | KeyringControllerStateChangeEvent | AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent; @@ -138,10 +138,8 @@ export type AllowedEvents = * The messenger restricted to actions and events that * {@link MultichainAccountService} needs to access. */ -export type MultichainAccountServiceMessenger = RestrictedMessenger< +export type MultichainAccountServiceMessenger = Messenger< 'MultichainAccountService', MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + MultichainAccountServiceEvents | AllowedEvents >; diff --git a/yarn.lock b/yarn.lock index 0cf2a57b624..b81d3e6dc76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3829,6 +3829,7 @@ __metadata: "@metamask/keyring-internal-api": "npm:^8.1.0" "@metamask/keyring-snap-client": "npm:^7.0.0" "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^0.2.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" From 3480c41716f9cc9c5201ffd20713fa61876d6a17 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 10 Sep 2025 16:05:47 +0200 Subject: [PATCH 068/247] refactor: migrate {AbstractMessage,DecryptMessage,EncryptionPublicKey}Manager to @metamask/messenger --- packages/message-manager/CHANGELOG.md | 2 ++ packages/message-manager/package.json | 1 + .../src/AbstractMessageManager.test.ts | 10 ++----- .../src/AbstractMessageManager.ts | 28 ++++++------------- .../src/DecryptMessageManager.ts | 14 ++++------ .../src/EncryptionPublicKeyManager.ts | 14 ++++------ packages/message-manager/src/index.ts | 6 +++- packages/message-manager/tsconfig.build.json | 3 +- packages/message-manager/tsconfig.json | 3 +- yarn.lock | 1 + 10 files changed, 35 insertions(+), 47 deletions(-) diff --git a/packages/message-manager/CHANGELOG.md b/packages/message-manager/CHANGELOG.md index 34ddd65318c..1c3a32a8326 100644 --- a/packages/message-manager/CHANGELOG.md +++ b/packages/message-manager/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6545](https://github.com/MetaMask/core/pull/6545)) + - Previously, `AbstractMessageManager`, `DecryptMessageManager` and `EncryptionPublicKeyManager` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - **BREAKING:** `AbstractMessageManager` now expects a `Name extends string` generic parameter to define the name of the message manager ([#6469](https://github.com/MetaMask/core/pull/6469)) - The type is used as namespace for `BaseController` and `Messenger` events and actions. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.3.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465)) diff --git a/packages/message-manager/package.json b/packages/message-manager/package.json index 06a7fb2660c..5bcff0d83d2 100644 --- a/packages/message-manager/package.json +++ b/packages/message-manager/package.json @@ -50,6 +50,7 @@ "@metamask/base-controller": "^8.3.0", "@metamask/controller-utils": "^11.12.0", "@metamask/eth-sig-util": "^8.2.0", + "@metamask/messenger": "^0.2.0", "@metamask/utils": "^11.4.2", "@types/uuid": "^8.3.0", "jsonschema": "^1.4.1", diff --git a/packages/message-manager/src/AbstractMessageManager.test.ts b/packages/message-manager/src/AbstractMessageManager.test.ts index 6914beccdd1..01e1b9e1729 100644 --- a/packages/message-manager/src/AbstractMessageManager.test.ts +++ b/packages/message-manager/src/AbstractMessageManager.test.ts @@ -1,5 +1,5 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import { ApprovalType } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { AbstractMessage, @@ -68,13 +68,7 @@ const MOCK_MESSENGER = { publish: jest.fn(), registerActionHandler: jest.fn(), registerInitialEventPayload: jest.fn(), -} as unknown as RestrictedMessenger< - 'TestManager', - never, - never, - string, - string ->; +} as unknown as Messenger<'TestManager', never, never>; const MOCK_INITIAL_OPTIONS = { additionalFinishStatuses: undefined, diff --git a/packages/message-manager/src/AbstractMessageManager.ts b/packages/message-manager/src/AbstractMessageManager.ts index 172a1230766..cee419efef5 100644 --- a/packages/message-manager/src/AbstractMessageManager.ts +++ b/packages/message-manager/src/AbstractMessageManager.ts @@ -1,10 +1,10 @@ -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; +import type { ApprovalType } from '@metamask/controller-utils'; import type { ActionConstraint, EventConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; -import type { ApprovalType } from '@metamask/controller-utils'; + Messenger, +} from '@metamask/messenger'; import type { Json } from '@metamask/utils'; // This package purposefully relies on Node's EventEmitter module. // eslint-disable-next-line import-x/no-nodejs-modules @@ -127,13 +127,7 @@ export type AbstractMessageManagerOptions< Event extends EventConstraint, > = { additionalFinishStatuses?: string[]; - messenger: RestrictedMessenger< - Name, - Action, - Event | UpdateBadgeEvent, - string, - string - >; + messenger: Messenger>; name: Name; securityProviderRequest?: SecurityProviderRequest; state?: MessageManagerState; @@ -152,13 +146,7 @@ export abstract class AbstractMessageManager< > extends BaseController< Name, MessageManagerState, - RestrictedMessenger< - Name, - Action, - Event | UpdateBadgeEvent, - string, - string - > + Messenger> > { protected messages: Message[]; @@ -190,7 +178,7 @@ export abstract class AbstractMessageManager< } /** - * Adds request props to the messsage params and returns a new messageParams object. + * Adds request props to the message params and returns a new messageParams object. * @param messageParams - The messageParams to add the request props to. * @param req - The original request object. * @returns The messageParams with the request props added. @@ -247,7 +235,7 @@ export abstract class AbstractMessageManager< state.unapprovedMessagesCount = this.getUnapprovedMessagesCount(); }); if (emitUpdateBadge) { - this.messagingSystem.publish(`${this.name}:updateBadge`); + this.messenger.publish(`${this.name}:updateBadge` as const); } } diff --git a/packages/message-manager/src/DecryptMessageManager.ts b/packages/message-manager/src/DecryptMessageManager.ts index 43399dca73c..c1a96581b42 100644 --- a/packages/message-manager/src/DecryptMessageManager.ts +++ b/packages/message-manager/src/DecryptMessageManager.ts @@ -1,9 +1,9 @@ +import { ApprovalType } from '@metamask/controller-utils'; import type { ActionConstraint, EventConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { ApprovalType } from '@metamask/controller-utils'; + Messenger, +} from '@metamask/messenger'; import type { AbstractMessage, @@ -30,14 +30,12 @@ export type DecryptMessageManagerUpdateBadgeEvent = { payload: []; }; -export type DecryptMessageManagerMessenger = RestrictedMessenger< +export type DecryptMessageManagerMessenger = Messenger< typeof managerName, ActionConstraint, | EventConstraint | DecryptMessageManagerUnapprovedMessageAddedEvent - | DecryptMessageManagerUpdateBadgeEvent, - string, - string + | DecryptMessageManagerUpdateBadgeEvent >; type DecryptMessageManagerOptions = { @@ -194,7 +192,7 @@ export class DecryptMessageManager extends AbstractMessageManager< const messageId = messageData.id; await this.addMessage(messageData); - this.messagingSystem.publish(`${managerName}:unapprovedMessage`, { + this.messenger.publish(`${managerName}:unapprovedMessage`, { ...updatedMessageParams, metamaskId: messageId, }); diff --git a/packages/message-manager/src/EncryptionPublicKeyManager.ts b/packages/message-manager/src/EncryptionPublicKeyManager.ts index 8df1a608906..cfff6f3b7b8 100644 --- a/packages/message-manager/src/EncryptionPublicKeyManager.ts +++ b/packages/message-manager/src/EncryptionPublicKeyManager.ts @@ -1,9 +1,9 @@ +import { ApprovalType } from '@metamask/controller-utils'; import type { ActionConstraint, EventConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { ApprovalType } from '@metamask/controller-utils'; + Messenger, +} from '@metamask/messenger'; import type { AbstractMessage, @@ -31,14 +31,12 @@ export type EncryptionPublicKeyManagerUpdateBadgeEvent = { payload: []; }; -export type EncryptionPublicKeyManagerMessenger = RestrictedMessenger< +export type EncryptionPublicKeyManagerMessenger = Messenger< typeof managerName, ActionConstraint, | EventConstraint | EncryptionPublicKeyManagerUnapprovedMessageAddedEvent - | EncryptionPublicKeyManagerUpdateBadgeEvent, - string, - string + | EncryptionPublicKeyManagerUpdateBadgeEvent >; type EncryptionPublicKeyManagerOptions = { @@ -185,7 +183,7 @@ export class EncryptionPublicKeyManager extends AbstractMessageManager< const messageId = messageData.id; await this.addMessage(messageData); - this.messagingSystem.publish(`${this.name}:unapprovedMessage`, { + this.messenger.publish(`${this.name}:unapprovedMessage` as const, { ...updatedMessageParams, metamaskId: messageId, }); diff --git a/packages/message-manager/src/index.ts b/packages/message-manager/src/index.ts index 27723685451..fae5514ea32 100644 --- a/packages/message-manager/src/index.ts +++ b/packages/message-manager/src/index.ts @@ -1,4 +1,8 @@ export * from './AbstractMessageManager'; export * from './EncryptionPublicKeyManager'; export * from './DecryptMessageManager'; -export * from './types'; +export type { + SignTypedDataMessageV3V4, + PersonalMessageParams, + TypedMessageParams, +} from './types'; diff --git a/packages/message-manager/tsconfig.build.json b/packages/message-manager/tsconfig.build.json index bbfe057a207..5a5c9e2326a 100644 --- a/packages/message-manager/tsconfig.build.json +++ b/packages/message-manager/tsconfig.build.json @@ -7,7 +7,8 @@ }, "references": [ { "path": "../base-controller/tsconfig.build.json" }, - { "path": "../controller-utils/tsconfig.build.json" } + { "path": "../controller-utils/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/message-manager/tsconfig.json b/packages/message-manager/tsconfig.json index 7ee9852347a..dfd15011442 100644 --- a/packages/message-manager/tsconfig.json +++ b/packages/message-manager/tsconfig.json @@ -5,7 +5,8 @@ }, "references": [ { "path": "../base-controller" }, - { "path": "../controller-utils" } + { "path": "../controller-utils" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 0cf2a57b624..3c000d6fac4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3777,6 +3777,7 @@ __metadata: "@metamask/base-controller": "npm:^8.3.0" "@metamask/controller-utils": "npm:^11.12.0" "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.2.0" "@metamask/utils": "npm:^11.4.2" "@types/jest": "npm:^27.4.1" "@types/uuid": "npm:^8.3.0" From 7db8785075adec8d467a835ac397cda606ee9373 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 11:33:32 +0200 Subject: [PATCH 069/247] update messenger package --- packages/accounts-controller/package.json | 2 +- yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 50badec5d9e..e96bd39abfd 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -53,7 +53,7 @@ "@metamask/keyring-api": "^21.0.0", "@metamask/keyring-internal-api": "^9.0.0", "@metamask/keyring-utils": "^3.1.0", - "@metamask/messenger": "^0.1.0", + "@metamask/messenger": "^0.2.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/superstruct": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 5743614eb08..f4d5f2504b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,7 +2456,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.1.0" "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-utils": "npm:^3.1.0" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.2.0" "@metamask/network-controller": "npm:^24.1.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" @@ -3810,6 +3810,13 @@ __metadata: languageName: unknown linkType: soft +"@metamask/messenger@npm:^0.2.0": + version: 0.2.0 + resolution: "@metamask/messenger@npm:0.2.0" + checksum: 10/48f682d9cde1208fbda0936022dea37acc3828cc221203b5f917df25c131d9a250dc5e86e9263f5dba8ee7c05adc6752a68dfb57da7d297f95f38b052f4fe5c1 + languageName: node + linkType: hard + "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From bff153674e39c74f9a4cd8263628134d9e36b760 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 11:33:36 +0200 Subject: [PATCH 070/247] update tsconfigs --- packages/accounts-controller/tsconfig.build.json | 3 ++- packages/accounts-controller/tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/accounts-controller/tsconfig.build.json b/packages/accounts-controller/tsconfig.build.json index 2ccd968d36d..d95a2cae72e 100644 --- a/packages/accounts-controller/tsconfig.build.json +++ b/packages/accounts-controller/tsconfig.build.json @@ -11,7 +11,8 @@ "path": "../base-controller/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, - { "path": "../network-controller/tsconfig.build.json" } + { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/accounts-controller/tsconfig.json b/packages/accounts-controller/tsconfig.json index 12cd20ecb5c..d568399237d 100644 --- a/packages/accounts-controller/tsconfig.json +++ b/packages/accounts-controller/tsconfig.json @@ -10,7 +10,8 @@ { "path": "../keyring-controller" }, - { "path": "../network-controller" } + { "path": "../network-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src", "src/tests"] } From 814aa6b47be3bb158bbd418e7bfb08362ba906f9 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 11:37:23 +0200 Subject: [PATCH 071/247] update tsconfigs --- packages/address-book-controller/tsconfig.build.json | 3 ++- packages/address-book-controller/tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/address-book-controller/tsconfig.build.json b/packages/address-book-controller/tsconfig.build.json index bbfe057a207..5a5c9e2326a 100644 --- a/packages/address-book-controller/tsconfig.build.json +++ b/packages/address-book-controller/tsconfig.build.json @@ -7,7 +7,8 @@ }, "references": [ { "path": "../base-controller/tsconfig.build.json" }, - { "path": "../controller-utils/tsconfig.build.json" } + { "path": "../controller-utils/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/address-book-controller/tsconfig.json b/packages/address-book-controller/tsconfig.json index 7ee9852347a..dfd15011442 100644 --- a/packages/address-book-controller/tsconfig.json +++ b/packages/address-book-controller/tsconfig.json @@ -5,7 +5,8 @@ }, "references": [ { "path": "../base-controller" }, - { "path": "../controller-utils" } + { "path": "../controller-utils" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } From cde42fadf3e9d0dd4c49b5ab3dcfb847c5a21b7a Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 13:54:58 +0200 Subject: [PATCH 072/247] use `MockAnyNamespace` namespace --- .../src/AddressBookController.test.ts | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/address-book-controller/src/AddressBookController.test.ts b/packages/address-book-controller/src/AddressBookController.test.ts index d03719abe70..1b8e4ed5fb3 100644 --- a/packages/address-book-controller/src/AddressBookController.test.ts +++ b/packages/address-book-controller/src/AddressBookController.test.ts @@ -1,13 +1,17 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { toHex } from '@metamask/controller-utils'; -import { Messenger } from '@metamask/messenger'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import type { - AddressBookControllerActions, - AddressBookControllerEvents, - AddressBookControllerContactUpdatedEvent, AddressBookControllerContactDeletedEvent, + AddressBookControllerMessenger, } from './AddressBookController'; import { AddressBookController, @@ -15,21 +19,32 @@ import { controllerName, } from './AddressBookController'; +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; + +/** + * Creates a new root messenger instance for testing. + * + * @returns A new Messenger instance. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); +} + /** * Helper function to create test fixtures * * @returns Test fixtures including messenger, controller, and event listeners */ function arrangeMocks() { - const rootMessenger = new Messenger< - 'Root', - AddressBookControllerActions, - AddressBookControllerEvents - >({ namespace: 'Root' }); + const rootMessenger = getRootMessenger(); const addressBookControllerMessenger = new Messenger< typeof controllerName, - AddressBookControllerActions, - AddressBookControllerEvents, + AllActions, + AllEvents, typeof rootMessenger >({ namespace: controllerName, @@ -45,7 +60,7 @@ function arrangeMocks() { // Subscribe to events rootMessenger.subscribe( - 'AddressBookController:contactUpdated' as AddressBookControllerContactUpdatedEvent['type'], + 'AddressBookController:contactUpdated', contactUpdatedListener, ); rootMessenger.subscribe( @@ -633,7 +648,7 @@ describe('AddressBookController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); From 9845fb4f2a18d9ea05a939a718447e3559958ddb Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 13:55:12 +0200 Subject: [PATCH 073/247] rename `anonymout` to `includeInDebugSnapshot` --- packages/address-book-controller/src/AddressBookController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/address-book-controller/src/AddressBookController.ts b/packages/address-book-controller/src/AddressBookController.ts index fdf62deba30..13d00a1561b 100644 --- a/packages/address-book-controller/src/AddressBookController.ts +++ b/packages/address-book-controller/src/AddressBookController.ts @@ -1,7 +1,7 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { BaseController } from '@metamask/base-controller/next'; import { normalizeEnsName, @@ -150,7 +150,7 @@ const addressBookControllerMetadata = { addressBook: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; From 74bd264a2b453ffd00d5f4bc5f14251832d815e0 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 13:58:28 +0200 Subject: [PATCH 074/247] update messenger package to `^0.3.0` --- packages/accounts-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index e96bd39abfd..b0dfa635ab7 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -53,7 +53,7 @@ "@metamask/keyring-api": "^21.0.0", "@metamask/keyring-internal-api": "^9.0.0", "@metamask/keyring-utils": "^3.1.0", - "@metamask/messenger": "^0.2.0", + "@metamask/messenger": "^0.3.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/superstruct": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index f4d5f2504b5..51ee348dee3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,7 +2456,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.1.0" "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-utils": "npm:^3.1.0" - "@metamask/messenger": "npm:^0.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.1.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" @@ -3810,13 +3810,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.2.0": - version: 0.2.0 - resolution: "@metamask/messenger@npm:0.2.0" - checksum: 10/48f682d9cde1208fbda0936022dea37acc3828cc221203b5f917df25c131d9a250dc5e86e9263f5dba8ee7c05adc6752a68dfb57da7d297f95f38b052f4fe5c1 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From 17359ad0da88a88579008e5e0c824f4882aae704 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 14:13:44 +0200 Subject: [PATCH 075/247] rename `anonymous` to `includeInDebugSnapshot` --- .../accounts-controller/src/AccountsController.test.ts | 10 +++++----- packages/accounts-controller/src/AccountsController.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 46661f50cee..97affea7864 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -3922,19 +3922,19 @@ describe('AccountsController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { - const { accountsController: controller } = setupAccountsController(); + const { accountsController: controller } = setupAccountsController({}); expect( deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); it('includes expected state in state logs', () => { - const { accountsController: controller } = setupAccountsController(); + const { accountsController: controller } = setupAccountsController({}); expect( deriveStateFromMetadata( @@ -3953,7 +3953,7 @@ describe('AccountsController', () => { }); it('persists expected state', () => { - const { accountsController: controller } = setupAccountsController(); + const { accountsController: controller } = setupAccountsController({}); expect( deriveStateFromMetadata( @@ -3972,7 +3972,7 @@ describe('AccountsController', () => { }); it('exposes expected state to UI', () => { - const { accountsController: controller } = setupAccountsController(); + const { accountsController: controller } = setupAccountsController({}); expect( deriveStateFromMetadata( diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 91ef89f4c1e..b5206c47626 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -221,7 +221,7 @@ const accountsControllerMetadata = { internalAccounts: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; From 53e3844cecff25b0475cc6c0d862ff695e4c0149 Mon Sep 17 00:00:00 2001 From: Michele Esposito <34438276+mikesposito@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:54:38 +0200 Subject: [PATCH 076/247] Update packages/accounts-controller/src/AccountsController.test.ts Co-authored-by: Elliot Winkler --- packages/accounts-controller/src/AccountsController.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 97affea7864..1686a4ec3fe 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -243,7 +243,7 @@ function buildMessenger(): RootMessenger { /** * Builds a messenger for the AccountsController. * - * @param rootMessenger - The parent messenger. + * @param rootMessenger - The root messenger. * @returns The messenger for AccountsController. */ function buildAccountsControllerMessenger(rootMessenger = buildMessenger()) { From 86a9e0bf41f5415d69265d1b3a528febb051ef55 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 19:57:16 +0200 Subject: [PATCH 077/247] apply suggestion from @mcmire --- packages/accounts-controller/src/AccountsController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 1686a4ec3fe..ecb620c4f76 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -232,9 +232,9 @@ function setExpectedLastSelectedAsAny( } /** - * Builds a new instance of the Root Messenger. + * Builds a new instance of the root messenger. * - * @returns A new instance of the Root Messenger. + * @returns A new instance of the root messenger. */ function buildMessenger(): RootMessenger { return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); From 2258bbc6ee88c1f627539c5cb6221250012ee527 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:06:16 +0200 Subject: [PATCH 078/247] update messenger to ^0.3.0 --- packages/address-book-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/address-book-controller/package.json b/packages/address-book-controller/package.json index 09b8b9caade..06c1d86a9d1 100644 --- a/packages/address-book-controller/package.json +++ b/packages/address-book-controller/package.json @@ -49,7 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", - "@metamask/messenger": "^0.2.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index dc693de41dc..b4c36a04d21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2503,7 +2503,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" - "@metamask/messenger": "npm:^0.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" @@ -3810,13 +3810,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.2.0": - version: 0.2.0 - resolution: "@metamask/messenger@npm:0.2.0" - checksum: 10/48f682d9cde1208fbda0936022dea37acc3828cc221203b5f917df25c131d9a250dc5e86e9263f5dba8ee7c05adc6752a68dfb57da7d297f95f38b052f4fe5c1 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From 2ef2292999f1b2abddf19b1c3db845ee69cc612b Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:12:01 +0200 Subject: [PATCH 079/247] update messenger to ^0.3.0 --- packages/announcement-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/announcement-controller/package.json b/packages/announcement-controller/package.json index 90c5d0a40ba..40a36e3b398 100644 --- a/packages/announcement-controller/package.json +++ b/packages/announcement-controller/package.json @@ -48,7 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", - "@metamask/messenger": "^0.1.0" + "@metamask/messenger": "^0.3.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/yarn.lock b/yarn.lock index 91f11402ca5..d220931f724 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2520,7 +2520,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" @@ -3810,13 +3810,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From 2b98d175040043bfd6056f73f0acf97a9fb68c42 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:14:08 +0200 Subject: [PATCH 080/247] update tsconfig files and readme --- README.md | 1 + packages/announcement-controller/tsconfig.build.json | 5 ++++- packages/announcement-controller/tsconfig.json | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eab983a1dc..5d3f9213f06 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ linkStyle default opacity:0.5 address_book_controller --> base_controller; address_book_controller --> controller_utils; announcement_controller --> base_controller; + announcement_controller --> messenger; app_metadata_controller --> base_controller; approval_controller --> base_controller; assets_controllers --> base_controller; diff --git a/packages/announcement-controller/tsconfig.build.json b/packages/announcement-controller/tsconfig.build.json index e5fd7422b9a..85cca3e8cdd 100644 --- a/packages/announcement-controller/tsconfig.build.json +++ b/packages/announcement-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" } + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/announcement-controller/tsconfig.json b/packages/announcement-controller/tsconfig.json index 34354c4b09d..8af2380112d 100644 --- a/packages/announcement-controller/tsconfig.json +++ b/packages/announcement-controller/tsconfig.json @@ -3,6 +3,9 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../base-controller" }], + "references": [ + { "path": "../base-controller" }, + { "path": "../messenger" } + ], "include": ["../../types", "./src"] } From 3f0ecfc866b807332c31da7475cee1fdfb2a8f1f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:15:13 +0200 Subject: [PATCH 081/247] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4eab983a1dc..4238ccc952d 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ linkStyle default opacity:0.5 accounts_controller --> network_controller; address_book_controller --> base_controller; address_book_controller --> controller_utils; + address_book_controller --> messenger; announcement_controller --> base_controller; app_metadata_controller --> base_controller; approval_controller --> base_controller; From 11a662db9ebba6b1f5f026bc2db041361f5aba05 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:15:44 +0200 Subject: [PATCH 082/247] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4eab983a1dc..6c1fad2424a 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ linkStyle default opacity:0.5 account_tree_controller --> multichain_account_service; account_tree_controller --> profile_sync_controller; accounts_controller --> base_controller; + accounts_controller --> messenger; accounts_controller --> controller_utils; accounts_controller --> keyring_controller; accounts_controller --> network_controller; From cdb9f2a9abb04c3624fdda94c65f5abdffbaecb7 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:18:50 +0200 Subject: [PATCH 083/247] rename `anonymous` to `includeInDebugSnapshot` --- .../src/AnnouncementController.test.ts | 12 ++++++------ .../src/AnnouncementController.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/announcement-controller/src/AnnouncementController.test.ts b/packages/announcement-controller/src/AnnouncementController.test.ts index 704cb71b2fe..3de812b385d 100644 --- a/packages/announcement-controller/src/AnnouncementController.test.ts +++ b/packages/announcement-controller/src/AnnouncementController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { Messenger } from '@metamask/messenger'; import type { @@ -174,7 +174,7 @@ describe('announcement controller', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); @@ -182,7 +182,7 @@ describe('announcement controller', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -204,7 +204,7 @@ describe('announcement controller', () => { it('includes expected state in state logs', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); @@ -234,7 +234,7 @@ describe('announcement controller', () => { it('persists expected state', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); @@ -264,7 +264,7 @@ describe('announcement controller', () => { it('exposes expected state to UI', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); diff --git a/packages/announcement-controller/src/AnnouncementController.ts b/packages/announcement-controller/src/AnnouncementController.ts index 57f05b1db87..f98cca82034 100644 --- a/packages/announcement-controller/src/AnnouncementController.ts +++ b/packages/announcement-controller/src/AnnouncementController.ts @@ -64,7 +64,7 @@ const metadata: StateMetadata = { announcements: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; From fd010ee97e34db7dd84aa4d6df411eaba4ef458a Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:21:37 +0200 Subject: [PATCH 084/247] update messenger to 0.3.0 --- packages/app-metadata-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/app-metadata-controller/package.json b/packages/app-metadata-controller/package.json index 768003a6343..145f1027c54 100644 --- a/packages/app-metadata-controller/package.json +++ b/packages/app-metadata-controller/package.json @@ -48,7 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", - "@metamask/messenger": "^0.1.0" + "@metamask/messenger": "^0.3.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/yarn.lock b/yarn.lock index 39c8516bf53..5d4452430f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2543,7 +2543,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" @@ -3810,13 +3810,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From 5a79b84988885a3260ad78cf089ee531bb85963b Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:23:15 +0200 Subject: [PATCH 085/247] rename `anonymous` to `includeInDebugSnapshot` --- .../src/AppMetadataController.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/app-metadata-controller/src/AppMetadataController.ts b/packages/app-metadata-controller/src/AppMetadataController.ts index b7be4ba2fb3..ffd346aea48 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.ts @@ -2,7 +2,7 @@ import type { StateMetadata, ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { BaseController } from '@metamask/base-controller/next'; import type { Messenger } from '@metamask/messenger'; @@ -103,25 +103,25 @@ const controllerMetadata = { currentAppVersion: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, previousAppVersion: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, previousMigrationVersion: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, currentMigrationVersion: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, } satisfies StateMetadata; From 5b0c7c8fabe9575f74a25e2b3f673cfc31f28374 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:24:38 +0200 Subject: [PATCH 086/247] update tsconfig and README --- README.md | 1 + packages/app-metadata-controller/tsconfig.build.json | 5 ++++- packages/app-metadata-controller/tsconfig.json | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eab983a1dc..d5810999c32 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ linkStyle default opacity:0.5 address_book_controller --> controller_utils; announcement_controller --> base_controller; app_metadata_controller --> base_controller; + app_metadata_controller --> messenger; approval_controller --> base_controller; assets_controllers --> base_controller; assets_controllers --> controller_utils; diff --git a/packages/app-metadata-controller/tsconfig.build.json b/packages/app-metadata-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 100644 --- a/packages/app-metadata-controller/tsconfig.build.json +++ b/packages/app-metadata-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/app-metadata-controller/tsconfig.json b/packages/app-metadata-controller/tsconfig.json index 34354c4b09d..8af2380112d 100644 --- a/packages/app-metadata-controller/tsconfig.json +++ b/packages/app-metadata-controller/tsconfig.json @@ -3,6 +3,9 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../base-controller" }], + "references": [ + { "path": "../base-controller" }, + { "path": "../messenger" } + ], "include": ["../../types", "./src"] } From ededeb6b935fb84c78424b6204c9ea94cb1cfc8a Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:28:41 +0200 Subject: [PATCH 087/247] use `MockAnyNamespace` --- .../src/AppMetadataController.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/app-metadata-controller/src/AppMetadataController.test.ts b/packages/app-metadata-controller/src/AppMetadataController.test.ts index b19b43dbcb3..25722e514fd 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.test.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.test.ts @@ -1,5 +1,9 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; -import { Messenger } from '@metamask/messenger'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, +} from '@metamask/messenger'; import { AppMetadataController, @@ -223,10 +227,10 @@ function withController( const [options = {}, fn] = args.length === 2 ? args : [{}, args[0]]; const rootMessenger = new Messenger< - 'Root', + MockAnyNamespace, AppMetadataControllerActions, AppMetadataControllerEvents - >({ namespace: 'Root' }); + >({ namespace: MOCK_ANY_NAMESPACE }); const appMetadataControllerMessenger = new Messenger< 'AppMetadataController', From 6dc0cbacc7e9d618d9de9051a715bfcbafb1b5ab Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 22 Sep 2025 20:31:10 +0200 Subject: [PATCH 088/247] use `MockAnyNamespace` --- .../src/AnnouncementController.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/announcement-controller/src/AnnouncementController.test.ts b/packages/announcement-controller/src/AnnouncementController.test.ts index 3de812b385d..ee36b0ce4e5 100644 --- a/packages/announcement-controller/src/AnnouncementController.test.ts +++ b/packages/announcement-controller/src/AnnouncementController.test.ts @@ -1,5 +1,9 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; -import { Messenger } from '@metamask/messenger'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { AnnouncementControllerState, @@ -19,10 +23,10 @@ const name = 'AnnouncementController'; */ function getMessenger() { const messenger = new Messenger< - 'Root', + MockAnyNamespace, AnnouncementControllerActions, AnnouncementControllerEvents - >({ namespace: 'Root' }); + >({ namespace: MOCK_ANY_NAMESPACE }); return new Messenger< typeof name, AnnouncementControllerActions, From bee9e6f8cca39fbc26b864ce0372499a3381a626 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 11:56:40 +0200 Subject: [PATCH 089/247] refactor: use `MockAnyNamespace` namespace for root messenger --- .../src/AccountTreeController.test.ts | 118 ++++++------------ .../src/AccountTreeController.ts | 34 ++--- .../account-tree-controller/src/rule.test.ts | 37 +++--- .../src/rules/entropy.test.ts | 54 ++++---- .../src/rules/keyring.test.ts | 59 +++++---- .../src/rules/snap.test.ts | 64 +++++----- .../tests/mockMessenger.ts | 89 +------------ 7 files changed, 161 insertions(+), 294 deletions(-) diff --git a/packages/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index fec3c441fc4..54d0210a6b0 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -33,11 +33,8 @@ import { isAccountGroupNameUnique } from './group'; import { getAccountWalletNameFromKeyringType } from './rules/keyring'; import { type AccountTreeControllerState } from './types'; import { - getAccountsControllerMessenger, getAccountTreeControllerMessenger, - getKeyringControllerMessenger, getRootMessenger, - getSnapControllerMessenger, } from '../tests/mockMessenger'; // Local mock of EMPTY_ACCOUNT to avoid circular dependency @@ -245,11 +242,6 @@ function setup({ accountTreeControllerMessenger: ReturnType< typeof getAccountTreeControllerMessenger >; - accountsControllerMessenger: ReturnType< - typeof getAccountsControllerMessenger - >; - keyringControllerMessenger: ReturnType; - snapControllerMessenger: ReturnType; spies: { consoleWarn: jest.SpyInstance; }; @@ -304,16 +296,13 @@ function setup({ }), }, }; - const accountsControllerMessenger = getAccountsControllerMessenger(messenger); - const keyringControllerMessenger = getKeyringControllerMessenger(messenger); - const snapControllerMessenger = getSnapControllerMessenger(messenger); if (accounts) { mocks.AccountsController.listMultichainAccounts.mockImplementation( () => mocks.AccountsController.accounts, ); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:listMultichainAccounts', mocks.AccountsController.listMultichainAccounts, ); @@ -321,7 +310,7 @@ function setup({ mocks.AccountsController.getAccount.mockImplementation((id) => mocks.AccountsController.accounts.find((account) => account.id === id), ); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', mocks.AccountsController.getAccount, ); @@ -336,7 +325,7 @@ function setup({ ); // Mock AccountsController:setSelectedAccount - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:setSelectedAccount', jest.fn(), ); @@ -380,7 +369,7 @@ function setup({ isUnlocked: true, keyrings: mocks.KeyringController.keyrings, })); - keyringControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'KeyringController:getState', mocks.KeyringController.getState, ); @@ -402,9 +391,6 @@ function setup({ controller, messenger, accountTreeControllerMessenger, - accountsControllerMessenger, - keyringControllerMessenger, - snapControllerMessenger, spies: { consoleWarn: consoleWarnSpy }, mocks, }; @@ -417,7 +403,7 @@ describe('AccountTreeController', () => { describe('init', () => { it('groups accounts by entropy source, then snapId, then wallet type', () => { - const { controller, snapControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [ MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2, @@ -428,7 +414,7 @@ describe('AccountTreeController', () => { keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); - snapControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'SnapController:get', () => // TODO: Update this to avoid the unknown cast if possible. @@ -650,12 +636,12 @@ describe('AccountTreeController', () => { }, } as const; - const { controller, snapControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [mockSnapAccountWithEntropy], keyrings: [MOCK_HD_KEYRING_2], }); - snapControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'SnapController:get', () => ({ @@ -682,15 +668,12 @@ describe('AccountTreeController', () => { }); it('fallback to Snap ID if Snap cannot be found', () => { - const { controller, snapControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_SNAP_ACCOUNT_1], keyrings: [], }); - snapControllerMessenger.registerActionHandler( - 'SnapController:get', - () => undefined, - ); // Snap won't be found. + messenger.registerActionHandler('SnapController:get', () => undefined); // Snap won't be found. controller.init(); @@ -918,7 +901,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [mockHdAccount1, mockHdAccount2], keyrings: [MOCK_HD_KEYRING_1], }); @@ -926,10 +909,7 @@ describe('AccountTreeController', () => { // Create entropy wallets that will both get "Wallet" as base name, then get numbered controller.init(); - accountsControllerMessenger.publish( - 'AccountsController:accountRemoved', - mockHdAccount1.id, - ); + messenger.publish('AccountsController:accountRemoved', mockHdAccount1.id); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -998,17 +978,14 @@ describe('AccountTreeController', () => { }, }; - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [mockHdAccount1, mockHdAccount2], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); - accountsControllerMessenger.publish( - 'AccountsController:accountRemoved', - mockHdAccount1.id, - ); + messenger.publish('AccountsController:accountRemoved', mockHdAccount1.id); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -1070,17 +1047,14 @@ describe('AccountTreeController', () => { it('prunes an empty wallet if it holds no groups', () => { const mockHdAccount1: Bip44Account = MOCK_HD_ACCOUNT_1; - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [mockHdAccount1], keyrings: [MOCK_HD_KEYRING_1], }); controller.init(); - accountsControllerMessenger.publish( - 'AccountsController:accountRemoved', - mockHdAccount1.id, - ); + messenger.publish('AccountsController:accountRemoved', mockHdAccount1.id); expect(controller.state).toStrictEqual({ accountGroupsMetadata: {}, @@ -1122,7 +1096,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [mockHdAccount1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1130,10 +1104,7 @@ describe('AccountTreeController', () => { // Create entropy wallets that will both get "Wallet" as base name, then get numbered controller.init(); - accountsControllerMessenger.publish( - 'AccountsController:accountAdded', - mockHdAccount2, - ); + messenger.publish('AccountsController:accountAdded', mockHdAccount2); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -1214,7 +1185,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, accountsControllerMessenger, mocks } = setup({ + const { controller, messenger, mocks } = setup({ accounts: [mockHdAccount1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1224,10 +1195,7 @@ describe('AccountTreeController', () => { mocks.KeyringController.keyrings = [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2]; mocks.AccountsController.accounts = [mockHdAccount1, mockHdAccount2]; - accountsControllerMessenger.publish( - 'AccountsController:accountAdded', - mockHdAccount2, - ); + messenger.publish('AccountsController:accountAdded', mockHdAccount2); const walletId1 = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -1416,7 +1384,7 @@ describe('AccountTreeController', () => { }); it('updates selectedAccountGroup when AccountsController selected account changes', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1424,7 +1392,7 @@ describe('AccountTreeController', () => { controller.init(); const initialGroup = controller.getSelectedAccountGroup(); - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:selectedAccountChange', MOCK_HD_ACCOUNT_2, ); @@ -1541,7 +1509,7 @@ describe('AccountTreeController', () => { }); it('is idempotent - receiving selectedAccountChange for account in same group should not update state', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1560,7 +1528,7 @@ describe('AccountTreeController', () => { const initialState = { ...controller.state }; - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:selectedAccountChange', MOCK_HD_ACCOUNT_1, ); @@ -1585,7 +1553,7 @@ describe('AccountTreeController', () => { }); it('handles AccountsController selectedAccountChange for account not in tree gracefully', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1598,7 +1566,7 @@ describe('AccountTreeController', () => { id: 'unknown-account-id', }; - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:selectedAccountChange', unknownAccount, ); @@ -1681,7 +1649,7 @@ describe('AccountTreeController', () => { describe('account removal and memory management', () => { it('cleans up reverse mapping and does not change selectedAccountGroup when removing from non-selected group', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1701,7 +1669,7 @@ describe('AccountTreeController', () => { const initialSelectedGroup = controller.getSelectedAccountGroup(); // Remove account from the second group (not selected) - tests false branch and reverse cleanup - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:accountRemoved', MOCK_HD_ACCOUNT_2.id, ); @@ -1710,7 +1678,7 @@ describe('AccountTreeController', () => { expect(controller.getSelectedAccountGroup()).toBe(initialSelectedGroup); // Test that subsequent selectedAccountChange for removed account is handled gracefully (indirect test of reverse cleanup) - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:selectedAccountChange', MOCK_HD_ACCOUNT_2, ); @@ -1718,7 +1686,7 @@ describe('AccountTreeController', () => { }); it('updates selectedAccountGroup when last account in selected group is removed and other groups exist', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -1744,7 +1712,7 @@ describe('AccountTreeController', () => { ); // Remove the account from the selected group - tests true branch and findFirstNonEmptyGroup finding a group - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:accountRemoved', MOCK_HD_ACCOUNT_1.id, ); @@ -1754,7 +1722,7 @@ describe('AccountTreeController', () => { }); it('sets selectedAccountGroup to empty when no non-empty groups exist', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1762,7 +1730,7 @@ describe('AccountTreeController', () => { controller.init(); // Remove the only account - tests findFirstNonEmptyGroup returning empty string - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:accountRemoved', MOCK_HD_ACCOUNT_1.id, ); @@ -1772,7 +1740,7 @@ describe('AccountTreeController', () => { }); it('handles removal gracefully when account is not found in reverse mapping', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1782,17 +1750,14 @@ describe('AccountTreeController', () => { // Try to remove an account that was never added const unknownAccountId = 'unknown-account-id'; - accountsControllerMessenger.publish( - 'AccountsController:accountRemoved', - unknownAccountId, - ); + messenger.publish('AccountsController:accountRemoved', unknownAccountId); // State should remain unchanged expect(controller.state).toStrictEqual(initialState); }); it('handles edge cases gracefully in account removal', () => { - const { controller, accountsControllerMessenger } = setup({ + const { controller, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1], keyrings: [MOCK_HD_KEYRING_1], }); @@ -1800,7 +1765,7 @@ describe('AccountTreeController', () => { controller.init(); expect(() => { - accountsControllerMessenger.publish( + messenger.publish( 'AccountsController:accountRemoved', 'non-existent-account', ); @@ -2511,7 +2476,7 @@ describe('AccountTreeController', () => { }, }; - const { controller, accountsControllerMessenger, mocks } = setup({ + const { controller, messenger, mocks } = setup({ accounts: [existingAccount], keyrings: [MOCK_HD_KEYRING_1], }); @@ -2520,10 +2485,7 @@ describe('AccountTreeController', () => { // Add the new account to the existing group mocks.AccountsController.accounts = [existingAccount, newAccount]; - accountsControllerMessenger.publish( - 'AccountsController:accountAdded', - newAccount, - ); + messenger.publish('AccountsController:accountAdded', newAccount); const expectedWalletId = toMultichainAccountWalletId( MOCK_HD_KEYRING_1.metadata.id, @@ -3717,7 +3679,7 @@ describe('AccountTreeController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/account-tree-controller/src/AccountTreeController.ts b/packages/account-tree-controller/src/AccountTreeController.ts index 5b04f249155..25b71a5c35b 100644 --- a/packages/account-tree-controller/src/AccountTreeController.ts +++ b/packages/account-tree-controller/src/AccountTreeController.ts @@ -11,7 +11,7 @@ import type { } from '@metamask/account-api'; import type { MultichainAccountWalletStatus } from '@metamask/account-api'; import { type AccountId } from '@metamask/accounts-controller'; -import type { StateMetadata } from '@metamask/base-controller'; +import type { StateMetadata } from '@metamask/base-controller/next'; import { BaseController } from '@metamask/base-controller/next'; import type { TraceCallback } from '@metamask/controller-utils'; import { isEvmAccountType } from '@metamask/keyring-api'; @@ -49,31 +49,31 @@ const accountTreeControllerMetadata: StateMetadata = accountTree: { includeInStateLogs: true, persist: false, // We do re-recompute this state everytime. - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isAccountTreeSyncingInProgress: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, hasAccountTreeSyncingSyncedAtLeastOnce: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, accountGroupsMetadata: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, accountWalletsMetadata: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -295,7 +295,7 @@ export class AccountTreeController extends BaseController< previousSelectedAccountGroup !== this.state.accountTree.selectedAccountGroup ) { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup, @@ -607,7 +607,7 @@ export class AccountTreeController extends BaseController< } } }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:accountTreeChange`, this.state.accountTree, ); @@ -657,14 +657,14 @@ export class AccountTreeController extends BaseController< } } }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:accountTreeChange`, this.state.accountTree, ); // Emit selectedAccountGroupChange event if the selected group changed if (selectedAccountGroupChanged) { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup, @@ -865,7 +865,7 @@ export class AccountTreeController extends BaseController< this.update((state) => { state.accountTree.selectedAccountGroup = groupId; }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup, @@ -930,7 +930,7 @@ export class AccountTreeController extends BaseController< this.update((state) => { state.accountTree.selectedAccountGroup = groupId; }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup, @@ -1277,22 +1277,22 @@ export class AccountTreeController extends BaseController< this.getAccountsFromSelectedAccountGroup.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setAccountWalletName`, this.setAccountWalletName.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setAccountGroupName`, this.setAccountGroupName.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setAccountGroupPinned`, this.setAccountGroupPinned.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:setAccountGroupHidden`, this.setAccountGroupHidden.bind(this), ); @@ -1339,7 +1339,7 @@ export class AccountTreeController extends BaseController< return { ...this.#backupAndSyncConfig, controller: this, - messenger: this.messagingSystem, + messenger: this.messenger, controllerStateUpdateFn: this.update.bind(this), traceFn: this.#trace.bind(this), groupIdToWalletId: this.#groupIdToWalletId, diff --git a/packages/account-tree-controller/src/rule.test.ts b/packages/account-tree-controller/src/rule.test.ts index af4cb81f066..b8a42160598 100644 --- a/packages/account-tree-controller/src/rule.test.ts +++ b/packages/account-tree-controller/src/rule.test.ts @@ -17,7 +17,6 @@ import type { AccountGroupObject } from './group'; import { BaseRule } from './rule'; import { getAccountTreeControllerMessenger, - getAccountsControllerMessenger, getRootMessenger, } from '../tests/mockMessenger'; @@ -56,13 +55,12 @@ const MOCK_HD_ACCOUNT_1: Bip44Account = { describe('BaseRule', () => { describe('getComputedAccountGroupName', () => { it('returns empty string when account is not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new BaseRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new BaseRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -88,13 +86,12 @@ describe('BaseRule', () => { }); it('returns account name when account is found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new BaseRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new BaseRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HD_ACCOUNT_1, ); @@ -124,17 +121,19 @@ describe('BaseRule', () => { describe('getDefaultAccountGroupName', () => { it('returns empty string when no index is provided', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new BaseRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new BaseRule(accountTreeControllerMessenger); expect(rule.getDefaultAccountGroupName()).toBe(''); }); it('returns formatted account name when index is provided', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new BaseRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new BaseRule(accountTreeControllerMessenger); expect(rule.getDefaultAccountGroupName(0)).toBe('Account 1'); expect(rule.getDefaultAccountGroupName(1)).toBe('Account 2'); diff --git a/packages/account-tree-controller/src/rules/entropy.test.ts b/packages/account-tree-controller/src/rules/entropy.test.ts index 904c0aa8838..f5a6657c637 100644 --- a/packages/account-tree-controller/src/rules/entropy.test.ts +++ b/packages/account-tree-controller/src/rules/entropy.test.ts @@ -17,7 +17,6 @@ import type { InternalAccount } from '@metamask/keyring-internal-api'; import { EntropyRule } from './entropy'; import { getAccountTreeControllerMessenger, - getAccountsControllerMessenger, getRootMessenger, } from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; @@ -63,13 +62,12 @@ const MOCK_HD_ACCOUNT_1: Bip44Account = { describe('EntropyRule', () => { describe('getComputedAccountGroupName', () => { it('uses BaseRule implementation', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HD_ACCOUNT_1, ); @@ -97,13 +95,12 @@ describe('EntropyRule', () => { }); it('returns empty string when account is not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -160,11 +157,10 @@ describe('EntropyRule', () => { }); it('getComputedAccountGroupName returns account name with EVM priority', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); const mockEvmAccount: InternalAccount = { ...MOCK_HD_ACCOUNT_1, @@ -176,7 +172,7 @@ describe('EntropyRule', () => { }, }; - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => mockEvmAccount, ); @@ -202,13 +198,12 @@ describe('EntropyRule', () => { }); it('getComputedAccountGroupName returns empty string when no accounts found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -234,9 +229,10 @@ describe('EntropyRule', () => { }); it('getComputedAccountGroupName returns empty string for non-EVM accounts to prevent chain-specific names', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); const mockSolanaAccount: InternalAccount = { ...MOCK_HD_ACCOUNT_1, @@ -248,7 +244,7 @@ describe('EntropyRule', () => { }, }; - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', (accountId: string) => { const accounts: Record = { diff --git a/packages/account-tree-controller/src/rules/keyring.test.ts b/packages/account-tree-controller/src/rules/keyring.test.ts index 8a754317075..eea3f957094 100644 --- a/packages/account-tree-controller/src/rules/keyring.test.ts +++ b/packages/account-tree-controller/src/rules/keyring.test.ts @@ -11,7 +11,6 @@ import type { InternalAccount } from '@metamask/keyring-internal-api'; import { KeyringRule, getAccountWalletNameFromKeyringType } from './keyring'; import { getAccountTreeControllerMessenger, - getAccountsControllerMessenger, getRootMessenger, } from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; @@ -66,13 +65,12 @@ describe('keyring', () => { describe('getComputedAccountGroupName', () => { it('uses BaseRule implementation', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HARDWARE_ACCOUNT_1, ); @@ -97,13 +95,12 @@ describe('keyring', () => { }); it('returns empty string when account is not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -128,9 +125,10 @@ describe('keyring', () => { describe('getDefaultAccountGroupName', () => { it('uses BaseRule implementation', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); expect(rule.getDefaultAccountGroupName(0)).toBe('Account 1'); expect(rule.getDefaultAccountGroupName(1)).toBe('Account 2'); @@ -138,14 +136,13 @@ describe('keyring', () => { }); it('getComputedAccountGroupName returns computed name from base class', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); // Mock the AccountsController to always return the account - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HARDWARE_ACCOUNT_1, ); @@ -173,14 +170,13 @@ describe('keyring', () => { }); it('getComputedAccountGroupName returns empty string when account not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); // Mock the AccountsController to return undefined (account not found) - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -207,9 +203,10 @@ describe('keyring', () => { }); it('getDefaultAccountWalletName returns wallet name based on keyring type', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); const hdWallet: AccountWalletObjectOf = { id: toAccountWalletId(AccountWalletType.Keyring, KeyringTypes.hd), diff --git a/packages/account-tree-controller/src/rules/snap.test.ts b/packages/account-tree-controller/src/rules/snap.test.ts index c09e96b9f2c..ebbb049d667 100644 --- a/packages/account-tree-controller/src/rules/snap.test.ts +++ b/packages/account-tree-controller/src/rules/snap.test.ts @@ -13,9 +13,7 @@ import type { Snap } from '@metamask/snaps-utils'; import { SnapRule } from './snap'; import { getAccountTreeControllerMessenger, - getAccountsControllerMessenger, getRootMessenger, - getSnapControllerMessenger, } from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; import type { AccountWalletObjectOf } from '../wallet'; @@ -59,14 +57,13 @@ const MOCK_SNAP_ACCOUNT_1: InternalAccount = { describe('SnapRule', () => { describe('getComputedAccountGroupName', () => { it('returns computed name from base class', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); // Mock the AccountsController to return an account - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_SNAP_ACCOUNT_1, ); @@ -91,14 +88,13 @@ describe('SnapRule', () => { }); it('returns empty string when account not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const accountsControllerMessenger = - getAccountsControllerMessenger(rootMessenger); - const rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); // Mock the AccountsController to return undefined (account not found) - accountsControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -124,9 +120,10 @@ describe('SnapRule', () => { describe('getDefaultAccountGroupName', () => { it('returns default name from base class based on index', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); expect(rule.getDefaultAccountGroupName(0)).toBe('Account 1'); expect(rule.getDefaultAccountGroupName(1)).toBe('Account 2'); @@ -136,13 +133,13 @@ describe('SnapRule', () => { describe('getDefaultAccountWalletName', () => { it('returns snap proposed name when available', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const snapControllerMessenger = getSnapControllerMessenger(rootMessenger); - const rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); // Mock SnapController to return snap with proposed name - snapControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'SnapController:get', () => MOCK_SNAP_1 as unknown as Snap, ); @@ -162,10 +159,10 @@ describe('SnapRule', () => { }); it('returns cleaned snap ID when no proposed name available', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const snapControllerMessenger = getSnapControllerMessenger(rootMessenger); - const rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); const snapWithoutProposedName = { id: 'npm:@metamask/example-snap' as unknown as SnapId, @@ -179,7 +176,7 @@ describe('SnapRule', () => { }; // Mock SnapController to return snap without proposed name - snapControllerMessenger.registerActionHandler( + messenger.registerActionHandler( 'SnapController:get', () => snapWithoutProposedName as unknown as Snap, ); @@ -203,16 +200,13 @@ describe('SnapRule', () => { }); it('returns cleaned snap ID when snap not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const snapControllerMessenger = getSnapControllerMessenger(rootMessenger); - const rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); // Mock SnapController to return undefined (snap not found) - snapControllerMessenger.registerActionHandler( - 'SnapController:get', - () => undefined, - ); + messenger.registerActionHandler('SnapController:get', () => undefined); const snapId = 'npm:@metamask/missing-snap'; const wallet: AccountWalletObjectOf = { diff --git a/packages/account-tree-controller/tests/mockMessenger.ts b/packages/account-tree-controller/tests/mockMessenger.ts index ef7ee851bee..10fa770a675 100644 --- a/packages/account-tree-controller/tests/mockMessenger.ts +++ b/packages/account-tree-controller/tests/mockMessenger.ts @@ -1,21 +1,11 @@ import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; -import type { GetSnap } from '@metamask/snaps-controllers'; -import type { - AccountsControllerAccountAddedEvent, - AccountsControllerAccountRemovedEvent, - AccountsControllerAccountRenamedEvent, - AccountsControllerGetAccountAction, - AccountsControllerGetSelectedAccountAction, - AccountsControllerListMultichainAccountsAction, - AccountsControllerSelectedAccountChangeEvent, - AccountsControllerSetSelectedAccountAction, -} from '../../accounts-controller/src/AccountsController'; -import type { KeyringControllerGetStateAction } from '../../keyring-controller/src/KeyringController'; import type { AccountTreeControllerMessenger } from '../src/types'; type AllAccountTreeControllerActions = @@ -31,10 +21,10 @@ type AllAccountTreeControllerEvents = */ export function getRootMessenger() { return new Messenger< - 'Root', + MockAnyNamespace, AllAccountTreeControllerActions, AllAccountTreeControllerEvents - >({ namespace: 'Root' }); + >({ namespace: MOCK_ANY_NAMESPACE }); } /** @@ -79,74 +69,3 @@ export function getAccountTreeControllerMessenger( }); return accountTreeControllerMessenger; } - -/** - * Retrieves a messenger for the AccountsController. - * - * @param rootMessenger - The root messenger instance. - * @returns The messenger for the AccountsController. - */ -export function getAccountsControllerMessenger( - rootMessenger: ReturnType, -): Messenger< - 'AccountsController', - | AccountsControllerGetAccountAction - | AccountsControllerGetSelectedAccountAction - | AccountsControllerListMultichainAccountsAction - | AccountsControllerSetSelectedAccountAction, - | AccountsControllerAccountAddedEvent - | AccountsControllerAccountRemovedEvent - | AccountsControllerAccountRenamedEvent - | AccountsControllerSelectedAccountChangeEvent, - typeof rootMessenger -> { - return new Messenger< - 'AccountsController', - | AccountsControllerGetAccountAction - | AccountsControllerGetSelectedAccountAction - | AccountsControllerListMultichainAccountsAction - | AccountsControllerSetSelectedAccountAction, - | AccountsControllerAccountAddedEvent - | AccountsControllerAccountRemovedEvent - | AccountsControllerAccountRenamedEvent - | AccountsControllerSelectedAccountChangeEvent, - typeof rootMessenger - >({ namespace: 'AccountsController', parent: rootMessenger }); -} - -/** - * Retrieves a messenger for the KeyringController. - * - * @param rootMessenger - The root messenger instance. - * @returns The messenger for the KeyringController. - */ -export function getKeyringControllerMessenger( - rootMessenger: ReturnType, -): Messenger< - 'KeyringController', - KeyringControllerGetStateAction, - never, - typeof rootMessenger -> { - return new Messenger< - 'KeyringController', - KeyringControllerGetStateAction, - never, - typeof rootMessenger - >({ namespace: 'KeyringController', parent: rootMessenger }); -} - -/** - * Retrieves a messenger for the SnapController. - * - * @param rootMessenger - The root messenger instance. - * @returns The messenger for the SnapController. - */ -export function getSnapControllerMessenger( - rootMessenger: ReturnType, -): Messenger<'SnapController', GetSnap, never, typeof rootMessenger> { - return new Messenger<'SnapController', GetSnap, never, typeof rootMessenger>({ - namespace: 'SnapController', - parent: rootMessenger, - }); -} From a91b196e44af5a7aa2d8d2769fb6e315b3e39788 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 11:57:45 +0200 Subject: [PATCH 090/247] update tsconfig files and README --- README.md | 1 + packages/account-tree-controller/tsconfig.build.json | 1 + packages/account-tree-controller/tsconfig.json | 3 +++ 3 files changed, 5 insertions(+) diff --git a/README.md b/README.md index 4eab983a1dc..f7f615d8581 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/account-tree-controller/tsconfig.build.json b/packages/account-tree-controller/tsconfig.build.json index 707a559080c..d52110b5b4f 100644 --- a/packages/account-tree-controller/tsconfig.build.json +++ b/packages/account-tree-controller/tsconfig.build.json @@ -9,6 +9,7 @@ { "path": "../accounts-controller/tsconfig.build.json" }, { "path": "../base-controller/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, { "path": "../multichain-account-service/tsconfig.build.json" }, { "path": "../profile-sync-controller/tsconfig.build.json" } ], diff --git a/packages/account-tree-controller/tsconfig.json b/packages/account-tree-controller/tsconfig.json index 6f903741569..80394fb0531 100644 --- a/packages/account-tree-controller/tsconfig.json +++ b/packages/account-tree-controller/tsconfig.json @@ -13,6 +13,9 @@ { "path": "../accounts-controller" }, + { + "path": "../messenger" + }, { "path": "../multichain-account-service" }, From d3691a5a48c6fadd18aee392a3c055d87ff992e2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 12:00:10 +0200 Subject: [PATCH 091/247] update messenger to 0.3.0 --- packages/account-tree-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/account-tree-controller/package.json b/packages/account-tree-controller/package.json index eac2c60a48e..36a9d766099 100644 --- a/packages/account-tree-controller/package.json +++ b/packages/account-tree-controller/package.json @@ -48,7 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", - "@metamask/messenger": "^0.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/superstruct": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 8899eea6db0..e1362b55d79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,7 +2413,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.0" "@metamask/keyring-api": "npm:^21.0.0" "@metamask/keyring-controller": "npm:^23.1.0" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/multichain-account-service": "npm:^1.0.0" "@metamask/profile-sync-controller": "npm:^25.0.0" "@metamask/providers": "npm:^22.1.0" @@ -3823,13 +3823,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From 0e3bb77b38f489048382bf17c10c95f3a5b7e3af Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 12:02:17 +0200 Subject: [PATCH 092/247] fix tsconfig --- packages/announcement-controller/tsconfig.build.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/announcement-controller/tsconfig.build.json b/packages/announcement-controller/tsconfig.build.json index 85cca3e8cdd..931c4d6594b 100644 --- a/packages/announcement-controller/tsconfig.build.json +++ b/packages/announcement-controller/tsconfig.build.json @@ -6,7 +6,7 @@ "rootDir": "./src" }, "references": [ - { "path": "../base-controller/tsconfig.build.json" } + { "path": "../base-controller/tsconfig.build.json" }, { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] From e40037f66e2a63661a964345a66b9065e492fb60 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 12:05:01 +0200 Subject: [PATCH 093/247] run prettier --- packages/app-metadata-controller/tsconfig.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/app-metadata-controller/tsconfig.json b/packages/app-metadata-controller/tsconfig.json index 8af2380112d..68c3ddfc2cd 100644 --- a/packages/app-metadata-controller/tsconfig.json +++ b/packages/app-metadata-controller/tsconfig.json @@ -3,9 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [ - { "path": "../base-controller" }, - { "path": "../messenger" } - ], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } From 6746c38edce0163cc0a39cbeeab6857bf05b90f6 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 12:14:24 +0200 Subject: [PATCH 094/247] update changelog --- packages/account-tree-controller/CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index b246f5b09e3..4031cf54df8 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) + - Previously, `AccountTreeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ### Fixed - Fix group naming for PK/Hardware accounts ([#6677](https://github.com/MetaMask/core/pull/6677)) @@ -163,8 +168,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) - - Previously, `AccountTreeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355)) ## [0.10.0] From a1a938c862df31c79fbd2c9a252e73f13e4fd040 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 12:15:22 +0200 Subject: [PATCH 095/247] update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d5810999c32..1c4bd5efa12 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; From 0267ab28ad124ae22213650be6842ea993348c3f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 13:15:00 +0200 Subject: [PATCH 096/247] rename `anonymous` to `inclueInDebugSnapshot` --- .../src/ApprovalController.test.ts | 2 +- .../approval-controller/src/ApprovalController.ts | 13 ++++++++----- .../assets-controllers/src/NftController.test.ts | 2 +- packages/assets-controllers/src/NftController.ts | 9 +++++---- .../assets-controllers/src/TokensController.test.ts | 2 +- packages/assets-controllers/src/TokensController.ts | 9 +++++---- .../gas-fee-controller/src/GasFeeController.test.ts | 2 +- packages/gas-fee-controller/src/GasFeeController.ts | 13 +++++++------ .../network-controller/src/NetworkController.ts | 6 +++--- .../tests/NetworkController.test.ts | 4 ++-- .../src/TransactionController.test.ts | 2 +- .../src/TransactionController.ts | 13 +++++++------ 12 files changed, 42 insertions(+), 35 deletions(-) diff --git a/packages/approval-controller/src/ApprovalController.test.ts b/packages/approval-controller/src/ApprovalController.test.ts index 5bf81cb1539..d570e5d5f52 100644 --- a/packages/approval-controller/src/ApprovalController.test.ts +++ b/packages/approval-controller/src/ApprovalController.test.ts @@ -1707,7 +1707,7 @@ describe('approval controller', () => { deriveStateFromMetadata( approvalController.state, approvalController.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/approval-controller/src/ApprovalController.ts b/packages/approval-controller/src/ApprovalController.ts index 680649afe05..f2e7efe13c3 100644 --- a/packages/approval-controller/src/ApprovalController.ts +++ b/packages/approval-controller/src/ApprovalController.ts @@ -1,4 +1,7 @@ -import type { ControllerGetStateAction } from '@metamask/base-controller'; +import type { + ControllerGetStateAction, + StateMetadata, +} from '@metamask/base-controller/next'; import { BaseController, type ControllerStateChangeEvent, @@ -26,23 +29,23 @@ export const APPROVAL_TYPE_RESULT_SUCCESS = 'result_success'; const controllerName = 'ApprovalController'; -const stateMetadata = { +const stateMetadata: StateMetadata = { pendingApprovals: { includeInStateLogs: true, persist: false, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, pendingApprovalCount: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, approvalFlows: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; diff --git a/packages/assets-controllers/src/NftController.test.ts b/packages/assets-controllers/src/NftController.test.ts index 548ecba192a..cb3bddac5de 100644 --- a/packages/assets-controllers/src/NftController.test.ts +++ b/packages/assets-controllers/src/NftController.test.ts @@ -5914,7 +5914,7 @@ describe('NftController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/NftController.ts b/packages/assets-controllers/src/NftController.ts index 32f2897631a..9dfd924efd6 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -9,6 +9,7 @@ import { BaseController, type ControllerStateChangeEvent, type ControllerGetStateAction, + StateMetadata, } from '@metamask/base-controller/next'; import { safelyExecute, @@ -206,23 +207,23 @@ export type NftControllerState = { ignoredNfts: Nft[]; }; -const nftControllerMetadata = { +const nftControllerMetadata: StateMetadata = { allNftContracts: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, allNfts: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, ignoredNfts: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 70360eedea5..b84f5a901b5 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -3483,7 +3483,7 @@ describe('TokensController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index fb5ec04e60a..8a45509a468 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -10,6 +10,7 @@ import type { AddApprovalRequest } from '@metamask/approval-controller'; import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { BaseController } from '@metamask/base-controller/next'; import contractsMap from '@metamask/contract-metadata'; @@ -88,23 +89,23 @@ export type TokensControllerState = { allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } }; }; -const metadata = { +const metadata: StateMetadata = { allTokens: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, allIgnoredTokens: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, allDetectedTokens: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; diff --git a/packages/gas-fee-controller/src/GasFeeController.test.ts b/packages/gas-fee-controller/src/GasFeeController.test.ts index 28537bcfcb4..9e736d00018 100644 --- a/packages/gas-fee-controller/src/GasFeeController.test.ts +++ b/packages/gas-fee-controller/src/GasFeeController.test.ts @@ -1297,7 +1297,7 @@ describe('GasFeeController', () => { deriveStateFromMetadata( gasFeeController.state, gasFeeController.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/gas-fee-controller/src/GasFeeController.ts b/packages/gas-fee-controller/src/GasFeeController.ts index 5e9289f2334..be6fe7b3fb0 100644 --- a/packages/gas-fee-controller/src/GasFeeController.ts +++ b/packages/gas-fee-controller/src/GasFeeController.ts @@ -1,6 +1,7 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { convertHexToDecimal, @@ -160,35 +161,35 @@ type FallbackGasFeeEstimates = { networkCongestion: null; }; -const metadata = { +const metadata: StateMetadata = { gasFeeEstimatesByChainId: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, gasFeeEstimates: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, estimatedGasFeeTimeBounds: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, gasEstimateType: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, nonRPCGasFeeApisDisabled: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; diff --git a/packages/network-controller/src/NetworkController.ts b/packages/network-controller/src/NetworkController.ts index 49cb110a3a7..5ddfc752c3e 100644 --- a/packages/network-controller/src/NetworkController.ts +++ b/packages/network-controller/src/NetworkController.ts @@ -1200,19 +1200,19 @@ export class NetworkController extends BaseController< selectedNetworkClientId: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, networksMetadata: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, networkConfigurationsByChainId: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }, diff --git a/packages/network-controller/tests/NetworkController.test.ts b/packages/network-controller/tests/NetworkController.test.ts index d69a93d46fd..24231afca51 100644 --- a/packages/network-controller/tests/NetworkController.test.ts +++ b/packages/network-controller/tests/NetworkController.test.ts @@ -1,7 +1,7 @@ // A lot of the tests in this file have conditionals. /* eslint-disable jest/no-conditional-in-test */ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { BuiltInNetworkName, ChainId, @@ -14512,7 +14512,7 @@ describe('NetworkController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/transaction-controller/src/TransactionController.test.ts b/packages/transaction-controller/src/TransactionController.test.ts index c4c135e949c..4cd35b905a2 100644 --- a/packages/transaction-controller/src/TransactionController.test.ts +++ b/packages/transaction-controller/src/TransactionController.test.ts @@ -8016,7 +8016,7 @@ describe('TransactionController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index e4588002f7e..832e9e8e786 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -11,6 +11,7 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { BaseController } from '@metamask/base-controller/next'; import { @@ -182,35 +183,35 @@ import { * Metadata for the TransactionController state, describing how to "anonymize" * the state and which parts should be persisted. */ -const metadata = { +const metadata: StateMetadata = { transactions: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, transactionBatches: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, methodData: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, lastFetchedBlockNumbers: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, submitHistory: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; From d899417655423aaf3a6b50cbbca84c677e601929 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 13:17:44 +0200 Subject: [PATCH 097/247] update tsconfig and README --- README.md | 4 ++++ packages/approval-controller/tsconfig.build.json | 3 +++ packages/approval-controller/tsconfig.json | 3 +++ packages/assets-controllers/tsconfig.build.json | 1 + packages/assets-controllers/tsconfig.json | 1 + packages/network-controller/tsconfig.build.json | 3 ++- packages/network-controller/tsconfig.json | 3 ++- packages/transaction-controller/tsconfig.build.json | 1 + packages/transaction-controller/tsconfig.json | 1 + 9 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eab983a1dc..fb4cf4a5ee3 100644 --- a/README.md +++ b/README.md @@ -153,8 +153,10 @@ linkStyle default opacity:0.5 announcement_controller --> base_controller; app_metadata_controller --> base_controller; approval_controller --> base_controller; + approval_controller --> messenger; assets_controllers --> base_controller; assets_controllers --> controller_utils; + assets_controllers --> messenger; assets_controllers --> polling_controller; assets_controllers --> account_tree_controller; assets_controllers --> accounts_controller; @@ -246,6 +248,7 @@ linkStyle default opacity:0.5 network_controller --> controller_utils; network_controller --> eth_json_rpc_provider; network_controller --> json_rpc_engine; + network_controller --> messenger; network_controller --> error_reporting_service; network_enablement_controller --> base_controller; network_enablement_controller --> controller_utils; @@ -292,6 +295,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/approval-controller/tsconfig.build.json b/packages/approval-controller/tsconfig.build.json index 779d385a6ab..249f327913d 100644 --- a/packages/approval-controller/tsconfig.build.json +++ b/packages/approval-controller/tsconfig.build.json @@ -8,6 +8,9 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/approval-controller/tsconfig.json b/packages/approval-controller/tsconfig.json index f2d7b67ff66..cb296895b28 100644 --- a/packages/approval-controller/tsconfig.json +++ b/packages/approval-controller/tsconfig.json @@ -6,6 +6,9 @@ "references": [ { "path": "../base-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src"] diff --git a/packages/assets-controllers/tsconfig.build.json b/packages/assets-controllers/tsconfig.build.json index bca6a835d37..851f7d485ee 100644 --- a/packages/assets-controllers/tsconfig.build.json +++ b/packages/assets-controllers/tsconfig.build.json @@ -13,6 +13,7 @@ { "path": "../controller-utils/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, { "path": "../preferences-controller/tsconfig.build.json" }, { "path": "../polling-controller/tsconfig.build.json" }, { "path": "../permission-controller/tsconfig.build.json" }, diff --git a/packages/assets-controllers/tsconfig.json b/packages/assets-controllers/tsconfig.json index 2b0acd993f8..01db9a79264 100644 --- a/packages/assets-controllers/tsconfig.json +++ b/packages/assets-controllers/tsconfig.json @@ -12,6 +12,7 @@ { "path": "../controller-utils" }, { "path": "../keyring-controller" }, { "path": "../network-controller" }, + { "path": "../messenger" }, { "path": "../preferences-controller" }, { "path": "../phishing-controller" }, { "path": "../polling-controller" }, diff --git a/packages/network-controller/tsconfig.build.json b/packages/network-controller/tsconfig.build.json index fb5b1cb08e5..894e58c9650 100644 --- a/packages/network-controller/tsconfig.build.json +++ b/packages/network-controller/tsconfig.build.json @@ -10,7 +10,8 @@ { "path": "../controller-utils/tsconfig.build.json" }, { "path": "../eth-json-rpc-provider/tsconfig.build.json" }, { "path": "../json-rpc-engine/tsconfig.build.json" }, - { "path": "../error-reporting-service/tsconfig.build.json" } + { "path": "../error-reporting-service/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/network-controller/tsconfig.json b/packages/network-controller/tsconfig.json index cc0926fbd0c..6542199fa4f 100644 --- a/packages/network-controller/tsconfig.json +++ b/packages/network-controller/tsconfig.json @@ -9,7 +9,8 @@ { "path": "../controller-utils" }, { "path": "../eth-json-rpc-provider" }, { "path": "../json-rpc-engine" }, - { "path": "../error-reporting-service" } + { "path": "../error-reporting-service" }, + { "path": "../messenger" } ], "include": ["../../types", "../../tests", "./src", "./tests"] } diff --git a/packages/transaction-controller/tsconfig.build.json b/packages/transaction-controller/tsconfig.build.json index 716dda8820b..6e04a4ba1d8 100644 --- a/packages/transaction-controller/tsconfig.build.json +++ b/packages/transaction-controller/tsconfig.build.json @@ -12,6 +12,7 @@ { "path": "../controller-utils/tsconfig.build.json" }, { "path": "../gas-fee-controller/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, { "path": "../remote-feature-flag-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/transaction-controller/tsconfig.json b/packages/transaction-controller/tsconfig.json index b839b37eed5..1e328031877 100644 --- a/packages/transaction-controller/tsconfig.json +++ b/packages/transaction-controller/tsconfig.json @@ -11,6 +11,7 @@ { "path": "../controller-utils" }, { "path": "../gas-fee-controller" }, { "path": "../network-controller" }, + { "path": "../messenger" }, { "path": "../remote-feature-flag-controller" } ], "include": ["../../types", "./src", "./tests"] From b2a2585e617847c63e7fbba63f50013b0c269494 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 13:48:17 +0200 Subject: [PATCH 098/247] refactor: use `MockAnyNetwork` --- .../src/ApprovalController.test.ts | 23 ++++-- .../tests/NetworkController.test.ts | 9 +-- packages/network-controller/tests/helpers.ts | 31 +------- .../src/TransactionController.test.ts | 75 +++++++------------ 4 files changed, 50 insertions(+), 88 deletions(-) diff --git a/packages/approval-controller/src/ApprovalController.test.ts b/packages/approval-controller/src/ApprovalController.test.ts index d570e5d5f52..5afdfdf1898 100644 --- a/packages/approval-controller/src/ApprovalController.test.ts +++ b/packages/approval-controller/src/ApprovalController.test.ts @@ -1,7 +1,13 @@ /* eslint-disable jest/expect-expect */ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; -import { Messenger } from '@metamask/messenger'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { errorCodes, JsonRpcError } from '@metamask/rpc-errors'; import { nanoid } from 'nanoid'; @@ -10,6 +16,7 @@ import type { AddApprovalOptions, ApprovalControllerActions, ApprovalControllerEvents, + ApprovalControllerMessenger, ErrorOptions, StartFlowOptions, SuccessOptions, @@ -29,6 +36,12 @@ import { jest.mock('nanoid'); +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; + const nanoidMock = jest.mocked(nanoid); const PENDING_APPROVALS_STORE_KEY = 'pendingApprovals'; @@ -229,11 +242,9 @@ function getError(message: string, code?: number) { * @returns A controller messenger. */ function getMessengers() { - const rootMessenger = new Messenger< - 'Root', - ApprovalControllerActions, - ApprovalControllerEvents - >({ namespace: 'Root' }); + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); return { rootMessenger, approvalControllerMessenger: new Messenger< diff --git a/packages/network-controller/tests/NetworkController.test.ts b/packages/network-controller/tests/NetworkController.test.ts index 24231afca51..4841f92b647 100644 --- a/packages/network-controller/tests/NetworkController.test.ts +++ b/packages/network-controller/tests/NetworkController.test.ts @@ -27,7 +27,6 @@ import { buildCustomNetworkClientConfiguration, buildCustomNetworkConfiguration, buildCustomRpcEndpoint, - buildErrorReportingServiceMessenger, buildInfuraNetworkClientConfiguration, buildInfuraNetworkConfiguration, buildInfuraRpcEndpoint, @@ -354,9 +353,7 @@ describe('NetworkController', () => { describe('if selectedNetworkClientId does not match the networkClientId of an RPC endpoint in networkConfigurationsByChainId', () => { it('corrects selectedNetworkClientId to the default RPC endpoint of the first chain', () => { const messenger = buildRootMessenger(); - const errorReportingServiceMessenger = - buildErrorReportingServiceMessenger(messenger); - errorReportingServiceMessenger.registerActionHandler( + messenger.registerActionHandler( 'ErrorReportingService:captureException', jest.fn(), ); @@ -396,10 +393,8 @@ describe('NetworkController', () => { it('logs a Sentry error', () => { const messenger = buildRootMessenger(); - const errorReportingServiceMessenger = - buildErrorReportingServiceMessenger(messenger); const captureExceptionMock = jest.fn(); - errorReportingServiceMessenger.registerActionHandler( + messenger.registerActionHandler( 'ErrorReportingService:captureException', captureExceptionMock, ); diff --git a/packages/network-controller/tests/helpers.ts b/packages/network-controller/tests/helpers.ts index 1a509bd1fb8..bff0d7f5010 100644 --- a/packages/network-controller/tests/helpers.ts +++ b/packages/network-controller/tests/helpers.ts @@ -7,8 +7,10 @@ import { } from '@metamask/controller-utils'; import { Messenger, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, + MOCK_ANY_NAMESPACE, } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import { v4 as uuidV4 } from 'uuid'; @@ -49,7 +51,7 @@ export type AllNetworkControllerEvents = MessengerEvents; export type RootMessenger = Messenger< - 'Root', + MockAnyNamespace, AllNetworkControllerActions, AllNetworkControllerEvents >; @@ -83,7 +85,7 @@ export const TESTNET = { * @returns The messenger. */ export function buildRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** @@ -111,31 +113,6 @@ export function buildNetworkControllerMessenger( return networkControllerMessenger; } -/** - * Build a messenger for the error reporting service. - * - * @param rootMessenger - The root messenger. - * @returns The error reporting service messenger. - */ -export function buildErrorReportingServiceMessenger( - rootMessenger = buildRootMessenger(), -): Messenger< - 'ErrorReportingService', - AllNetworkControllerActions, - AllNetworkControllerEvents, - typeof rootMessenger -> { - return new Messenger< - 'ErrorReportingService', - AllNetworkControllerActions, - AllNetworkControllerEvents, - typeof rootMessenger - >({ - namespace: 'ErrorReportingService', - parent: rootMessenger, - }); -} - /** * Builds an object that satisfies the NetworkClient shape, but using a fake * provider and block tracker which doesn't make any requests. diff --git a/packages/transaction-controller/src/TransactionController.test.ts b/packages/transaction-controller/src/TransactionController.test.ts index 4cd35b905a2..16a15c67a1e 100644 --- a/packages/transaction-controller/src/TransactionController.test.ts +++ b/packages/transaction-controller/src/TransactionController.test.ts @@ -15,7 +15,13 @@ import { import type { SafeEventEmitterProvider } from '@metamask/eth-json-rpc-provider'; import EthQuery from '@metamask/eth-query'; import HttpProvider from '@metamask/ethjs-provider-http'; -import { Messenger } from '@metamask/messenger'; +import { + Messenger, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, + MOCK_ANY_NAMESPACE, +} from '@metamask/messenger'; import type { BlockTracker, NetworkClientConfiguration, @@ -52,11 +58,7 @@ import { PendingTransactionTracker } from './helpers/PendingTransactionTracker'; import { shouldResimulate } from './helpers/ResimulateHelper'; import { ExtraTransactionsPublishHook } from './hooks/ExtraTransactionsPublishHook'; import type { - AllowedActions, - AllowedEvents, MethodData, - TransactionControllerActions, - TransactionControllerEvents, TransactionControllerMessenger, TransactionControllerOptions, } from './TransactionController'; @@ -113,10 +115,16 @@ import { buildMockGetNetworkClientById, } from '../../network-controller/tests/helpers'; +type AllTransactionControllerActions = + MessengerActions; + +type AllTransactionControllerEvents = + MessengerEvents; + type RootMessenger = Messenger< - 'Root', - TransactionControllerActions | AllowedActions, - TransactionControllerEvents | AllowedEvents + MockAnyNamespace, + AllTransactionControllerActions, + AllTransactionControllerEvents >; const MOCK_V1_UUID = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'; @@ -662,32 +670,20 @@ describe('TransactionController', () => { }); }; const rootMessenger: RootMessenger = new Messenger({ - namespace: 'Root', - }); - const networkControllerMessenger = new Messenger< - 'NetworkController', - AllowedActions, - AllowedEvents, - typeof rootMessenger - >({ namespace: 'NetworkController', parent: rootMessenger }); + namespace: MOCK_ANY_NAMESPACE, + }); const getNetworkClientById = buildMockGetNetworkClientById( mockNetworkClientConfigurationsByNetworkClientId, ); - networkControllerMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'NetworkController:getNetworkClientById', getNetworkClientById, ); const { addTransactionApprovalRequest = { state: 'pending' } } = messengerOptions; - const approvalControllerMessenger = new Messenger< - 'ApprovalController', - AllowedActions, - AllowedEvents, - typeof rootMessenger - >({ namespace: 'ApprovalController', parent: rootMessenger }); const mockTransactionApprovalRequest = mockAddTransactionApprovalRequest( - approvalControllerMessenger, + rootMessenger, addTransactionApprovalRequest, ); @@ -730,20 +726,13 @@ describe('TransactionController', () => { ], }); - const accountsControllerMessenger = new Messenger< - 'AccountsController', - AllowedActions, - AllowedEvents, - typeof rootMessenger - >({ namespace: 'AccountsController', parent: rootMessenger }); - const mockGetSelectedAccount = jest.fn().mockReturnValue(selectedAccount); - accountsControllerMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', mockGetSelectedAccount, ); - accountsControllerMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:getState', () => ({}) as never, ); @@ -752,17 +741,7 @@ describe('TransactionController', () => { featureFlags: {}, }); - const remoteFeatureFlagControllerMessenger = new Messenger< - 'RemoteFeatureFlagController', - AllowedActions, - AllowedEvents, - typeof rootMessenger - >({ - namespace: 'RemoteFeatureFlagController', - parent: rootMessenger, - }); - - remoteFeatureFlagControllerMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', remoteFeatureFlagControllerGetStateMock, ); @@ -793,7 +772,7 @@ describe('TransactionController', () => { return { controller, messenger: transactionControllerMessenger, - networkControllerMessenger, + rootMessenger, mockTransactionApprovalRequest, mockGetSelectedAccount, changeNetwork, @@ -822,7 +801,7 @@ describe('TransactionController', () => { * finally the mocked version of the action handler itself. */ function mockAddTransactionApprovalRequest( - messenger: Messenger<'ApprovalController', AllowedActions, AllowedEvents>, + messenger: RootMessenger, options: | { state: 'approved'; @@ -6082,8 +6061,8 @@ describe('TransactionController', () => { }); it('uses the nonceTracker for the networkClientId matching the chainId', async () => { - const { controller, networkControllerMessenger } = setupController(); - networkControllerMessenger.registerActionHandler( + const { controller, rootMessenger } = setupController(); + rootMessenger.registerActionHandler( 'NetworkController:findNetworkClientIdByChainId', () => 'sepolia', ); From f786d7bb4ba1cf1b628a0f8f222b095c0f4485a4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 13:49:55 +0200 Subject: [PATCH 099/247] update messenger to 0.3.0 --- packages/transaction-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/transaction-controller/package.json b/packages/transaction-controller/package.json index 7a1a80fb9a9..adc3e1c57fe 100644 --- a/packages/transaction-controller/package.json +++ b/packages/transaction-controller/package.json @@ -57,7 +57,7 @@ "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", "@metamask/eth-query": "^4.0.0", - "@metamask/messenger": "^0.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/nonce-tracker": "^6.0.0", "@metamask/rpc-errors": "^7.0.2", diff --git a/yarn.lock b/yarn.lock index 001868b6d24..d73f8f63a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3824,13 +3824,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4793,7 +4786,7 @@ __metadata: "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-provider-http": "npm:^0.3.0" "@metamask/gas-fee-controller": "npm:^24.0.0" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^24.2.0" "@metamask/nonce-tracker": "npm:^6.0.0" From c07c63dfd57939465cf386a82b78ff63cd592e79 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 13:54:35 +0200 Subject: [PATCH 100/247] fix `NftController` lint --- packages/assets-controllers/src/NftController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/NftController.ts b/packages/assets-controllers/src/NftController.ts index 9dfd924efd6..8a18e547b5f 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -9,7 +9,7 @@ import { BaseController, type ControllerStateChangeEvent, type ControllerGetStateAction, - StateMetadata, + type StateMetadata, } from '@metamask/base-controller/next'; import { safelyExecute, From cf380bd510987373efb12c74b174fe016d022a92 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 14:04:51 +0200 Subject: [PATCH 101/247] fix changelogs --- packages/polling-controller/CHANGELOG.md | 4 ++++ packages/transaction-controller/CHANGELOG.md | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/polling-controller/CHANGELOG.md b/packages/polling-controller/CHANGELOG.md index 6a4a8b3fff1..d10662fdc96 100644 --- a/packages/polling-controller/CHANGELOG.md +++ b/packages/polling-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `StaticIntervalPollingControllerNext` to extend from `@metamask/base-controller/next` ([#6386](https://github.com/MetaMask/core/pull/6386)) + ### Changed - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) diff --git a/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index 5c2f9c79059..de3c351b3d7 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `TransactionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [60.4.0] ### Added @@ -43,11 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `isGasFeeIncluded` to `TransactionMeta`, `TransactionBatchRequest` and `addTransaction` options so the client can signal that MetaMask is compensated for the gas fee by the transaction ([#6428](https://github.com/MetaMask/core/pull/6428)) - Add optional `gasUsed` property to `TransactionMeta`, returned by the transaction simulation result ([#6410](https://github.com/MetaMask/core/pull/6410)) -### Changed - -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - - Previously, `TransactionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - ## [60.1.0] ### Added From d8541b39ad85ac81659048550b380105cac46fcc Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 14:07:57 +0200 Subject: [PATCH 102/247] add entry to gas-fee-controller changelog --- packages/gas-fee-controller/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gas-fee-controller/CHANGELOG.md b/packages/gas-fee-controller/CHANGELOG.md index 6a3c877bbab..b8c1c7ea06c 100644 --- a/packages/gas-fee-controller/CHANGELOG.md +++ b/packages/gas-fee-controller/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add two new controller state metadata properties: `includeInStateLogs` and `usedInUi` ([#6473](https://github.com/MetaMask/core/pull/6473)) +- Export `GasFeeMessenger` type ([#6386](https://github.com/MetaMask/core/pull/6386)) ### Changed From fa9084b5e406fa7f7db56f518a0ce00f3b775e19 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:18:59 +0200 Subject: [PATCH 103/247] migrate leftover `messagingSystem` usage --- packages/assets-controllers/src/TokenBalancesController.ts | 4 ++-- .../bridge-status-controller/src/bridge-status-controller.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 09688ae36e2..db8ba047d8e 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -259,12 +259,12 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ ); // Register action handlers for polling interval control - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `TokenBalancesController:updateChainPollingConfigs`, this.updateChainPollingConfigs.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `TokenBalancesController:getChainPollingConfig`, this.getChainPollingConfig.bind(this), ); diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 9369c66e31f..ae57a883577 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -187,7 +187,7 @@ export class BridgeStatusController extends StaticIntervalPollingController Date: Tue, 23 Sep 2025 15:24:09 +0200 Subject: [PATCH 104/247] fix changelogs --- packages/bridge-controller/CHANGELOG.md | 7 +++++-- packages/gas-fee-controller/CHANGELOG.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index e89855a914a..78f32d0cb74 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `BridgeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [44.0.0] ### Changed @@ -62,8 +67,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) - - Previously, `BridgeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - **BREAKING** Rename QuotesError and InputSourceDestinationSwitched events to match segment schema ([#6447](https://github.com/MetaMask/core/pull/6447)) - Bump `@metamask/base-controller` from `^8.2.0` to `^8.3.0` ([#6465](https://github.com/MetaMask/core/pull/6465)) - **BREAKING** Rename `gasless7702` to `gasIncluded7702` in QuoteRequest and Quote types diff --git a/packages/gas-fee-controller/CHANGELOG.md b/packages/gas-fee-controller/CHANGELOG.md index b8c1c7ea06c..374de43f2a6 100644 --- a/packages/gas-fee-controller/CHANGELOG.md +++ b/packages/gas-fee-controller/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add two new controller state metadata properties: `includeInStateLogs` and `usedInUi` ([#6473](https://github.com/MetaMask/core/pull/6473)) -- Export `GasFeeMessenger` type ([#6386](https://github.com/MetaMask/core/pull/6386)) +- Export `GasFeeMessenger` type ([#6386](https://github.com/MetaMask/core/pull/6386), [#6444](https://github.com/MetaMask/core/pull/6444)) ### Changed From 126e88c8950bdbd609b1d3dc0adc69735b0a3ed3 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:27:39 +0200 Subject: [PATCH 105/247] apply suggestion from @mcmire --- packages/assets-controllers/CHANGELOG.md | 4 ++++ packages/bridge-controller/CHANGELOG.md | 4 ++++ packages/bridge-controller/src/index.ts | 1 + 3 files changed, 9 insertions(+) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 50c5704a7e6..fd4558f8455 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add export for `CurrencyRateMessenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + ### Changed - **BREAKING:** Migrate the following controllers to the new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index 78f32d0cb74..9ca7e8c6786 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `BridgeControllerGetStateAction` type ([#6444](https://github.com/MetaMask/core/pull/6444)) + ### Changed - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) diff --git a/packages/bridge-controller/src/index.ts b/packages/bridge-controller/src/index.ts index 53f9ee0fa27..c4fa4f16416 100644 --- a/packages/bridge-controller/src/index.ts +++ b/packages/bridge-controller/src/index.ts @@ -57,6 +57,7 @@ export { RequestStatus, BridgeUserAction, BridgeBackgroundAction, + type BridgeControllerGetStateAction, } from './types'; export { From db25b2c7c3573555c6b671f211d7d973e2a6e2d1 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:33:56 +0200 Subject: [PATCH 106/247] update tsconfig files and README --- README.md | 2 ++ packages/delegation-controller/tsconfig.build.json | 3 ++- packages/delegation-controller/tsconfig.json | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eab983a1dc..836d609b2ea 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ linkStyle default opacity:0.5 composable_controller --> base_controller; composable_controller --> json_rpc_engine; delegation_controller --> base_controller; + delegation_controller --> messenger; delegation_controller --> accounts_controller; delegation_controller --> keyring_controller; earn_controller --> base_controller; @@ -292,6 +293,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/delegation-controller/tsconfig.build.json b/packages/delegation-controller/tsconfig.build.json index 573b24248e1..6f7018d977e 100644 --- a/packages/delegation-controller/tsconfig.build.json +++ b/packages/delegation-controller/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, - { "path": "../accounts-controller/tsconfig.build.json" } + { "path": "../accounts-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/delegation-controller/tsconfig.json b/packages/delegation-controller/tsconfig.json index e766ef509b6..2808844b345 100644 --- a/packages/delegation-controller/tsconfig.json +++ b/packages/delegation-controller/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../keyring-controller" }, - { "path": "../accounts-controller" } + { "path": "../accounts-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } From c0369df17cf7bbdf3570c3b86983a595dd2d2da2 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:34:56 +0200 Subject: [PATCH 107/247] rename `anonymous` to `includeInDebugSnapshot` --- packages/delegation-controller/src/DelegationController.test.ts | 2 +- packages/delegation-controller/src/DelegationController.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/delegation-controller/src/DelegationController.test.ts b/packages/delegation-controller/src/DelegationController.test.ts index 743ce196bbb..6c701639968 100644 --- a/packages/delegation-controller/src/DelegationController.test.ts +++ b/packages/delegation-controller/src/DelegationController.test.ts @@ -700,7 +700,7 @@ describe(`${controllerName}`, () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/delegation-controller/src/DelegationController.ts b/packages/delegation-controller/src/DelegationController.ts index 001ff3613d8..95a149ad3c7 100644 --- a/packages/delegation-controller/src/DelegationController.ts +++ b/packages/delegation-controller/src/DelegationController.ts @@ -23,7 +23,7 @@ const delegationControllerMetadata = { delegations: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, } satisfies StateMetadata; From fd4148a618e182d54729c6a00f3acec0695ea380 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:40:56 +0200 Subject: [PATCH 108/247] update tsconfig files and readme --- README.md | 2 ++ packages/ens-controller/tsconfig.build.json | 3 ++- packages/ens-controller/tsconfig.json | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eab983a1dc..25ade47e4f7 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,7 @@ linkStyle default opacity:0.5 eip1193_permission_middleware --> permission_controller; ens_controller --> base_controller; ens_controller --> controller_utils; + ens_controller --> messenger; ens_controller --> network_controller; error_reporting_service --> base_controller; eth_json_rpc_provider --> json_rpc_engine; @@ -292,6 +293,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/ens-controller/tsconfig.build.json b/packages/ens-controller/tsconfig.build.json index ac0df4920c6..c55f67af5af 100644 --- a/packages/ens-controller/tsconfig.build.json +++ b/packages/ens-controller/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, - { "path": "../network-controller/tsconfig.build.json" } + { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/ens-controller/tsconfig.json b/packages/ens-controller/tsconfig.json index 4bbb0be81b1..f62c7c2e078 100644 --- a/packages/ens-controller/tsconfig.json +++ b/packages/ens-controller/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../controller-utils" }, - { "path": "../network-controller" } + { "path": "../network-controller" }, + { "path": "../messenger" }, ], "include": ["../../types", "./src"] } From 954867d18a26bda6d76301fbec077d50b42c954c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:42:20 +0200 Subject: [PATCH 109/247] rename `anonymous` to `includeInDebugSnapshot` --- packages/ens-controller/src/EnsController.test.ts | 2 +- packages/ens-controller/src/EnsController.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/ens-controller/src/EnsController.test.ts b/packages/ens-controller/src/EnsController.test.ts index 01e55925463..52bd47d6875 100644 --- a/packages/ens-controller/src/EnsController.test.ts +++ b/packages/ens-controller/src/EnsController.test.ts @@ -730,7 +730,7 @@ describe('EnsController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/ens-controller/src/EnsController.ts b/packages/ens-controller/src/EnsController.ts index 8bffe59be21..0f8f6aa79b5 100644 --- a/packages/ens-controller/src/EnsController.ts +++ b/packages/ens-controller/src/EnsController.ts @@ -1,6 +1,7 @@ import { Web3Provider } from '@ethersproject/providers'; import { BaseController, + StateMetadata, type ControllerGetStateAction, type ControllerStateChangeEvent, } from '@metamask/base-controller/next'; @@ -93,17 +94,17 @@ export type EnsControllerMessenger = Messenger< EnsControllerEvents >; -const metadata = { +const metadata: StateMetadata = { ensEntries: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, ensResolutionsByAddress: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; From 24d9bf0ebd4b93fbb044057488e7c5cda7608a71 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:44:48 +0200 Subject: [PATCH 110/247] update tsconfig and README --- README.md | 2 ++ packages/earn-controller/tsconfig.build.json | 3 +++ packages/earn-controller/tsconfig.json | 3 +++ 3 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 4eab983a1dc..d46c36e5a5b 100644 --- a/README.md +++ b/README.md @@ -197,6 +197,7 @@ linkStyle default opacity:0.5 delegation_controller --> keyring_controller; earn_controller --> base_controller; earn_controller --> controller_utils; + earn_controller --> messenger; earn_controller --> account_tree_controller; earn_controller --> network_controller; earn_controller --> transaction_controller; @@ -292,6 +293,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/earn-controller/tsconfig.build.json b/packages/earn-controller/tsconfig.build.json index 439abdd5ef5..fe81e33f321 100644 --- a/packages/earn-controller/tsconfig.build.json +++ b/packages/earn-controller/tsconfig.build.json @@ -17,6 +17,9 @@ }, { "path": "../account-tree-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/earn-controller/tsconfig.json b/packages/earn-controller/tsconfig.json index 1b34af0ba0f..8bcc4e52ff3 100644 --- a/packages/earn-controller/tsconfig.json +++ b/packages/earn-controller/tsconfig.json @@ -16,6 +16,9 @@ }, { "path": "../account-tree-controller" + }, + { + "path": "../messenger" } ] } From 02795c3d66a4e80b8e55324619beb801231f8710 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:45:31 +0200 Subject: [PATCH 111/247] rename `anonymous` to `includeInDebugSnapshot` --- packages/earn-controller/src/EarnController.test.ts | 2 +- packages/earn-controller/src/EarnController.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/earn-controller/src/EarnController.test.ts b/packages/earn-controller/src/EarnController.test.ts index 8da6e348586..85996af48f6 100644 --- a/packages/earn-controller/src/EarnController.test.ts +++ b/packages/earn-controller/src/EarnController.test.ts @@ -2591,7 +2591,7 @@ describe('EarnController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/earn-controller/src/EarnController.ts b/packages/earn-controller/src/EarnController.ts index 50d17da3f66..1a3c367b620 100644 --- a/packages/earn-controller/src/EarnController.ts +++ b/packages/earn-controller/src/EarnController.ts @@ -117,19 +117,19 @@ const earnControllerMetadata: StateMetadata = { pooled_staking: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, lending: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, lastUpdated: { includeInStateLogs: true, persist: false, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, }; From 6b7daf788d0a9f29b730a9385bfadc7e6a4726ed Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:56:03 +0200 Subject: [PATCH 112/247] update tsconfig and README --- README.md | 2 ++ packages/gator-permissions-controller/tsconfig.build.json | 5 ++++- packages/gator-permissions-controller/tsconfig.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4eab983a1dc..440216b77c1 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,7 @@ linkStyle default opacity:0.5 gas_fee_controller --> polling_controller; gas_fee_controller --> network_controller; gator_permissions_controller --> base_controller; + gator_permissions_controller --> messenger; json_rpc_middleware_stream --> json_rpc_engine; keyring_controller --> base_controller; logging_controller --> base_controller; @@ -292,6 +293,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/gator-permissions-controller/tsconfig.build.json b/packages/gator-permissions-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 100644 --- a/packages/gator-permissions-controller/tsconfig.build.json +++ b/packages/gator-permissions-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/gator-permissions-controller/tsconfig.json b/packages/gator-permissions-controller/tsconfig.json index 34354c4b09d..68c3ddfc2cd 100644 --- a/packages/gator-permissions-controller/tsconfig.json +++ b/packages/gator-permissions-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../base-controller" }], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } From bc41fe414ba574ed07bf3f5d4e1e87bfa66e972d Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 15:58:19 +0200 Subject: [PATCH 113/247] rename `anonymous` to `includeInDebugSnapshot` --- .../src/GatorPermissionsController.test.ts | 12 ++--- .../src/GatorPermissionsController.ts | 53 ++++++++++--------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts index e3aee87d584..e56b2313e0d 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.test.ts @@ -397,21 +397,21 @@ describe('GatorPermissionsController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect( deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); it('includes expected state in state logs', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect( @@ -432,7 +432,7 @@ describe('GatorPermissionsController', () => { it('persists expected state', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect( @@ -451,7 +451,7 @@ describe('GatorPermissionsController', () => { it('exposes expected state to UI', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect( @@ -487,7 +487,7 @@ describe('GatorPermissionsController', () => { beforeEach(() => { controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); }); diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.ts index 9a32cb8efc3..017f3b3eff6 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.ts @@ -92,32 +92,33 @@ export type GatorPermissionsControllerState = { gatorPermissionsProviderSnapId: SnapId; }; -const gatorPermissionsControllerMetadata = { - isGatorPermissionsEnabled: { - includeInStateLogs: true, - persist: true, - anonymous: false, - usedInUi: false, - }, - gatorPermissionsMapSerialized: { - includeInStateLogs: true, - persist: true, - anonymous: false, - usedInUi: true, - }, - isFetchingGatorPermissions: { - includeInStateLogs: true, - persist: false, - anonymous: false, - usedInUi: false, - }, - gatorPermissionsProviderSnapId: { - includeInStateLogs: true, - persist: false, - anonymous: false, - usedInUi: false, - }, -} satisfies StateMetadata; +const gatorPermissionsControllerMetadata: StateMetadata = + { + isGatorPermissionsEnabled: { + includeInStateLogs: true, + persist: true, + includeInDebugSnapshot: false, + usedInUi: false, + }, + gatorPermissionsMapSerialized: { + includeInStateLogs: true, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + isFetchingGatorPermissions: { + includeInStateLogs: true, + persist: false, + includeInDebugSnapshot: false, + usedInUi: false, + }, + gatorPermissionsProviderSnapId: { + includeInStateLogs: true, + persist: false, + includeInDebugSnapshot: false, + usedInUi: false, + }, + } satisfies StateMetadata; /** * Constructs the default {@link GatorPermissionsController} state. This allows From 2e28e6997f383d2a84473d3d1ec9fbe057c98fae Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 16:21:40 +0200 Subject: [PATCH 114/247] update leftover `messagingSystem` reference --- .../src/GatorPermissionsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gator-permissions-controller/src/GatorPermissionsController.ts b/packages/gator-permissions-controller/src/GatorPermissionsController.ts index 017f3b3eff6..90122bea62d 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.ts @@ -293,7 +293,7 @@ export default class GatorPermissionsController extends BaseController< this.disableGatorPermissions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:decodePermissionFromPermissionContextForOrigin`, this.decodePermissionFromPermissionContextForOrigin.bind(this), ); From 1ce55a62259b61af9a510a61e7575d6cd7d35440 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 16:25:08 +0200 Subject: [PATCH 115/247] add changelog entry --- packages/gator-permissions-controller/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/gator-permissions-controller/CHANGELOG.md b/packages/gator-permissions-controller/CHANGELOG.md index fbcf1a68161..be507938012 100644 --- a/packages/gator-permissions-controller/CHANGELOG.md +++ b/packages/gator-permissions-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6445](https://github.com/MetaMask/core/pull/6445)) + - Previously, `GatorPermissionsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [0.2.0] ### Added From b20bbc70b63c34911b669cc60b9fc489a285dd8d Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 16:54:20 +0200 Subject: [PATCH 116/247] refactor: add `RootMessenger` --- .../src/KeyringController.test.ts | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/packages/keyring-controller/src/KeyringController.test.ts b/packages/keyring-controller/src/KeyringController.test.ts index c89e3610417..14d28b5cb76 100644 --- a/packages/keyring-controller/src/KeyringController.test.ts +++ b/packages/keyring-controller/src/KeyringController.test.ts @@ -1,7 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common'; import type { TypedTxData } from '@ethereumjs/tx'; import { TransactionFactory } from '@ethereumjs/tx'; -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { HdKeyring } from '@metamask/eth-hd-keyring'; import { normalize, @@ -14,7 +14,13 @@ import { import SimpleKeyring from '@metamask/eth-simple-keyring'; import type { EthKeyring } from '@metamask/keyring-internal-api'; import type { KeyringClass } from '@metamask/keyring-utils'; -import { Messenger } from '@metamask/messenger'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { wordlist } from '@metamask/scure-bip39/dist/wordlists/english'; import { bytesToHex, isValidHexAddress, type Hex } from '@metamask/utils'; import sinon from 'sinon'; @@ -45,6 +51,16 @@ import { MockKeyring } from '../tests/mocks/mockKeyring'; import MockShallowKeyring from '../tests/mocks/mockShallowKeyring'; import { buildMockTransaction } from '../tests/mocks/mockTransaction'; +type AllKeyringControllerActions = MessengerActions; + +type AllKeyringControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllKeyringControllerActions, + AllKeyringControllerEvents +>; + jest.mock('uuid', () => { return { ...jest.requireActual('uuid'), @@ -4357,7 +4373,7 @@ type WithControllerCallback = ({ controller: KeyringController; encryptor: MockEncryptor; initialState: KeyringControllerState; - messenger: KeyringControllerMessenger; + messenger: RootMessenger; }) => Promise | ReturnValue; type WithControllerOptions = Partial & { @@ -4388,16 +4404,28 @@ function stubKeyringClassWithAccount( } /** - * Build a restricted messenger for the keyring controller. + * Build a root messenger. + * + * @returns The root messenger. + */ +function buildRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); +} + +/** + * Build a messenger for the keyring controller. * + * @param messenger - An optional root messenger to use as the base for the + * controller messenger * @returns The keyring controller restricted messenger. */ -function buildKeyringControllerMessenger() { +function buildKeyringControllerMessenger(messenger = buildRootMessenger()) { return new Messenger< 'KeyringController', KeyringControllerActions, - KeyringControllerEvents - >({ namespace: 'KeyringController' }); + KeyringControllerEvents, + typeof messenger + >({ namespace: 'KeyringController', parent: messenger }); } /** @@ -4415,10 +4443,11 @@ async function withController( ): Promise { const [{ ...rest }, fn] = args.length === 2 ? args : [{}, args[0]]; const encryptor = new MockEncryptor(); - const messenger = buildKeyringControllerMessenger(); + const messenger = buildRootMessenger(); + const keyringControllerMessenger = buildKeyringControllerMessenger(messenger); const controller = new KeyringController({ encryptor, - messenger, + messenger: keyringControllerMessenger, ...rest, }); if (!rest.skipVaultCreation) { From 233b56fe9d7f0abbf1b172adae7ac16201c23d0b Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 16:55:27 +0200 Subject: [PATCH 117/247] rename `anonymous` to `includeInDebugSnapshot` --- .../keyring-controller/src/KeyringController.test.ts | 2 +- packages/keyring-controller/src/KeyringController.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/keyring-controller/src/KeyringController.test.ts b/packages/keyring-controller/src/KeyringController.test.ts index 14d28b5cb76..681f2c4f714 100644 --- a/packages/keyring-controller/src/KeyringController.test.ts +++ b/packages/keyring-controller/src/KeyringController.test.ts @@ -4289,7 +4289,7 @@ describe('KeyringController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index a5f6b58ee24..ab5d95fbe9c 100644 --- a/packages/keyring-controller/src/KeyringController.ts +++ b/packages/keyring-controller/src/KeyringController.ts @@ -680,31 +680,31 @@ export class KeyringController extends BaseController< vault: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, isUnlocked: { includeInStateLogs: true, persist: false, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, keyrings: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, encryptionKey: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, encryptionSalt: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }, From cd4a02072870895f68e270efca1e2ca3ae01f86c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 16:56:06 +0200 Subject: [PATCH 118/247] rename leftover `messagingSystem` to `messenger` --- packages/keyring-controller/src/KeyringController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index ab5d95fbe9c..c21b09d1136 100644 --- a/packages/keyring-controller/src/KeyringController.ts +++ b/packages/keyring-controller/src/KeyringController.ts @@ -1695,7 +1695,7 @@ export class KeyringController extends BaseController< } /** - * Constructor helper for registering this controller's messaging system + * Constructor helper for registering this controller's messeger * actions. */ #registerMessageHandlers() { @@ -1774,7 +1774,7 @@ export class KeyringController extends BaseController< this.withKeyring.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:addNewKeyring`, this.addNewKeyring.bind(this), ); From 45f1b2841c359dbcbff4fbd92ae39edbd46ae2a5 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 23 Sep 2025 16:57:06 +0200 Subject: [PATCH 119/247] update tsconfig and README --- README.md | 2 ++ packages/keyring-controller/tsconfig.build.json | 3 +++ packages/keyring-controller/tsconfig.json | 3 +++ 3 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 4eab983a1dc..7b506d7e0b4 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ linkStyle default opacity:0.5 gator_permissions_controller --> base_controller; json_rpc_middleware_stream --> json_rpc_engine; keyring_controller --> base_controller; + keyring_controller --> messenger; logging_controller --> base_controller; logging_controller --> controller_utils; message_manager --> base_controller; @@ -292,6 +293,7 @@ linkStyle default opacity:0.5 signature_controller --> controller_utils; signature_controller --> accounts_controller; signature_controller --> approval_controller; + signature_controller --> gator_permissions_controller; signature_controller --> keyring_controller; signature_controller --> logging_controller; signature_controller --> network_controller; diff --git a/packages/keyring-controller/tsconfig.build.json b/packages/keyring-controller/tsconfig.build.json index 38d8a31843f..0e0e00fc987 100644 --- a/packages/keyring-controller/tsconfig.build.json +++ b/packages/keyring-controller/tsconfig.build.json @@ -11,6 +11,9 @@ }, { "path": "../message-manager/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/keyring-controller/tsconfig.json b/packages/keyring-controller/tsconfig.json index 831b2ae3b47..aae4c04c485 100644 --- a/packages/keyring-controller/tsconfig.json +++ b/packages/keyring-controller/tsconfig.json @@ -9,6 +9,9 @@ }, { "path": "../message-manager" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src", "./tests"] From f8d5bfa5c18d8e1b330bc40417595eea04f93f1c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 24 Sep 2025 14:15:26 +0200 Subject: [PATCH 120/247] fix changelog link --- packages/gator-permissions-controller/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gator-permissions-controller/CHANGELOG.md b/packages/gator-permissions-controller/CHANGELOG.md index be507938012..d9a69287082 100644 --- a/packages/gator-permissions-controller/CHANGELOG.md +++ b/packages/gator-permissions-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6445](https://github.com/MetaMask/core/pull/6445)) +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6461](https://github.com/MetaMask/core/pull/6461)) - Previously, `GatorPermissionsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. ## [0.2.0] @@ -33,7 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial release ([#6033](https://github.com/MetaMask/core/pull/6033), [#6461](https://github.com/MetaMask/core/pull/6461)) +- Initial release ([#6033](https://github.com/MetaMask/core/pull/6033)) [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@0.2.0...HEAD [0.2.0]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@0.1.0...@metamask/gator-permissions-controller@0.2.0 From d927cc4310619c053b4e7459a5f6fd1dd281b826 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 24 Sep 2025 15:09:23 +0200 Subject: [PATCH 121/247] migrate `BlockTrackerPollingController` --- packages/polling-controller/CHANGELOG.md | 8 ++------ .../src/BlockTrackerPollingController.test.ts | 14 +++++++------- .../src/BlockTrackerPollingController.ts | 2 +- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/polling-controller/CHANGELOG.md b/packages/polling-controller/CHANGELOG.md index 37725c16de7..abf9b7b8c97 100644 --- a/packages/polling-controller/CHANGELOG.md +++ b/packages/polling-controller/CHANGELOG.md @@ -7,14 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added - -- Add `StaticIntervalPollingControllerNext` to extend from `@metamask/base-controller/next` ([#6386](https://github.com/MetaMask/core/pull/6386)) - ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` for `StaticIntervalPollingController` ([#6444](https://github.com/MetaMask/core/pull/6444)) - - Previously, `StaticIntervalPollingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` for `StaticIntervalPollingController` and `BlockTrackerPollingController` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `StaticIntervalPollingController` and `BlockTrackerPollingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/controller-utils` from `^11.10.0` to `^11.14.0` ([#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303), [#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629)) - Bump `@metamask/utils` from `^11.2.0` to `^11.4.2` ([#6054](https://github.com/MetaMask/core/pull/6054)) diff --git a/packages/polling-controller/src/BlockTrackerPollingController.test.ts b/packages/polling-controller/src/BlockTrackerPollingController.test.ts index d5c8615a3ff..5824691a81c 100644 --- a/packages/polling-controller/src/BlockTrackerPollingController.test.ts +++ b/packages/polling-controller/src/BlockTrackerPollingController.test.ts @@ -1,4 +1,8 @@ -import { Messenger } from '@metamask/base-controller'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + MockAnyNamespace, +} from '@metamask/messenger'; import type { NetworkClient } from '@metamask/network-controller'; import EventEmitter from 'events'; import { useFakeTimers } from 'sinon'; @@ -43,17 +47,13 @@ class TestBlockTracker extends EventEmitter { describe('BlockTrackerPollingController', () => { let clock: sinon.SinonFakeTimers; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let mockMessenger: any; + let mockMessenger: Messenger; let controller: ChildBlockTrackerPollingController; let mainnetBlockTracker: TestBlockTracker; let goerliBlockTracker: TestBlockTracker; let sepoliaBlockTracker: TestBlockTracker; beforeEach(() => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockMessenger = new Messenger(); + mockMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); controller = new ChildBlockTrackerPollingController({ messenger: mockMessenger, metadata: {}, diff --git a/packages/polling-controller/src/BlockTrackerPollingController.ts b/packages/polling-controller/src/BlockTrackerPollingController.ts index f7221768f90..6f4725577e3 100644 --- a/packages/polling-controller/src/BlockTrackerPollingController.ts +++ b/packages/polling-controller/src/BlockTrackerPollingController.ts @@ -1,4 +1,4 @@ -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import type { NetworkClientId, NetworkClient, From 601100bd065ce9fa368cc01f1f455333840848b8 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 24 Sep 2025 15:10:19 +0200 Subject: [PATCH 122/247] update tsconfig and README --- README.md | 2 ++ packages/polling-controller/tsconfig.build.json | 3 ++- packages/polling-controller/tsconfig.json | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fb4cf4a5ee3..baa962a1511 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ linkStyle default opacity:0.5 bridge_controller --> base_controller; bridge_controller --> controller_utils; bridge_controller --> gas_fee_controller; + bridge_controller --> messenger; bridge_controller --> multichain_network_controller; bridge_controller --> polling_controller; bridge_controller --> accounts_controller; @@ -305,6 +306,7 @@ linkStyle default opacity:0.5 token_search_discovery_controller --> base_controller; transaction_controller --> base_controller; transaction_controller --> controller_utils; + transaction_controller --> messenger; transaction_controller --> accounts_controller; transaction_controller --> approval_controller; transaction_controller --> eth_json_rpc_provider; diff --git a/packages/polling-controller/tsconfig.build.json b/packages/polling-controller/tsconfig.build.json index ac0df4920c6..c55f67af5af 100644 --- a/packages/polling-controller/tsconfig.build.json +++ b/packages/polling-controller/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, - { "path": "../network-controller/tsconfig.build.json" } + { "path": "../network-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/polling-controller/tsconfig.json b/packages/polling-controller/tsconfig.json index 9f1187b22dc..ca0d50a83f4 100644 --- a/packages/polling-controller/tsconfig.json +++ b/packages/polling-controller/tsconfig.json @@ -7,7 +7,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../controller-utils" }, - { "path": "../network-controller" } + { "path": "../network-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src", "../../tests"] } From 6d0f21d74d46b575c862dcfeda1a423fed68e4f4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 24 Sep 2025 15:12:15 +0200 Subject: [PATCH 123/247] fix test types --- .../src/BlockTrackerPollingController.test.ts | 2 +- .../src/StaticIntervalPollingController.test.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/polling-controller/src/BlockTrackerPollingController.test.ts b/packages/polling-controller/src/BlockTrackerPollingController.test.ts index 5824691a81c..88321e80b97 100644 --- a/packages/polling-controller/src/BlockTrackerPollingController.test.ts +++ b/packages/polling-controller/src/BlockTrackerPollingController.test.ts @@ -1,7 +1,7 @@ import { MOCK_ANY_NAMESPACE, Messenger, - MockAnyNamespace, + type MockAnyNamespace, } from '@metamask/messenger'; import type { NetworkClient } from '@metamask/network-controller'; import EventEmitter from 'events'; diff --git a/packages/polling-controller/src/StaticIntervalPollingController.test.ts b/packages/polling-controller/src/StaticIntervalPollingController.test.ts index 797f431bc88..380465867e8 100644 --- a/packages/polling-controller/src/StaticIntervalPollingController.test.ts +++ b/packages/polling-controller/src/StaticIntervalPollingController.test.ts @@ -1,4 +1,8 @@ -import { Messenger } from '@metamask/base-controller'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MockAnyNamespace, +} from '@metamask/messenger'; import { createDeferredPromise } from '@metamask/utils'; import { useFakeTimers } from 'sinon'; @@ -39,14 +43,10 @@ class ChildBlockTrackerPollingController extends StaticIntervalPollingController describe('StaticIntervalPollingController', () => { let clock: sinon.SinonFakeTimers; - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let mockMessenger: any; + let mockMessenger: Messenger; let controller: ChildBlockTrackerPollingController; beforeEach(() => { - // TODO: Replace `any` with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mockMessenger = new Messenger(); + mockMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); controller = new ChildBlockTrackerPollingController({ messenger: mockMessenger, metadata: {}, From 203db7f6837ec83884bd59d0dfe51e2bea9157ce Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 24 Sep 2025 15:54:55 +0200 Subject: [PATCH 124/247] refactor: migrate `ComposableController` to `@metamask/messenger` --- packages/composable-controller/package.json | 3 +- .../src/ComposableController.test.ts | 356 +++++++++--------- .../src/ComposableController.ts | 46 ++- yarn.lock | 1 + 4 files changed, 203 insertions(+), 203 deletions(-) diff --git a/packages/composable-controller/package.json b/packages/composable-controller/package.json index 27313b5a3b9..b1f305e9eec 100644 --- a/packages/composable-controller/package.json +++ b/packages/composable-controller/package.json @@ -47,7 +47,8 @@ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { - "@metamask/base-controller": "^8.2.0" + "@metamask/base-controller": "^8.2.0", + "@metamask/messenger": "^0.3.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/composable-controller/src/ComposableController.test.ts b/packages/composable-controller/src/ComposableController.test.ts index c5c8bce434f..f3304c186da 100644 --- a/packages/composable-controller/src/ComposableController.test.ts +++ b/packages/composable-controller/src/ComposableController.test.ts @@ -1,11 +1,23 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController, Messenger } from '@metamask/base-controller'; +import { + BaseController, + ControllerStateChangeEvent, + type ControllerGetStateAction, + StateConstraint, +} from '@metamask/base-controller/next'; import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + MessengerActions, + MessengerEvents, + MockAnyNamespace, +} from '@metamask/messenger'; import type { Patch } from 'immer'; import * as sinon from 'sinon'; import type { ChildControllerStateChangeEvents, + ComposableControllerActions, ComposableControllerEvents, } from './ComposableController'; import { @@ -15,20 +27,29 @@ import { // Mock BaseController classes +type RootMessenger = Messenger< + MockAnyNamespace, + MessengerActions | MessengerActions, + MessengerEvents | MessengerEvents +>; + type FooControllerState = { foo: string; }; +type FooControllerAction = ControllerGetStateAction< + 'FooController', + FooControllerState +>; type FooControllerEvent = { type: `FooController:stateChange`; payload: [FooControllerState, Patch[]]; }; -type FooMessenger = RestrictedMessenger< +type FooMessenger = Messenger< 'FooController', - never, + FooControllerAction, FooControllerEvent | QuzControllerEvent, - never, - QuzControllerEvent['type'] + RootMessenger >; const fooControllerStateMetadata = { @@ -62,17 +83,20 @@ class FooController extends BaseController< type QuzControllerState = { quz: string; }; +type QuzControllerAction = ControllerGetStateAction< + 'QuzController', + QuzControllerState +>; type QuzControllerEvent = { type: `QuzController:stateChange`; payload: [QuzControllerState, Patch[]]; }; -type QuzMessenger = RestrictedMessenger< +type QuzMessenger = Messenger< 'QuzController', - never, + QuzControllerAction, QuzControllerEvent, - never, - never + RootMessenger >; const quzControllerStateMetadata = { @@ -103,50 +127,17 @@ class QuzController extends BaseController< } } -type ControllerWithoutStateChangeEventState = { - qux: string; -}; - -type ControllerWithoutStateChangeEventMessenger = RestrictedMessenger< - 'ControllerWithoutStateChangeEvent', - never, - QuzControllerEvent, - never, - QuzControllerEvent['type'] +type ComposableControllerMessenger = Messenger< + 'ComposableController', + ControllerGetStateAction<'ComposableController', State>, + | ControllerStateChangeEvent<'ComposableController', State> + | FooControllerEvent, + RootMessenger >; -const controllerWithoutStateChangeEventStateMetadata = { - qux: { - persist: true, - anonymous: true, - }, -}; - -class ControllerWithoutStateChangeEvent extends BaseController< - 'ControllerWithoutStateChangeEvent', - ControllerWithoutStateChangeEventState, - ControllerWithoutStateChangeEventMessenger -> { - constructor(messagingSystem: ControllerWithoutStateChangeEventMessenger) { - super({ - messenger: messagingSystem, - metadata: controllerWithoutStateChangeEventStateMetadata, - name: 'ControllerWithoutStateChangeEvent', - state: { qux: 'qux' }, - }); - } - - updateState(qux: string) { - super.update((state) => { - state.qux = qux; - }); - } -} - type ControllersMap = { FooController: FooController; QuzController: QuzController; - ControllerWithoutStateChangeEvent: ControllerWithoutStateChangeEvent; }; describe('ComposableController', () => { @@ -157,39 +148,39 @@ describe('ComposableController', () => { describe('BaseController', () => { it('should compose controller state', () => { type ComposableControllerState = { - FooController: FooControllerState; QuzController: QuzControllerState; + FooController: FooControllerState; }; - const messenger = new Messenger< - never, - | ComposableControllerEvents - | FooControllerEvent - | QuzControllerEvent - >(); - const fooMessenger = messenger.getRestricted< - 'FooController', - never, - QuzControllerEvent['type'] - >({ - name: 'FooController', - allowedActions: [], - allowedEvents: ['QuzController:stateChange'], + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, }); - const quzMessenger = messenger.getRestricted({ - name: 'QuzController', - allowedActions: [], - allowedEvents: [], + const fooMessenger: FooMessenger = new Messenger({ + namespace: 'FooController', + parent: messenger, + }); + messenger.delegate({ + messenger: fooMessenger, + events: ['QuzController:stateChange'], + }); + const quzMessenger: QuzMessenger = new Messenger({ + namespace: 'QuzController', + parent: messenger, }); const fooController = new FooController(fooMessenger); const quzController = new QuzController(quzMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: [ - 'FooController:stateChange', - 'QuzController:stateChange', - ], + const composableControllerMessenger = new Messenger< + 'ComposableController', + never, + FooControllerEvent | QuzControllerEvent, + RootMessenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + composableControllerMessenger.delegate({ + messenger: fooMessenger, + events: ['FooController:stateChange', 'QuzController:stateChange'], }); const composableController = new ComposableController< ComposableControllerState, @@ -212,20 +203,32 @@ describe('ComposableController', () => { FooController: FooControllerState; }; const messenger = new Messenger< - never, - | ComposableControllerEvents + MockAnyNamespace, + | FooControllerAction + | ComposableControllerActions, | FooControllerEvent - >(); - const fooControllerMessenger = messenger.getRestricted({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + | ComposableControllerEvents + >({ + namespace: MOCK_ANY_NAMESPACE, + }); + const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerAction, + FooControllerEvent, + typeof messenger + >({ + namespace: 'FooController', + parent: messenger, }); const fooController = new FooController(fooControllerMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], + const composableControllerMessenger: ComposableControllerMessenger = + new Messenger({ + namespace: 'ComposableController', + parent: messenger, + }); + composableControllerMessenger.delegate({ + messenger: fooControllerMessenger, + events: ['FooController:stateChange'], }); new ComposableController< ComposableControllerState, @@ -256,26 +259,47 @@ describe('ComposableController', () => { FooController: FooControllerState; }; const messenger = new Messenger< - never, + MockAnyNamespace, + | ComposableControllerActions + | QuzControllerAction + | FooControllerAction, | ComposableControllerEvents | ChildControllerStateChangeEvents - >(); - const quzControllerMessenger = messenger.getRestricted({ - name: 'QuzController', - allowedActions: [], - allowedEvents: [], + >({ namespace: MOCK_ANY_NAMESPACE }); + const quzControllerMessenger = new Messenger< + 'QuzController', + QuzControllerAction, + QuzControllerEvent, + typeof messenger + >({ + namespace: 'QuzController', + parent: messenger, }); const quzController = new QuzController(quzControllerMessenger); - const fooControllerMessenger = messenger.getRestricted({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerAction, + FooControllerEvent, + typeof messenger + >({ + namespace: 'FooController', + parent: messenger, }); const fooController = new FooController(fooControllerMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['QuzController:stateChange', 'FooController:stateChange'], + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent + | QuzControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['QuzController:stateChange', 'FooController:stateChange'], }); new ComposableController< ComposableControllerState, @@ -304,17 +328,29 @@ describe('ComposableController', () => { }); it('should throw if controller messenger not provided', () => { - const messenger = new Messenger(); - const quzControllerMessenger = messenger.getRestricted({ - name: 'QuzController', - allowedActions: [], - allowedEvents: [], + const messenger = new Messenger< + MockAnyNamespace, + QuzControllerAction | FooControllerAction, + QuzControllerEvent | FooControllerEvent + >({ namespace: MOCK_ANY_NAMESPACE }); + const quzControllerMessenger = new Messenger< + 'QuzController', + QuzControllerAction, + QuzControllerEvent, + typeof messenger + >({ + namespace: 'QuzController', + parent: messenger, }); const quzController = new QuzController(quzControllerMessenger); - const fooControllerMessenger = messenger.getRestricted({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerAction, + FooControllerEvent, + typeof messenger + >({ + namespace: 'FooController', + parent: messenger, }); const fooController = new FooController(fooControllerMessenger); expect( @@ -335,19 +371,34 @@ describe('ComposableController', () => { }; const notController = new JsonRpcEngine(); const messenger = new Messenger< - never, + MockAnyNamespace, + | ComposableControllerActions + | FooControllerAction, ComposableControllerEvents | FooControllerEvent - >(); - const fooControllerMessenger = messenger.getRestricted({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + >({ namespace: MOCK_ANY_NAMESPACE }); + const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerAction, + FooControllerEvent, + typeof messenger + >({ + namespace: 'FooController', + parent: messenger, }); const fooController = new FooController(fooControllerMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], }); expect( () => @@ -369,71 +420,4 @@ describe('ComposableController', () => { }), ).toThrow(INVALID_CONTROLLER_ERROR); }); - - it('should not throw if composing a controller without a `stateChange` event', () => { - const messenger = new Messenger(); - const controllerWithoutStateChangeEventMessenger = messenger.getRestricted({ - name: 'ControllerWithoutStateChangeEvent', - allowedActions: [], - allowedEvents: [], - }); - const controllerWithoutStateChangeEvent = - new ControllerWithoutStateChangeEvent( - controllerWithoutStateChangeEventMessenger, - ); - const fooControllerMessenger = messenger.getRestricted({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], - }); - const fooController = new FooController(fooControllerMessenger); - expect( - () => - new ComposableController({ - controllers: { - ControllerWithoutStateChangeEvent: - controllerWithoutStateChangeEvent, - FooController: fooController, - }, - messenger: messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], - }), - }), - ).not.toThrow(); - }); - - it('should not throw if a child controller `stateChange` event is missing from the messenger events allowlist', () => { - const messenger = new Messenger< - never, - FooControllerEvent | QuzControllerEvent - >(); - const QuzControllerMessenger = messenger.getRestricted({ - name: 'QuzController', - allowedActions: [], - allowedEvents: [], - }); - const quzController = new QuzController(QuzControllerMessenger); - const fooControllerMessenger = messenger.getRestricted({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], - }); - const fooController = new FooController(fooControllerMessenger); - expect( - () => - new ComposableController({ - controllers: { - QuzController: quzController, - FooController: fooController, - }, - messenger: messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], - }), - }), - ).not.toThrow(); - }); }); diff --git a/packages/composable-controller/src/ComposableController.ts b/packages/composable-controller/src/ComposableController.ts index 8b2d908fb79..e802c97877e 100644 --- a/packages/composable-controller/src/ComposableController.ts +++ b/packages/composable-controller/src/ComposableController.ts @@ -1,12 +1,13 @@ import type { - RestrictedMessenger, StateConstraint, StateMetadata, StateMetadataConstraint, ControllerStateChangeEvent, + ControllerGetStateAction, BaseControllerInstance as ControllerInstance, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; export const controllerName = 'ComposableController'; @@ -20,6 +21,15 @@ export type ComposableControllerStateConstraint = { [controllerName: string]: StateConstraint; }; +/** + * The `getState` action type for the {@link ComposableControllerMessenger}. + * + * @template ComposableControllerState - A type object that maps controller names to their state types. + */ +export type ComposableControllerGetStateAction< + ComposableControllerState extends ComposableControllerStateConstraint, +> = ControllerGetStateAction; + /** * The `stateChange` event type for the {@link ComposableControllerMessenger}. * @@ -41,6 +51,15 @@ export type ComposableControllerEvents< ComposableControllerState extends ComposableControllerStateConstraint, > = ComposableControllerStateChangeEvent; +/** + * A union type of action types available to the {@link ComposableControllerMessenger}. + * + * @template ComposableControllerState - A type object that maps controller names to their state types. + */ +export type ComposableControllerActions< + ComposableControllerState extends ComposableControllerStateConstraint, +> = ComposableControllerGetStateAction; + /** * A utility type that extracts controllers from the {@link ComposableControllerState} type, * and derives a union type of all of their corresponding `stateChange` events. @@ -75,13 +94,11 @@ export type AllowedEvents< */ export type ComposableControllerMessenger< ComposableControllerState extends ComposableControllerStateConstraint, -> = RestrictedMessenger< +> = Messenger< typeof controllerName, - never, + ComposableControllerActions, | ComposableControllerEvents - | AllowedEvents, - never, - AllowedEvents['type'] + | AllowedEvents >; /** @@ -106,7 +123,7 @@ export class ComposableController< * * @param options - Initial options used to configure this controller * @param options.controllers - An object that contains child controllers keyed by their names. - * @param options.messenger - A restricted messenger. + * @param options.messenger - A controller messenger. */ constructor({ controllers, @@ -141,6 +158,7 @@ export class ComposableController< }, {} as never, ), + // @ts-expect-error "Property 'messagingSystem' is missing in type ..." messenger, }); @@ -161,16 +179,12 @@ export class ComposableController< delete this.metadata[name]; delete this.state[name]; // eslint-disable-next-line no-empty - } catch (_) {} - // False negative. `name` is a string type. - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + } catch {} throw new Error(`${name} - ${INVALID_CONTROLLER_ERROR}`); } try { - this.messagingSystem.subscribe( - // False negative. `name` is a string type. - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - `${name}:stateChange`, + this.messenger.subscribe( + `${controllerName}:stateChange`, (childState: StateConstraint) => { this.update((state) => { // Type assertion is necessary for property assignment to a generic type. This does not pollute or widen the type of the asserted variable. diff --git a/yarn.lock b/yarn.lock index 6ba685274fa..1e0f3f157e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2864,6 +2864,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.2.0" "@metamask/json-rpc-engine": "npm:^10.0.3" + "@metamask/messenger": "npm:^0.1.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" From 36cfd5d05682a1981f18d5ec75c953d9bc29255f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 10:21:28 +0200 Subject: [PATCH 125/247] migrate `MultichainAssetsController` messenger --- .../MultichainAssetsController.test.ts | 62 +++++++++------- .../MultichainAssetsController.ts | 73 +++++++++---------- 2 files changed, 72 insertions(+), 63 deletions(-) diff --git a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts index 9f07e38a6ab..1bc95642ae5 100644 --- a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts +++ b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { AccountAssetListUpdatedEventPayload, CaipAssetTypeOrId, @@ -11,6 +11,13 @@ import { } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { PermissionConstraint } from '@metamask/permission-controller'; import type { SubjectPermissions } from '@metamask/permission-controller'; import type { Snap } from '@metamask/snaps-utils'; @@ -27,10 +34,6 @@ import type { MultichainAssetsControllerState, } from './MultichainAssetsController'; import { advanceTime } from '../../../../tests/helpers'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; const mockSolanaAccount: InternalAccount = { type: 'solana:data-account', @@ -216,21 +219,26 @@ const mockGetMetadataReturnValue: AssetMetadataResponse | undefined = { /** * The union of actions that the root messenger allows. */ -type RootAction = ExtractAvailableAction; +type RootAction = MessengerActions; /** * The union of events that the root messenger allows. */ -type RootEvent = ExtractAvailableEvent; +type RootEvent = MessengerEvents; + +/** + * The root messenger type. + */ +type RootMessenger = Messenger; /** - * Constructs the unrestricted messenger. This can be used to call actions and + * Constructs the root messenger. This can be used to call actions and * publish events within the tests for this controller. * - * @returns The unrestricted messenger suited for MultichainAssetsController. + * @returns The root messenger suited for MultichainAssetsController. */ -function getRootMessenger(): Messenger { - return new Messenger(); +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } const setupController = ({ @@ -248,20 +256,24 @@ const setupController = ({ const messenger = getRootMessenger(); const multichainAssetsControllerMessenger: MultichainAssetsControllerMessenger = - messenger.getRestricted({ - name: 'MultichainAssetsController', - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'SnapController:handleRequest', - 'SnapController:getAll', - 'PermissionController:getPermissions', - ], - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - 'AccountsController:accountAssetListUpdated', - ], + new Messenger({ + namespace: 'MultichainAssetsController', + parent: messenger, }); + messenger.delegate({ + messenger: multichainAssetsControllerMessenger, + actions: [ + 'AccountsController:listMultichainAccounts', + 'SnapController:handleRequest', + 'SnapController:getAll', + 'PermissionController:getPermissions', + ], + events: [ + 'AccountsController:accountAdded', + 'AccountsController:accountRemoved', + 'AccountsController:accountAssetListUpdated', + ], + }); const mockSnapHandleRequest = jest.fn(); messenger.registerActionHandler( @@ -827,7 +839,7 @@ describe('MultichainAssetsController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts index 5b477a83c70..81358d1378a 100644 --- a/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts +++ b/packages/assets-controllers/src/MultichainAssetsController/MultichainAssetsController.ts @@ -8,8 +8,8 @@ import { BaseController, type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, -} from '@metamask/base-controller'; + type StateMetadata, +} from '@metamask/base-controller/next'; import { isEvmAccountType } from '@metamask/keyring-api'; import type { AccountAssetListUpdatedEventPayload, @@ -18,6 +18,7 @@ import type { } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { KeyringClient } from '@metamask/keyring-snap-client'; +import type { Messenger } from '@metamask/messenger'; import type { GetPermissions, PermissionConstraint, @@ -141,12 +142,10 @@ type AllowedEvents = /** * Messenger type for the MultichainAssetsController. */ -export type MultichainAssetsControllerMessenger = RestrictedMessenger< +export type MultichainAssetsControllerMessenger = Messenger< typeof controllerName, MultichainAssetsControllerActions | AllowedActions, - MultichainAssetsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + MultichainAssetsControllerEvents | AllowedEvents >; /** @@ -156,20 +155,21 @@ export type MultichainAssetsControllerMessenger = RestrictedMessenger< * using the `persist` flag; and if they can be sent to Sentry or not, using * the `anonymous` flag. */ -const assetsControllerMetadata = { - assetsMetadata: { - includeInStateLogs: false, - persist: true, - anonymous: false, - usedInUi: true, - }, - accountsAssets: { - includeInStateLogs: false, - persist: true, - anonymous: false, - usedInUi: true, - }, -}; +const assetsControllerMetadata: StateMetadata = + { + assetsMetadata: { + includeInStateLogs: false, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + accountsAssets: { + includeInStateLogs: false, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + }; // TODO: make this controller extends StaticIntervalPollingController and update all assetsMetadata once a day. @@ -202,15 +202,15 @@ export class MultichainAssetsController extends BaseController< this.#snaps = {}; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountAdded', async (account) => await this.#handleOnAccountAddedEvent(account), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountRemoved', async (account) => await this.#handleOnAccountRemovedEvent(account), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountAssetListUpdated', async (event) => await this.#handleAccountAssetListUpdatedEvent(event), ); @@ -237,7 +237,7 @@ export class MultichainAssetsController extends BaseController< * actions. */ #registerMessageHandlers() { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'MultichainAssetsController:getAssetMetadata', this.getAssetMetadata.bind(this), ); @@ -323,7 +323,7 @@ export class MultichainAssetsController extends BaseController< // Trigger fetching metadata for new assets await this.#refreshAssetsMetadata(Array.from(assetsForMetadataRefresh)); - this.messagingSystem.publish(`${controllerName}:accountAssetListUpdated`, { + this.messenger.publish(`${controllerName}:accountAssetListUpdated`, { assets: accountsAndAssetsToUpdate, }); } @@ -364,17 +364,14 @@ export class MultichainAssetsController extends BaseController< this.update((state) => { state.accountsAssets[account.id] = assets; }); - this.messagingSystem.publish( - `${controllerName}:accountAssetListUpdated`, - { - assets: { - [account.id]: { - added: assets, - removed: [], - }, + this.messenger.publish(`${controllerName}:accountAssetListUpdated`, { + assets: { + [account.id]: { + added: assets, + removed: [], }, }, - ); + }); } } @@ -510,7 +507,7 @@ export class MultichainAssetsController extends BaseController< */ #getAllSnaps(): Snap[] { // TODO: Use dedicated SnapController's action once available for this: - return this.messagingSystem + return this.messenger .call('SnapController:getAll') .filter((snap) => snap.enabled && !snap.blocked); } @@ -524,7 +521,7 @@ export class MultichainAssetsController extends BaseController< #getSnapsPermissions( origin: string, ): SubjectPermissions { - return this.messagingSystem.call( + return this.messenger.call( 'PermissionController:getPermissions', origin, ) as SubjectPermissions; @@ -542,7 +539,7 @@ export class MultichainAssetsController extends BaseController< snapId: string, ): Promise { try { - return (await this.messagingSystem.call('SnapController:handleRequest', { + return (await this.messenger.call('SnapController:handleRequest', { snapId: snapId as SnapId, origin: 'metamask', handler: HandlerType.OnAssetsLookup, @@ -584,7 +581,7 @@ export class MultichainAssetsController extends BaseController< #getClient(snapId: string): KeyringClient { return new KeyringClient({ send: async (request: JsonRpcRequest) => - (await this.messagingSystem.call('SnapController:handleRequest', { + (await this.messenger.call('SnapController:handleRequest', { snapId: snapId as SnapId, origin: 'metamask', handler: HandlerType.OnKeyringRequest, From bffbca08bdff8de61065a96975acaf6895d78594 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 10:40:19 +0200 Subject: [PATCH 126/247] rename `anonymous` to `includeInDebugSnapshot` --- .../src/AccountTrackerController.test.ts | 2 +- .../src/AccountTrackerController.ts | 5 +++-- .../src/CurrencyRateController.test.ts | 10 +++++----- .../src/CurrencyRateController.ts | 7 ++++--- .../DeFiPositionsController.test.ts | 4 ++-- .../DeFiPositionsController.ts | 4 ++-- .../MultichainAssetsRatesController.test.ts | 2 +- .../MultichainAssetsRatesController.ts | 7 ++++--- .../src/TokenBalancesController.test.ts | 2 +- .../src/TokenBalancesController.ts | 5 +++-- .../src/TokenListController.test.ts | 2 +- .../src/TokenListController.ts | 7 ++++--- .../src/TokenRatesController.test.ts | 2 +- .../src/TokenRatesController.ts | 5 +++-- .../src/bridge-controller.test.ts | 4 ++-- .../bridge-controller/src/bridge-controller.ts | 18 +++++++++--------- .../src/bridge-status-controller.test.ts | 2 +- .../src/bridge-status-controller.ts | 4 ++-- 18 files changed, 49 insertions(+), 43 deletions(-) diff --git a/packages/assets-controllers/src/AccountTrackerController.test.ts b/packages/assets-controllers/src/AccountTrackerController.test.ts index 2f5d80abbad..b999ceed0e9 100644 --- a/packages/assets-controllers/src/AccountTrackerController.test.ts +++ b/packages/assets-controllers/src/AccountTrackerController.test.ts @@ -1293,7 +1293,7 @@ describe('AccountTrackerController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/AccountTrackerController.ts b/packages/assets-controllers/src/AccountTrackerController.ts index 2e9966fcb9a..a4bac5fd620 100644 --- a/packages/assets-controllers/src/AccountTrackerController.ts +++ b/packages/assets-controllers/src/AccountTrackerController.ts @@ -8,6 +8,7 @@ import type { import type { ControllerStateChangeEvent, ControllerGetStateAction, + StateMetadata, } from '@metamask/base-controller/next'; import { query, @@ -120,11 +121,11 @@ export type AccountTrackerControllerState = { accountsByChainId: Record; }; -const accountTrackerMetadata = { +const accountTrackerMetadata: StateMetadata = { accountsByChainId: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; diff --git a/packages/assets-controllers/src/CurrencyRateController.test.ts b/packages/assets-controllers/src/CurrencyRateController.test.ts index 03e304098bc..e11d7683785 100644 --- a/packages/assets-controllers/src/CurrencyRateController.test.ts +++ b/packages/assets-controllers/src/CurrencyRateController.test.ts @@ -798,14 +798,14 @@ describe('CurrencyRateController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const controller = new CurrencyRateController({ - messenger: getRestrictedMessenger(), + messenger: getCurrencyRateControllerMessenger(), }); expect( deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -823,7 +823,7 @@ describe('CurrencyRateController', () => { it('includes expected state in state logs', () => { const controller = new CurrencyRateController({ - messenger: getRestrictedMessenger(), + messenger: getCurrencyRateControllerMessenger(), }); expect( @@ -848,7 +848,7 @@ describe('CurrencyRateController', () => { it('persists expected state', () => { const controller = new CurrencyRateController({ - messenger: getRestrictedMessenger(), + messenger: getCurrencyRateControllerMessenger(), }); expect( @@ -873,7 +873,7 @@ describe('CurrencyRateController', () => { it('exposes expected state to UI', () => { const controller = new CurrencyRateController({ - messenger: getRestrictedMessenger(), + messenger: getCurrencyRateControllerMessenger(), }); expect( diff --git a/packages/assets-controllers/src/CurrencyRateController.ts b/packages/assets-controllers/src/CurrencyRateController.ts index 66da864fab8..a116e8ac337 100644 --- a/packages/assets-controllers/src/CurrencyRateController.ts +++ b/packages/assets-controllers/src/CurrencyRateController.ts @@ -1,6 +1,7 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { TESTNET_TICKER_SYMBOLS, @@ -57,17 +58,17 @@ export type CurrencyRateMessenger = Messenger< CurrencyRateControllerEvents >; -const metadata = { +const metadata: StateMetadata = { currentCurrency: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, currencyRates: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; diff --git a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts index 7ddc2b9c408..cd5b5f3f8dc 100644 --- a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts +++ b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { BtcAccountType } from '@metamask/keyring-api'; import { MOCK_ANY_NAMESPACE, @@ -532,7 +532,7 @@ describe('DeFiPositionsController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts index a6b5730fa0f..9b53e15443c 100644 --- a/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts +++ b/packages/assets-controllers/src/DeFiPositionsController/DeFiPositionsController.ts @@ -70,13 +70,13 @@ const controllerMetadata: StateMetadata = { allDeFiPositions: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, allDeFiPositionsCount: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; diff --git a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts index 9dda89b4342..7ce1cdc7b9c 100644 --- a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts +++ b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts @@ -1080,7 +1080,7 @@ describe('MultichainAssetsRatesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts index 866a6057c2b..4aa863496d0 100644 --- a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts +++ b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts @@ -6,6 +6,7 @@ import type { import type { ControllerStateChangeEvent, ControllerGetStateAction, + StateMetadata, } from '@metamask/base-controller/next'; import { type CaipAssetType, isEvmAccountType } from '@metamask/keyring-api'; import type { @@ -157,17 +158,17 @@ export type MultichainAssetsRatesPollingInput = { accountId: string; }; -const metadata = { +const metadata: StateMetadata = { conversionRates: { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, historicalPrices: { includeInStateLogs: false, persist: false, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 07a02719910..53a016626dc 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -4121,7 +4121,7 @@ describe('TokenBalancesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index db8ba047d8e..89e98b72e9f 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -6,6 +6,7 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { BNToHex, @@ -54,11 +55,11 @@ export type ChecksumAddress = Hex; const CONTROLLER = 'TokenBalancesController' as const; const DEFAULT_INTERVAL_MS = 180_000; // 3 minutes -const metadata = { +const metadata: StateMetadata = { tokenBalances: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; diff --git a/packages/assets-controllers/src/TokenListController.test.ts b/packages/assets-controllers/src/TokenListController.test.ts index e4ee22a8979..84ff24accbd 100644 --- a/packages/assets-controllers/src/TokenListController.test.ts +++ b/packages/assets-controllers/src/TokenListController.test.ts @@ -1298,7 +1298,7 @@ describe('TokenListController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/assets-controllers/src/TokenListController.ts b/packages/assets-controllers/src/TokenListController.ts index ffa989b3730..3b40ae7b6df 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -1,6 +1,7 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { safelyExecute } from '@metamask/controller-utils'; import type { Messenger } from '@metamask/messenger'; @@ -74,17 +75,17 @@ export type TokenListControllerMessenger = Messenger< TokenListControllerEvents | AllowedEvents >; -const metadata = { +const metadata: StateMetadata = { tokensChainsCache: { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, preventPollingOnNetworkRestart: { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, }; diff --git a/packages/assets-controllers/src/TokenRatesController.test.ts b/packages/assets-controllers/src/TokenRatesController.test.ts index f695ab4b633..c4b51141001 100644 --- a/packages/assets-controllers/src/TokenRatesController.test.ts +++ b/packages/assets-controllers/src/TokenRatesController.test.ts @@ -2710,7 +2710,7 @@ describe('TokenRatesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/TokenRatesController.ts b/packages/assets-controllers/src/TokenRatesController.ts index 587c4396be7..f5d1d382d4f 100644 --- a/packages/assets-controllers/src/TokenRatesController.ts +++ b/packages/assets-controllers/src/TokenRatesController.ts @@ -6,6 +6,7 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { safelyExecute, @@ -200,11 +201,11 @@ async function getCurrencyConversionRate({ } } -const tokenRatesControllerMetadata = { +const tokenRatesControllerMetadata: StateMetadata = { marketData: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; diff --git a/packages/bridge-controller/src/bridge-controller.test.ts b/packages/bridge-controller/src/bridge-controller.test.ts index 4cd8ad60725..ea7b51d3707 100644 --- a/packages/bridge-controller/src/bridge-controller.test.ts +++ b/packages/bridge-controller/src/bridge-controller.test.ts @@ -1,7 +1,7 @@ /* eslint-disable jest/no-restricted-matchers */ /* eslint-disable jest/no-conditional-in-test */ import { Contract } from '@ethersproject/contracts'; -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { EthAccountType, EthScope, @@ -2444,7 +2444,7 @@ describe('BridgeController', function () { deriveStateFromMetadata( bridgeController.state, bridgeController.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/bridge-controller/src/bridge-controller.ts b/packages/bridge-controller/src/bridge-controller.ts index 29df14a2491..f8fece58577 100644 --- a/packages/bridge-controller/src/bridge-controller.ts +++ b/packages/bridge-controller/src/bridge-controller.ts @@ -80,55 +80,55 @@ const metadata: StateMetadata = { quoteRequest: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, quotes: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, quotesInitialLoadTime: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, quotesLastFetched: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, quotesLoadingStatus: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, quoteFetchError: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, quotesRefreshCount: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, assetExchangeRates: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, minimumBalanceForRentExemptionInLamports: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index 52204c4e8d3..4b1dd78e0a8 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -3708,7 +3708,7 @@ describe('BridgeStatusController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index ae57a883577..8bfe168c2ed 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -1,5 +1,5 @@ import type { AccountsControllerState } from '@metamask/accounts-controller'; -import type { StateMetadata } from '@metamask/base-controller'; +import type { StateMetadata } from '@metamask/base-controller/next'; import type { QuoteMetadata, RequiredEventContextFromClient, @@ -81,7 +81,7 @@ const metadata: StateMetadata = { txHistory: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; From 1b32baac855b944f3645575ae0efc46afec27417 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 10:49:55 +0200 Subject: [PATCH 127/247] migrate `MultichainBalancesController` messenger --- .../MultichainBalancesController.test.ts | 52 +++++++++++++------ .../MultichainBalancesController.ts | 50 ++++++++---------- 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.test.ts b/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.test.ts index 7d28fc95176..53f17015c27 100644 --- a/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.test.ts +++ b/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.test.ts @@ -1,4 +1,4 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { Balance, CaipAssetType } from '@metamask/keyring-api'; import { BtcAccountType, @@ -13,6 +13,13 @@ import { } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { v4 as uuidv4 } from 'uuid'; import { MultichainBalancesController } from '.'; @@ -21,10 +28,6 @@ import type { MultichainBalancesControllerState, } from '.'; import { getDefaultMultichainBalancesControllerState } from './MultichainBalancesController'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; const mockBtcAccount = { address: 'bc1qssdcp5kvwh6nghzg9tuk99xsflwkdv4hgvq58q', @@ -103,21 +106,26 @@ const mockBalanceResult = { /** * The union of actions that the root messenger allows. */ -type RootAction = ExtractAvailableAction; +type RootAction = MessengerActions; /** * The union of events that the root messenger allows. */ -type RootEvent = ExtractAvailableEvent; +type RootEvent = MessengerEvents; /** - * Constructs the unrestricted messenger. This can be used to call actions and + * The root messenger type + */ +type RootMessenger = Messenger; + +/** + * Constructs the root messenger. This can be used to call actions and * publish events within the tests for this controller. * - * @returns The unrestricted messenger suited for MultichainBalancesController. + * @returns The root messenger suited for MultichainBalancesController. */ -function getRootMessenger(): Messenger { - return new Messenger(); +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** @@ -127,23 +135,33 @@ function getRootMessenger(): Messenger { * @returns The unrestricted messenger suited for MultichainBalancesController. */ function getRestrictedMessenger( - messenger: Messenger, + messenger: RootMessenger, ): MultichainBalancesControllerMessenger { - return messenger.getRestricted({ - name: 'MultichainBalancesController', - allowedActions: [ + const multichainBalancesControllerMessenger = new Messenger< + 'MultichainBalancesController', + RootAction, + RootEvent, + RootMessenger + >({ + namespace: 'MultichainBalancesController', + parent: messenger, + }); + messenger.delegate({ + messenger: multichainBalancesControllerMessenger, + actions: [ 'SnapController:handleRequest', 'AccountsController:listMultichainAccounts', 'MultichainAssetsController:getState', 'KeyringController:getState', ], - allowedEvents: [ + events: [ 'AccountsController:accountAdded', 'AccountsController:accountRemoved', 'AccountsController:accountBalancesUpdated', 'MultichainAssetsController:accountAssetListUpdated', ], }); + return multichainBalancesControllerMessenger; } const setupController = ({ @@ -651,7 +669,7 @@ describe('MultichainBalancesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.ts b/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.ts index 1545e547b79..d547f5bc237 100644 --- a/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.ts +++ b/packages/assets-controllers/src/MultichainBalancesController/MultichainBalancesController.ts @@ -6,10 +6,10 @@ import type { } from '@metamask/accounts-controller'; import { BaseController, + type StateMetadata, type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { isEvmAccountType } from '@metamask/keyring-api'; import type { Balance, @@ -19,6 +19,7 @@ import type { import type { KeyringControllerGetStateAction } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { KeyringClient } from '@metamask/keyring-snap-client'; +import type { Messenger } from '@metamask/messenger'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import type { SnapId } from '@metamask/snaps-sdk'; import { HandlerType } from '@metamask/snaps-utils'; @@ -108,12 +109,10 @@ type AllowedEvents = /** * Messenger type for the MultichainBalancesController. */ -export type MultichainBalancesControllerMessenger = RestrictedMessenger< +export type MultichainBalancesControllerMessenger = Messenger< typeof controllerName, MultichainBalancesControllerActions | AllowedActions, - MultichainBalancesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + MultichainBalancesControllerEvents | AllowedEvents >; /** @@ -123,14 +122,15 @@ export type MultichainBalancesControllerMessenger = RestrictedMessenger< * using the `persist` flag; and if they can be sent to Sentry or not, using * the `anonymous` flag. */ -const balancesControllerMetadata = { - balances: { - includeInStateLogs: false, - persist: true, - anonymous: false, - usedInUi: true, - }, -}; +const balancesControllerMetadata: StateMetadata = + { + balances: { + includeInStateLogs: false, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + }; /** * The MultichainBalancesController is responsible for fetching and caching account @@ -165,17 +165,17 @@ export class MultichainBalancesController extends BaseController< void this.updateBalance(account.id); } - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountRemoved', (account: string) => this.#handleOnAccountRemoved(account), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountBalancesUpdated', (balanceUpdate: AccountBalancesUpdatedEventPayload) => this.#handleOnAccountBalancesUpdated(balanceUpdate), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'MultichainAssetsController:accountAssetListUpdated', async ({ assets }) => { const newAccountAssets = Object.entries(assets).map( @@ -200,9 +200,7 @@ export class MultichainBalancesController extends BaseController< assets: CaipAssetType[]; }[], ): Promise { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { isUnlocked } = this.messenger.call('KeyringController:getState'); if (!isUnlocked) { return; @@ -256,9 +254,7 @@ export class MultichainBalancesController extends BaseController< accountId: string, assets: CaipAssetType[], ): Promise { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { isUnlocked } = this.messenger.call('KeyringController:getState'); if (!isUnlocked) { return; @@ -305,9 +301,7 @@ export class MultichainBalancesController extends BaseController< * @returns A list of multichain accounts. */ #listMultichainAccounts(): InternalAccount[] { - return this.messagingSystem.call( - 'AccountsController:listMultichainAccounts', - ); + return this.messenger.call('AccountsController:listMultichainAccounts'); } /** @@ -328,7 +322,7 @@ export class MultichainBalancesController extends BaseController< */ #listAccountAssets(accountId: string): CaipAssetType[] { // TODO: Add an action `MultichainAssetsController:getAccountAssets` maybe? - const assetsState = this.messagingSystem.call( + const assetsState = this.messenger.call( 'MultichainAssetsController:getState', ); @@ -427,7 +421,7 @@ export class MultichainBalancesController extends BaseController< #getClient(snapId: string): KeyringClient { return new KeyringClient({ send: async (request: JsonRpcRequest) => - (await this.messagingSystem.call('SnapController:handleRequest', { + (await this.messenger.call('SnapController:handleRequest', { snapId: snapId as SnapId, origin: 'metamask', handler: HandlerType.OnKeyringRequest, From 10916bcd7e1dffd707210a5e12c5715ee3774093 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 10:56:07 +0200 Subject: [PATCH 128/247] migrate `RatesController` messenger --- .../RatesController/RatesController.test.ts | 72 ++++++++++--------- .../src/RatesController/RatesController.ts | 17 +++-- .../src/RatesController/types.ts | 8 +-- 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/packages/assets-controllers/src/RatesController/RatesController.test.ts b/packages/assets-controllers/src/RatesController/RatesController.test.ts index e2b125ea4db..8ba6ccbe2b5 100644 --- a/packages/assets-controllers/src/RatesController/RatesController.test.ts +++ b/packages/assets-controllers/src/RatesController/RatesController.test.ts @@ -1,6 +1,14 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { useFakeTimers } from 'sinon'; +import type { RatesControllerMessenger, RatesControllerState } from './types'; import { advanceTime } from '../../../../tests/helpers'; import type { fetchMultiExchangeRate as defaultFetchExchangeRate } from '../crypto-compare-service'; import { @@ -8,12 +16,12 @@ import { RatesController, name as ratesControllerName, } from './RatesController'; -import type { - RatesControllerActions, - RatesControllerEvents, - RatesControllerMessenger, - RatesControllerState, -} from './types'; + +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; const MOCK_TIMESTAMP = 1709983353; @@ -26,28 +34,26 @@ function getStubbedDate(): number { } /** - * Builds a new Messenger instance for RatesController. - * @returns A new Messenger instance. + * Builds a new root messenger instance. + * + * @returns A new root messenger instance. */ -function buildMessenger(): Messenger< - RatesControllerActions, - RatesControllerEvents -> { - return new Messenger(); +function buildRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** * Builds a restricted messenger for the RatesController. + * * @param messenger - The base messenger instance. * @returns A restricted messenger for the RatesController. */ function buildRatesControllerMessenger( - messenger: Messenger, + messenger: RootMessenger, ): RatesControllerMessenger { - return messenger.getRestricted({ - name: ratesControllerName, - allowedEvents: [], - allowedActions: [], + return new Messenger({ + namespace: ratesControllerName, + parent: messenger, }); } @@ -70,7 +76,7 @@ function setupRatesController({ }: { interval?: number; initialState?: Partial; - messenger: Messenger; + messenger: RootMessenger; includeUsdRate: boolean; fetchMultiExchangeRate?: typeof defaultFetchExchangeRate; }) { @@ -91,7 +97,7 @@ describe('RatesController', () => { it('constructs the RatesController with default values', () => { const ratesController = setupRatesController({ initialState: {}, - messenger: buildMessenger(), + messenger: buildRootMessenger(), includeUsdRate: false, }); const { fiatCurrency, rates, cryptocurrencies } = ratesController.state; @@ -118,7 +124,7 @@ describe('RatesController', () => { }); it('starts the polling process with default values', async () => { - const messenger = buildMessenger(); + const messenger = buildRootMessenger(); const publishActionSpy = jest.spyOn(messenger, 'publish'); jest.spyOn(global.Date, 'now').mockImplementation(() => getStubbedDate()); @@ -222,7 +228,7 @@ describe('RatesController', () => { cryptocurrencies: [Cryptocurrency.Btc], fiatCurrency: 'eur', }, - messenger: buildMessenger(), + messenger: buildRootMessenger(), includeUsdRate: true, fetchMultiExchangeRate: fetchExchangeRateStub, }); @@ -263,7 +269,7 @@ describe('RatesController', () => { }); it('stops the polling process', async () => { - const messenger = buildMessenger(); + const messenger = buildRootMessenger(); const publishActionSpy = jest.spyOn(messenger, 'publish'); const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const ratesController = setupRatesController({ @@ -318,7 +324,7 @@ describe('RatesController', () => { initialState: { cryptocurrencies: mockCryptocurrencyList, }, - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -335,7 +341,7 @@ describe('RatesController', () => { const ratesController = setupRatesController({ interval: 150, initialState: {}, - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -366,7 +372,7 @@ describe('RatesController', () => { const ratesController = setupRatesController({ interval: 150, initialState: {}, - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -385,7 +391,7 @@ describe('RatesController', () => { const ratesController = setupRatesController({ interval: 150, initialState: {}, - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -400,7 +406,7 @@ describe('RatesController', () => { it('includes expected state in debug snapshots', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const controller = setupRatesController({ - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -409,7 +415,7 @@ describe('RatesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -435,7 +441,7 @@ describe('RatesController', () => { it('includes expected state in state logs', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const controller = setupRatesController({ - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -460,7 +466,7 @@ describe('RatesController', () => { it('persists expected state', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const controller = setupRatesController({ - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -495,7 +501,7 @@ describe('RatesController', () => { it('exposes expected state to UI', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const controller = setupRatesController({ - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); diff --git a/packages/assets-controllers/src/RatesController/RatesController.ts b/packages/assets-controllers/src/RatesController/RatesController.ts index 5c916a7c92a..f2d4430a89f 100644 --- a/packages/assets-controllers/src/RatesController/RatesController.ts +++ b/packages/assets-controllers/src/RatesController/RatesController.ts @@ -1,4 +1,7 @@ -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type StateMetadata, +} from '@metamask/base-controller/next'; import { Mutex } from 'async-mutex'; import type { Draft } from 'immer'; @@ -25,23 +28,23 @@ export enum Cryptocurrency { const DEFAULT_INTERVAL = 180000; -const metadata = { +const metadata: StateMetadata = { fiatCurrency: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, rates: { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, cryptocurrencies: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, }; @@ -180,7 +183,7 @@ export class RatesController extends BaseController< return; } - this.messagingSystem.publish(`${name}:pollingStarted`); + this.messenger.publish(`${name}:pollingStarted`); await this.#updateRates(); @@ -199,7 +202,7 @@ export class RatesController extends BaseController< clearInterval(this.#intervalId); this.#intervalId = undefined; - this.messagingSystem.publish(`${name}:pollingStopped`); + this.messenger.publish(`${name}:pollingStopped`); } /** diff --git a/packages/assets-controllers/src/RatesController/types.ts b/packages/assets-controllers/src/RatesController/types.ts index c26ae070075..49c5515391b 100644 --- a/packages/assets-controllers/src/RatesController/types.ts +++ b/packages/assets-controllers/src/RatesController/types.ts @@ -1,8 +1,8 @@ import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; import type { fetchMultiExchangeRate as defaultFetchExchangeRate } from '../crypto-compare-service'; import type { @@ -97,12 +97,10 @@ export type RatesControllerActions = RatesControllerGetStateAction; /** * Defines the actions that the RatesController can perform. */ -export type RatesControllerMessenger = RestrictedMessenger< +export type RatesControllerMessenger = Messenger< typeof ratesControllerName, RatesControllerActions, - RatesControllerEvents, - never, - never + RatesControllerEvents >; /** From 3c83baf938e0c553e9487df84c9ab34ab0827779 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:00:58 +0200 Subject: [PATCH 129/247] migrate `TokenSearchDiscoveryDataController` messenger --- ...TokenSearchDiscoveryDataController.test.ts | 39 ++++++++++++---- .../TokenSearchDiscoveryDataController.ts | 44 +++++++++---------- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts index 2148acd60c2..04dad2f61bc 100644 --- a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts +++ b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts @@ -1,5 +1,12 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { ChainId } from '@metamask/controller-utils'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import assert from 'assert'; import { useFakeTimers } from 'sinon'; @@ -32,7 +39,11 @@ jest.mock('../token-service', () => { }; }); -type MainMessenger = Messenger; +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; /** * Builds a not found token display data object. @@ -111,13 +122,21 @@ function buildFoundTokenDisplayData( * @returns The restricted messenger. */ function buildTokenSearchDiscoveryDataControllerMessenger( - messenger: MainMessenger = new Messenger(), + messenger: RootMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }), ): TokenSearchDiscoveryDataControllerMessenger { - return messenger.getRestricted({ - name: controllerName, - allowedActions: ['CurrencyRateController:getState'], - allowedEvents: [], + const tokenSearchDiscoveryDataControllerMessenger = new Messenger< + typeof controllerName, + AllActions, + AllEvents, + RootMessenger + >({ + namespace: controllerName, }); + messenger.delegate({ + messenger: tokenSearchDiscoveryDataControllerMessenger, + actions: ['CurrencyRateController:getState'], + }); + return tokenSearchDiscoveryDataControllerMessenger; } /** @@ -206,7 +225,9 @@ async function withController( callback = maybeCallback; } - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); messenger.registerActionHandler('CurrencyRateController:getState', () => ({ currentCurrency: 'USD', @@ -899,7 +920,7 @@ describe('TokenSearchDiscoveryDataController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.ts b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.ts index dc508d7e3f7..7af8e66d20b 100644 --- a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.ts +++ b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.ts @@ -1,9 +1,10 @@ import { BaseController, + type StateMetadata, type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import type { TokenDisplayData } from './types'; @@ -33,20 +34,21 @@ export type TokenSearchDiscoveryDataControllerState = { >; }; -const tokenSearchDiscoveryDataControllerMetadata = { - tokenDisplayData: { - includeInStateLogs: false, - persist: true, - anonymous: false, - usedInUi: true, - }, - swapsTokenAddressesByChainId: { - includeInStateLogs: false, - persist: true, - anonymous: false, - usedInUi: true, - }, -} as const; +const tokenSearchDiscoveryDataControllerMetadata: StateMetadata = + { + tokenDisplayData: { + includeInStateLogs: false, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + swapsTokenAddressesByChainId: { + includeInStateLogs: false, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + } as const; // === MESSENGER === @@ -98,12 +100,10 @@ export type AllowedEvents = never; * The messenger which is restricted to actions and events accessed by * {@link TokenSearchDiscoveryDataController}. */ -export type TokenSearchDiscoveryDataControllerMessenger = RestrictedMessenger< +export type TokenSearchDiscoveryDataControllerMessenger = Messenger< typeof controllerName, TokenSearchDiscoveryDataControllerActions | AllowedActions, - TokenSearchDiscoveryDataControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokenSearchDiscoveryDataControllerEvents | AllowedEvents >; /** @@ -176,7 +176,7 @@ export class TokenSearchDiscoveryDataController extends BaseController< chainId: Hex, address: string, ): Promise | null> { - const { currentCurrency } = this.messagingSystem.call( + const { currentCurrency } = this.messenger.call( 'CurrencyRateController:getState', ); @@ -251,7 +251,7 @@ export class TokenSearchDiscoveryDataController extends BaseController< } } - const { currentCurrency } = this.messagingSystem.call( + const { currentCurrency } = this.messenger.call( 'CurrencyRateController:getState', ); From f0646a779bf0a1d7f25e203a9e3eadf0f3154e11 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:09:51 +0200 Subject: [PATCH 130/247] migrate `NftDetectionController` messenger --- .../src/NftDetectionController.test.ts | 57 +++++++++++++------ .../src/NftDetectionController.ts | 30 ++++++---- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/packages/assets-controllers/src/NftDetectionController.test.ts b/packages/assets-controllers/src/NftDetectionController.test.ts index af18eb5e175..a554b63e46c 100644 --- a/packages/assets-controllers/src/NftDetectionController.test.ts +++ b/packages/assets-controllers/src/NftDetectionController.test.ts @@ -1,10 +1,16 @@ import type { AccountsController } from '@metamask/accounts-controller'; -import { Messenger } from '@metamask/base-controller'; import { NFT_API_BASE_URL, ChainId, InfuraNetworkType, } from '@metamask/controller-utils'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { getDefaultNetworkControllerState, NetworkClientType, @@ -42,6 +48,12 @@ import { } from './NftDetectionController'; import * as constants from './NftDetectionController'; +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; + const controllerName = 'NftDetectionController' as const; const defaultSelectedAccount = createMockInternalAccount(); @@ -1914,7 +1926,9 @@ async function withController( testFunction, ] = args.length === 2 ? args : [{}, args[0]]; - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); messenger.registerActionHandler( 'NetworkController:getState', @@ -1954,21 +1968,32 @@ async function withController( }), ); + const nftDetectionControllerMessenger = new Messenger< + typeof controllerName, + AllActions, + AllEvents, + RootMessenger + >({ + namespace: controllerName, + parent: messenger, + }); + messenger.delegate({ + messenger: nftDetectionControllerMessenger, + actions: [ + 'NetworkController:getState', + 'NetworkController:getNetworkClientById', + 'PreferencesController:getState', + 'AccountsController:getSelectedAccount', + 'NetworkController:findNetworkClientIdByChainId', + ], + events: [ + 'NetworkController:stateChange', + 'PreferencesController:stateChange', + ], + }); + const controller = new NftDetectionController({ - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [ - 'NetworkController:getState', - 'NetworkController:getNetworkClientById', - 'PreferencesController:getState', - 'AccountsController:getSelectedAccount', - 'NetworkController:findNetworkClientIdByChainId', - ], - allowedEvents: [ - 'NetworkController:stateChange', - 'PreferencesController:stateChange', - ], - }), + messenger: nftDetectionControllerMessenger, disabled: true, addNft: jest.fn(), getNftState: getDefaultNftControllerState, diff --git a/packages/assets-controllers/src/NftDetectionController.ts b/packages/assets-controllers/src/NftDetectionController.ts index dd72056892c..d5a78a13b5e 100644 --- a/packages/assets-controllers/src/NftDetectionController.ts +++ b/packages/assets-controllers/src/NftDetectionController.ts @@ -1,7 +1,10 @@ import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller'; import type { AddApprovalRequest } from '@metamask/approval-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import { toChecksumHexAddress, ChainId, @@ -13,6 +16,7 @@ import { NFT_API_TIMEOUT, toHex, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClient, NetworkControllerGetNetworkClientByIdAction, @@ -40,6 +44,7 @@ const controllerName = 'NftDetectionController'; export type NFTDetectionControllerState = Record; export type AllowedActions = + | ControllerGetStateAction | AddApprovalRequest | NetworkControllerGetStateAction | NetworkControllerGetNetworkClientByIdAction @@ -48,15 +53,17 @@ export type AllowedActions = | NetworkControllerFindNetworkClientIdByChainIdAction; export type AllowedEvents = + | ControllerStateChangeEvent< + typeof controllerName, + NFTDetectionControllerState + > | PreferencesControllerStateChangeEvent | NetworkControllerStateChangeEvent; -export type NftDetectionControllerMessenger = RestrictedMessenger< +export type NftDetectionControllerMessenger = Messenger< typeof controllerName, AllowedActions, - AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AllowedEvents >; /** @@ -503,7 +510,7 @@ export class NftDetectionController extends BaseController< this.#getNftState = getNftState; this.#addNft = addNft; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'PreferencesController:stateChange', this.#onPreferencesControllerStateChange.bind(this), ); @@ -515,12 +522,12 @@ export class NftDetectionController extends BaseController< * @returns Whether current network is mainnet. */ isMainnet(): boolean { - const { selectedNetworkClientId } = this.messagingSystem.call( + const { selectedNetworkClientId } = this.messenger.call( 'NetworkController:getState', ); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', selectedNetworkClientId, ); @@ -591,8 +598,7 @@ export class NftDetectionController extends BaseController< async detectNfts(chainIds: Hex[], options?: { userAddress?: string }) { const userAddress = options?.userAddress ?? - this.messagingSystem.call('AccountsController:getSelectedAccount') - .address; + this.messenger.call('AccountsController:getSelectedAccount').address; // filter out unsupported chainIds const supportedChainIds = chainIds.filter((chainId) => @@ -796,7 +802,7 @@ export class NftDetectionController extends BaseController< collection && { collection }, chainId && { chainId }, ); - const networkClientId = this.messagingSystem.call( + const networkClientId = this.messenger.call( 'NetworkController:findNetworkClientIdByChainId', toHex(chainId), ); From a966f674e08b84a762aabcda082757c9416ff10f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:20:25 +0200 Subject: [PATCH 131/247] remove unused vars --- packages/assets-controllers/src/NftDetectionController.test.ts | 2 -- .../TokenSearchDiscoveryDataController.test.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/assets-controllers/src/NftDetectionController.test.ts b/packages/assets-controllers/src/NftDetectionController.test.ts index a554b63e46c..578c7857c16 100644 --- a/packages/assets-controllers/src/NftDetectionController.test.ts +++ b/packages/assets-controllers/src/NftDetectionController.test.ts @@ -43,8 +43,6 @@ import { NftDetectionController, BlockaidResultType, MAX_GET_COLLECTION_BATCH_SIZE, - type AllowedActions, - type AllowedEvents, } from './NftDetectionController'; import * as constants from './NftDetectionController'; diff --git a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts index 04dad2f61bc..86d1b7e01e2 100644 --- a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts +++ b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts @@ -16,8 +16,6 @@ import { TokenSearchDiscoveryDataController, controllerName, MAX_TOKEN_DISPLAY_DATA_LENGTH, - type AllowedActions, - type AllowedEvents, type TokenSearchDiscoveryDataControllerMessenger, type TokenSearchDiscoveryDataControllerState, } from './TokenSearchDiscoveryDataController'; From feb00b3f9c0b965f8035a4f6f4a7d1c6f223c6a0 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:20:33 +0200 Subject: [PATCH 132/247] rename leftover `messagingSystem` --- packages/assets-controllers/src/TokenBalancesController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 89e98b72e9f..47525156fac 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -836,10 +836,10 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ this.#intervalPollingTimers.clear(); // Unregister action handlers - this.messagingSystem.unregisterActionHandler( + this.messenger.unregisterActionHandler( `TokenBalancesController:updateChainPollingConfigs`, ); - this.messagingSystem.unregisterActionHandler( + this.messenger.unregisterActionHandler( `TokenBalancesController:getChainPollingConfig`, ); From fa0f1965aacc7df6bae6d3d63333161b301b060c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:22:11 +0200 Subject: [PATCH 133/247] update changelog --- packages/assets-controllers/CHANGELOG.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index fd4558f8455..fee29a67f9f 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -13,16 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Migrate the following controllers to the new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) - - `AccountTrackerController` - - `CurrencyRateController` - - `DeFiPositionController` - - `MultichainAssetsRatesController` - - `TokenBalancesController` - - `TokenDetectionController` - - `TokenListController` - - `TokenRatesController` -- **BREAKING:** Migrate `AssetsContractController`, `NftController`, and `TokensController` to new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) +- **BREAKING:** Migrate controllers to new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444), [#6386](https://github.com/MetaMask/core/pull/6386), [#6745](https://github.com/MetaMask/core/pull/6745)) - Previously, the controllers accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. ## [76.0.0] From 6e80728c10a529be20c28426635fb5efbf912c30 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:31:47 +0200 Subject: [PATCH 134/247] update lint thresholds --- eslint-warning-thresholds.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index 3815a68c937..e40228baf16 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -38,8 +38,8 @@ "jsdoc/tag-lines": 1 }, "packages/assets-controllers/src/RatesController/RatesController.test.ts": { - "import-x/order": 2, - "jsdoc/tag-lines": 4 + "import-x/order": 1, + "jsdoc/tag-lines": 2 }, "packages/assets-controllers/src/RatesController/RatesController.ts": { "@typescript-eslint/prefer-readonly": 1, @@ -70,7 +70,7 @@ }, "packages/assets-controllers/src/TokenListController.test.ts": { "import-x/namespace": 7, - "import-x/order": 3, + "import-x/order": 2, "jest/no-conditional-in-test": 2 }, "packages/assets-controllers/src/TokenRatesController.test.ts": { From 52899ba1dae36ccec64eec27363e7f198a5eadc0 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 11:33:08 +0200 Subject: [PATCH 135/247] fix `transaction.test.ts` --- .../bridge-status-controller/src/utils/transaction.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index b1f044da7e2..f2e51262bfa 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1456,7 +1456,7 @@ describe('Bridge Status Controller Transaction Utils', () => { const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, estimateGasFeeFn: jest.fn().mockResolvedValue({}), @@ -1473,7 +1473,7 @@ describe('Bridge Status Controller Transaction Utils', () => { const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, estimateGasFeeFn: jest.fn().mockResolvedValue({}), @@ -1490,7 +1490,7 @@ describe('Bridge Status Controller Transaction Utils', () => { const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, estimateGasFeeFn: jest.fn().mockResolvedValue({}), From b896289e0bcfbc92b45a08f23dc0760afe9186d6 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 29 Sep 2025 13:14:23 +0200 Subject: [PATCH 136/247] rename leftover `messagingSystem` --- packages/assets-controllers/src/TokenBalancesController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 89e98b72e9f..47525156fac 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -836,10 +836,10 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ this.#intervalPollingTimers.clear(); // Unregister action handlers - this.messagingSystem.unregisterActionHandler( + this.messenger.unregisterActionHandler( `TokenBalancesController:updateChainPollingConfigs`, ); - this.messagingSystem.unregisterActionHandler( + this.messenger.unregisterActionHandler( `TokenBalancesController:getChainPollingConfig`, ); From b4365edd7b8d90ea500ee47e1a7c2fa0e2639647 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 2 Oct 2025 14:50:07 +0200 Subject: [PATCH 137/247] revert rename `name` to `controllerName` --- packages/composable-controller/src/ComposableController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/composable-controller/src/ComposableController.ts b/packages/composable-controller/src/ComposableController.ts index 3bad5a93b6f..04be89d88fb 100644 --- a/packages/composable-controller/src/ComposableController.ts +++ b/packages/composable-controller/src/ComposableController.ts @@ -160,7 +160,6 @@ export class ComposableController< }, {} as never, ), - // @ts-expect-error "Property 'messagingSystem' is missing in type ..." messenger, }); @@ -186,7 +185,7 @@ export class ComposableController< } try { this.messenger.subscribe( - `${controllerName}:stateChange`, + `${name}:stateChange`, (childState: StateConstraint) => { this.update((state) => { // Type assertion is necessary for property assignment to a generic type. This does not pollute or widen the type of the asserted variable. From c793e67681575abb172ca4df6bfb5f9c9293235e Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Mon, 8 Sep 2025 15:52:52 +0200 Subject: [PATCH 138/247] refactor: migrate UserOperationController to @metamask/messenger --- .../user-operation-controller/CHANGELOG.md | 3 ++ .../user-operation-controller/package.json | 1 + .../src/UserOperationController.ts | 37 ++++++++++--------- .../tsconfig.build.json | 3 ++ .../user-operation-controller/tsconfig.json | 3 ++ yarn.lock | 8 ++++ 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/packages/user-operation-controller/CHANGELOG.md b/packages/user-operation-controller/CHANGELOG.md index 3fe8c5374de..b8edcc03ad9 100644 --- a/packages/user-operation-controller/CHANGELOG.md +++ b/packages/user-operation-controller/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6494](https://github.com/MetaMask/core/pull/6494)) + - Previously, `UserOperationController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +- Bump `@metamask/utils` from `^11.4.2` to `^11.8.0` ([#6588](https://github.com/MetaMask/core/pull/6588)) - Bump `@metamask/controller-utils` from `^11.12.0` to `^11.14.0` ([#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629)) - Bump `@metamask/base-controller` from `^8.1.0` to `^8.4.0` ([#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/utils` from `^11.4.2` to `^11.8.1` ([#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) diff --git a/packages/user-operation-controller/package.json b/packages/user-operation-controller/package.json index c3637f7a652..0b8cd146d4b 100644 --- a/packages/user-operation-controller/package.json +++ b/packages/user-operation-controller/package.json @@ -51,6 +51,7 @@ "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", "@metamask/eth-query": "^4.0.0", + "@metamask/messenger": "^0.2.0", "@metamask/polling-controller": "^14.0.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/superstruct": "^3.1.0", diff --git a/packages/user-operation-controller/src/UserOperationController.ts b/packages/user-operation-controller/src/UserOperationController.ts index 6f0180d5b84..1a59badf2f4 100644 --- a/packages/user-operation-controller/src/UserOperationController.ts +++ b/packages/user-operation-controller/src/UserOperationController.ts @@ -3,8 +3,11 @@ import type { AddApprovalRequest, AddResult, } from '@metamask/approval-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import { ApprovalType } from '@metamask/controller-utils'; import EthQuery from '@metamask/eth-query'; import type { GasFeeState } from '@metamask/gas-fee-controller'; @@ -13,6 +16,7 @@ import type { KeyringControllerPatchUserOperationAction, KeyringControllerSignUserOperationAction, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, Provider, @@ -28,7 +32,6 @@ import { add0x } from '@metamask/utils'; // This package purposefully relies on Node's EventEmitter module. // eslint-disable-next-line import-x/no-nodejs-modules import EventEmitter from 'events'; -import type { Patch } from 'immer'; import { cloneDeep } from 'lodash'; import { v1 as random } from 'uuid'; @@ -102,15 +105,15 @@ export type UserOperationControllerState = { userOperations: Record; }; -export type GetUserOperationState = { - type: `${typeof controllerName}:getState`; - handler: () => UserOperationControllerState; -}; +export type GetUserOperationState = ControllerGetStateAction< + typeof controllerName, + UserOperationControllerState +>; -export type UserOperationStateChange = { - type: `${typeof controllerName}:stateChange`; - payload: [UserOperationControllerState, Patch[]]; -}; +export type UserOperationStateChange = ControllerStateChangeEvent< + typeof controllerName, + UserOperationControllerState +>; export type UserOperationControllerActions = | GetUserOperationState @@ -122,12 +125,10 @@ export type UserOperationControllerActions = export type UserOperationControllerEvents = UserOperationStateChange; -export type UserOperationControllerMessenger = RestrictedMessenger< +export type UserOperationControllerMessenger = Messenger< typeof controllerName, UserOperationControllerActions, - UserOperationControllerEvents, - UserOperationControllerActions['type'], - UserOperationControllerEvents['type'] + UserOperationControllerEvents >; export type UserOperationControllerOptions = { @@ -339,7 +340,7 @@ export class UserOperationController extends BaseController< const smartContractAccount = requestSmartContractAccount ?? - new SnapSmartContractAccount(this.messagingSystem); + new SnapSmartContractAccount(this.messenger); const cache: UserOperationCache = { chainId, @@ -739,7 +740,7 @@ export class UserOperationController extends BaseController< const type = ApprovalType.Transaction; const requestData = { txId: id }; - return (await this.messagingSystem.call( + return (await this.messenger.call( 'ApprovalController:addRequest', { id, @@ -774,7 +775,7 @@ export class UserOperationController extends BaseController< async #getProvider( networkClientId: string, ): Promise<{ provider: Provider; chainId: string }> { - const { provider, configuration } = this.messagingSystem.call( + const { provider, configuration } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); diff --git a/packages/user-operation-controller/tsconfig.build.json b/packages/user-operation-controller/tsconfig.build.json index 4c886f75e4d..51437a5055f 100644 --- a/packages/user-operation-controller/tsconfig.build.json +++ b/packages/user-operation-controller/tsconfig.build.json @@ -26,6 +26,9 @@ }, { "path": "../transaction-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/user-operation-controller/tsconfig.json b/packages/user-operation-controller/tsconfig.json index 2a0259e8b9b..f597f35bf69 100644 --- a/packages/user-operation-controller/tsconfig.json +++ b/packages/user-operation-controller/tsconfig.json @@ -24,6 +24,9 @@ }, { "path": "../transaction-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src"] diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..82080091cf6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3820,6 +3820,13 @@ __metadata: languageName: unknown linkType: soft +"@metamask/messenger@npm:^0.2.0": + version: 0.2.0 + resolution: "@metamask/messenger@npm:0.2.0" + checksum: 10/48f682d9cde1208fbda0936022dea37acc3828cc221203b5f917df25c131d9a250dc5e86e9263f5dba8ee7c05adc6752a68dfb57da7d297f95f38b052f4fe5c1 + languageName: node + linkType: hard + "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4832,6 +4839,7 @@ __metadata: "@metamask/eth-query": "npm:^4.0.0" "@metamask/gas-fee-controller": "npm:^24.0.0" "@metamask/keyring-controller": "npm:^23.1.0" + "@metamask/messenger": "npm:^0.2.0" "@metamask/network-controller": "npm:^24.2.0" "@metamask/polling-controller": "npm:^14.0.0" "@metamask/rpc-errors": "npm:^7.0.2" From 0cc3ead908e1cd8b431dec41f3ae7d231682f447 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 24 Sep 2025 14:41:59 +0200 Subject: [PATCH 139/247] fix: changelog order --- packages/user-operation-controller/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/user-operation-controller/CHANGELOG.md b/packages/user-operation-controller/CHANGELOG.md index b8edcc03ad9..2cf06cd9dbc 100644 --- a/packages/user-operation-controller/CHANGELOG.md +++ b/packages/user-operation-controller/CHANGELOG.md @@ -15,7 +15,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6494](https://github.com/MetaMask/core/pull/6494)) - Previously, `UserOperationController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. -- Bump `@metamask/utils` from `^11.4.2` to `^11.8.0` ([#6588](https://github.com/MetaMask/core/pull/6588)) - Bump `@metamask/controller-utils` from `^11.12.0` to `^11.14.0` ([#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629)) - Bump `@metamask/base-controller` from `^8.1.0` to `^8.4.0` ([#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/utils` from `^11.4.2` to `^11.8.1` ([#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) From a845e04a20aaecd4702eedfe707be683e568bf32 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 24 Sep 2025 14:43:16 +0200 Subject: [PATCH 140/247] fix: messenger version --- packages/user-operation-controller/package.json | 2 +- yarn.lock | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/user-operation-controller/package.json b/packages/user-operation-controller/package.json index 0b8cd146d4b..8ee456ca2f4 100644 --- a/packages/user-operation-controller/package.json +++ b/packages/user-operation-controller/package.json @@ -51,7 +51,7 @@ "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", "@metamask/eth-query": "^4.0.0", - "@metamask/messenger": "^0.2.0", + "@metamask/messenger": "^0.3.0", "@metamask/polling-controller": "^14.0.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/superstruct": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 82080091cf6..b7d3b961c7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3820,13 +3820,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.2.0": - version: 0.2.0 - resolution: "@metamask/messenger@npm:0.2.0" - checksum: 10/48f682d9cde1208fbda0936022dea37acc3828cc221203b5f917df25c131d9a250dc5e86e9263f5dba8ee7c05adc6752a68dfb57da7d297f95f38b052f4fe5c1 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4839,7 +4832,7 @@ __metadata: "@metamask/eth-query": "npm:^4.0.0" "@metamask/gas-fee-controller": "npm:^24.0.0" "@metamask/keyring-controller": "npm:^23.1.0" - "@metamask/messenger": "npm:^0.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.2.0" "@metamask/polling-controller": "npm:^14.0.0" "@metamask/rpc-errors": "npm:^7.0.2" From 1dc59c056d94f11b56ba05dcab166195531fad83 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 24 Sep 2025 15:45:24 +0200 Subject: [PATCH 141/247] fix: rename anonymous to includeInDebugSnapshot --- .../src/UserOperationController.test.ts | 4 ++-- .../user-operation-controller/src/UserOperationController.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/user-operation-controller/src/UserOperationController.test.ts b/packages/user-operation-controller/src/UserOperationController.test.ts index b1c26ff44a1..53070fbbd55 100644 --- a/packages/user-operation-controller/src/UserOperationController.test.ts +++ b/packages/user-operation-controller/src/UserOperationController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { ApprovalType } from '@metamask/controller-utils'; import { errorCodes } from '@metamask/rpc-errors'; import { @@ -1453,7 +1453,7 @@ describe('UserOperationController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/user-operation-controller/src/UserOperationController.ts b/packages/user-operation-controller/src/UserOperationController.ts index 1a59badf2f4..6d76efd0c08 100644 --- a/packages/user-operation-controller/src/UserOperationController.ts +++ b/packages/user-operation-controller/src/UserOperationController.ts @@ -63,7 +63,7 @@ const stateMetadata = { userOperations: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; From fa22b4acc40026150bf09e1c48bde272b097c72d Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Mon, 8 Sep 2025 15:51:07 +0200 Subject: [PATCH 142/247] refactor: migrate TokenSearchDiscoveryController to @metamask/messenger --- .../CHANGELOG.md | 2 + .../package.json | 1 + .../token-search-discovery-controller.test.ts | 66 ++++++++++++++----- .../src/token-search-discovery-controller.ts | 36 ++++------ .../tsconfig.build.json | 5 +- .../tsconfig.json | 2 +- yarn.lock | 1 + 7 files changed, 71 insertions(+), 42 deletions(-) diff --git a/packages/token-search-discovery-controller/CHANGELOG.md b/packages/token-search-discovery-controller/CHANGELOG.md index 71320d5121a..03294a70062 100644 --- a/packages/token-search-discovery-controller/CHANGELOG.md +++ b/packages/token-search-discovery-controller/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6495](https://github.com/MetaMask/core/pull/6495)) + - Previously, `TokenSearchDiscoveryController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/utils` from `^11.2.0` to `^11.8.1` ([#6054](https://github.com/MetaMask/core/pull/6054), [#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) diff --git a/packages/token-search-discovery-controller/package.json b/packages/token-search-discovery-controller/package.json index d248b1e0b11..87c6df4397c 100644 --- a/packages/token-search-discovery-controller/package.json +++ b/packages/token-search-discovery-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/token-search-discovery-controller/src/token-search-discovery-controller.test.ts b/packages/token-search-discovery-controller/src/token-search-discovery-controller.test.ts index c6f8b31e386..b10c0e4b85d 100644 --- a/packages/token-search-discovery-controller/src/token-search-discovery-controller.test.ts +++ b/packages/token-search-discovery-controller/src/token-search-discovery-controller.test.ts @@ -1,4 +1,11 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { AbstractTokenDiscoveryApiService } from './token-discovery-api-service/abstract-token-discovery-api-service'; import { AbstractTokenSearchApiService } from './token-search-api-service/abstract-token-search-api-service'; @@ -14,18 +21,45 @@ import type { const controllerName = 'TokenSearchDiscoveryController'; +type AllTokenSearchDiscoveryControllerActions = + MessengerActions; + +type AllTokenSearchDiscoveryControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllTokenSearchDiscoveryControllerActions, + AllTokenSearchDiscoveryControllerEvents +>; + /** - * Helper function to get a restricted messenger for testing + * Constructs the root messenger. * - * @returns A restricted messenger for the TokenSearchDiscoveryController + * @returns A root messenger. */ -function getRestrictedMessenger() { - const messenger = new Messenger(); - return messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], - }) as TokenSearchDiscoveryControllerMessenger; +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + +/** + * Helper function to get a messenger for testing + * + * @returns A messenger for the TokenSearchDiscoveryController + */ +function getMessenger(): TokenSearchDiscoveryControllerMessenger { + const rootMessenger = getRootMessenger(); + return new Messenger< + typeof controllerName, + AllTokenSearchDiscoveryControllerActions, + AllTokenSearchDiscoveryControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); } describe('TokenSearchDiscoveryController', () => { @@ -133,7 +167,7 @@ describe('TokenSearchDiscoveryController', () => { mainController = new TokenSearchDiscoveryController({ tokenSearchService: new MockTokenSearchService(), tokenDiscoveryService: new MockTokenDiscoveryService(), - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); }); @@ -142,7 +176,7 @@ describe('TokenSearchDiscoveryController', () => { const controller = new TokenSearchDiscoveryController({ tokenSearchService: new MockTokenSearchService(), tokenDiscoveryService: new MockTokenDiscoveryService(), - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); expect(controller.state).toStrictEqual( @@ -160,7 +194,7 @@ describe('TokenSearchDiscoveryController', () => { tokenSearchService: new MockTokenSearchService(), tokenDiscoveryService: new MockTokenDiscoveryService(), state: initialState, - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); expect(controller.state).toStrictEqual(initialState); @@ -256,7 +290,7 @@ describe('TokenSearchDiscoveryController', () => { const errorController = new TokenSearchDiscoveryController({ tokenSearchService: new ErrorTokenSearchService(), tokenDiscoveryService: new MockTokenDiscoveryService(), - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); const results = await errorController.searchTokens({}); @@ -267,7 +301,7 @@ describe('TokenSearchDiscoveryController', () => { const errorController = new TokenSearchDiscoveryController({ tokenSearchService: new MockTokenSearchService(), tokenDiscoveryService: new ErrorTokenDiscoveryService(), - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); const results = await errorController.getTrendingTokens({}); @@ -281,7 +315,7 @@ describe('TokenSearchDiscoveryController', () => { deriveStateFromMetadata( mainController.state, mainController.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/token-search-discovery-controller/src/token-search-discovery-controller.ts b/packages/token-search-discovery-controller/src/token-search-discovery-controller.ts index 5f02a9d0a08..b8228c0c9db 100644 --- a/packages/token-search-discovery-controller/src/token-search-discovery-controller.ts +++ b/packages/token-search-discovery-controller/src/token-search-discovery-controller.ts @@ -1,10 +1,10 @@ -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, - StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, + type StateMetadata, +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { AbstractTokenDiscoveryApiService } from './token-discovery-api-service/abstract-token-discovery-api-service'; import type { AbstractTokenSearchApiService } from './token-search-api-service/abstract-token-search-api-service'; @@ -36,13 +36,13 @@ const tokenSearchDiscoveryControllerMetadata: StateMetadata; /** diff --git a/packages/token-search-discovery-controller/tsconfig.build.json b/packages/token-search-discovery-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 100644 --- a/packages/token-search-discovery-controller/tsconfig.build.json +++ b/packages/token-search-discovery-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/token-search-discovery-controller/tsconfig.json b/packages/token-search-discovery-controller/tsconfig.json index 831cc7b8670..68c3ddfc2cd 100644 --- a/packages/token-search-discovery-controller/tsconfig.json +++ b/packages/token-search-discovery-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../../packages/base-controller" }], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..2521f5304e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4733,6 +4733,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" From 4a6fc1057a2850e4682b6882b9b729f996e09f16 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Mon, 8 Sep 2025 15:49:06 +0200 Subject: [PATCH 143/247] refactor: migrate SignatureController to @metamask/messenger --- packages/signature-controller/CHANGELOG.md | 2 + packages/signature-controller/package.json | 1 + .../src/SignatureController.test.ts | 4 +- .../src/SignatureController.ts | 42 +++++++++---------- .../signature-controller/tsconfig.build.json | 3 ++ packages/signature-controller/tsconfig.json | 3 ++ yarn.lock | 1 + 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/signature-controller/CHANGELOG.md b/packages/signature-controller/CHANGELOG.md index 5ee6efed565..5e1ed7663b3 100644 --- a/packages/signature-controller/CHANGELOG.md +++ b/packages/signature-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6496](https://github.com/MetaMask/core/pull/6496)) + - Previously, `SignatureController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708)) ## [34.0.0] diff --git a/packages/signature-controller/package.json b/packages/signature-controller/package.json index 16880bb4ff7..ba619a75ea1 100644 --- a/packages/signature-controller/package.json +++ b/packages/signature-controller/package.json @@ -50,6 +50,7 @@ "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", "@metamask/eth-sig-util": "^8.2.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "jsonschema": "^1.4.1", "lodash": "^4.17.21", diff --git a/packages/signature-controller/src/SignatureController.test.ts b/packages/signature-controller/src/SignatureController.test.ts index 6fc70f9e00c..6742fbcd39e 100644 --- a/packages/signature-controller/src/SignatureController.test.ts +++ b/packages/signature-controller/src/SignatureController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { SIWEMessage } from '@metamask/controller-utils'; import { detectSIWE, ORIGIN_METAMASK } from '@metamask/controller-utils'; import { SignTypedDataVersion } from '@metamask/keyring-controller'; @@ -1534,7 +1534,7 @@ describe('SignatureController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/signature-controller/src/SignatureController.ts b/packages/signature-controller/src/SignatureController.ts index 7531a81c587..ca8e878eec6 100644 --- a/packages/signature-controller/src/SignatureController.ts +++ b/packages/signature-controller/src/SignatureController.ts @@ -4,12 +4,11 @@ import type { AcceptResultCallbacks, AddResult, } from '@metamask/approval-controller'; -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import type { TraceCallback, TraceContext } from '@metamask/controller-utils'; import { ApprovalType, @@ -32,6 +31,7 @@ import { SigningStage, type AddLog, } from '@metamask/logging-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller'; import type { Hex, Json } from '@metamask/utils'; // This package purposefully relies on Node's EventEmitter module. @@ -73,31 +73,31 @@ const stateMetadata = { signatureRequests: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, unapprovedPersonalMsgs: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, unapprovedTypedMessages: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, unapprovedPersonalMsgCount: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, unapprovedTypedMessagesCount: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -176,12 +176,10 @@ export type SignatureControllerActions = GetSignatureState; export type SignatureControllerEvents = SignatureStateChange; -export type SignatureControllerMessenger = RestrictedMessenger< +export type SignatureControllerMessenger = Messenger< typeof controllerName, SignatureControllerActions | AllowedActions, - SignatureControllerEvents, - AllowedActions['type'], - never + SignatureControllerEvents >; export type SignatureControllerOptions = { @@ -477,7 +475,7 @@ export class SignatureController extends BaseController< decodedPermission = decodePermissionFromRequest({ origin: request.origin, data, - messenger: this.messagingSystem, + messenger: this.messenger, }); } catch (error) { // we ignore this error, because it simply means the request could not be @@ -791,7 +789,7 @@ export class SignatureController extends BaseController< switch (type) { case SignatureRequestType.PersonalSign: - signature = await this.messagingSystem.call( + signature = await this.messenger.call( 'KeyringController:signPersonalMessage', // Keyring controller temporarily using message manager types. // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -804,7 +802,7 @@ export class SignatureController extends BaseController< ? this.#parseTypedData(messageParams, metadata.version) : messageParams; - signature = await this.messagingSystem.call( + signature = await this.messenger.call( 'KeyringController:signTypedMessage', finalRequest, metadata.version as SignTypedDataVersion, @@ -883,7 +881,7 @@ export class SignatureController extends BaseController< parentContext: traceContext, }); - return (await this.messagingSystem.call( + return (await this.messenger.call( 'ApprovalController:addRequest', { id, @@ -986,7 +984,7 @@ export class SignatureController extends BaseController< version, ); - this.messagingSystem.call('LoggingController:add', { + this.messenger.call('LoggingController:add', { type: LogType.EthSignLog, data: { signingMethod, @@ -1028,7 +1026,7 @@ export class SignatureController extends BaseController< throw new Error('Network client ID not found in request'); } - const networkClient = this.messagingSystem.call( + const networkClient = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -1069,7 +1067,7 @@ export class SignatureController extends BaseController< } #getInternalAccounts(): Hex[] { - const state = this.messagingSystem.call('AccountsController:getState'); + const state = this.messenger.call('AccountsController:getState'); /* istanbul ignore next */ return Object.values(state.internalAccounts?.accounts ?? {}) diff --git a/packages/signature-controller/tsconfig.build.json b/packages/signature-controller/tsconfig.build.json index 1574be2f695..a89237f1d75 100644 --- a/packages/signature-controller/tsconfig.build.json +++ b/packages/signature-controller/tsconfig.build.json @@ -32,6 +32,9 @@ }, { "path": "../network-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/signature-controller/tsconfig.json b/packages/signature-controller/tsconfig.json index 99b81e54d19..0e27711c20a 100644 --- a/packages/signature-controller/tsconfig.json +++ b/packages/signature-controller/tsconfig.json @@ -30,6 +30,9 @@ }, { "path": "../network-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src"] diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..7433a09c4f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4542,6 +4542,7 @@ __metadata: "@metamask/gator-permissions-controller": "npm:^0.2.0" "@metamask/keyring-controller": "npm:^23.1.0" "@metamask/logging-controller": "npm:^6.0.4" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.2.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" From 84486cde64c97aa88fb5284a2a8e4e1d22e1495e Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Mon, 8 Sep 2025 16:13:12 +0200 Subject: [PATCH 144/247] refactor: migrate ShieldController to @metamask/messenger --- packages/shield-controller/CHANGELOG.md | 6 ++ packages/shield-controller/package.json | 1 + .../src/ShieldController.test.ts | 56 ++++++++-------- .../shield-controller/src/ShieldController.ts | 49 +++++++------- packages/shield-controller/src/index.ts | 1 + .../tests/mocks/messenger.ts | 67 +++++++++++++------ .../shield-controller/tsconfig.build.json | 3 +- packages/shield-controller/tsconfig.json | 3 +- yarn.lock | 1 + 9 files changed, 113 insertions(+), 74 deletions(-) diff --git a/packages/shield-controller/CHANGELOG.md b/packages/shield-controller/CHANGELOG.md index 02e4120e4f0..f814934deb7 100644 --- a/packages/shield-controller/CHANGELOG.md +++ b/packages/shield-controller/CHANGELOG.md @@ -7,8 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add and Export new Controller Action `ShieldControllerGetStateAction` ([#6497](https://github.com/MetaMask/core/pull/6497)) + ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6497](https://github.com/MetaMask/core/pull/6497)) + - Previously, `ShieldController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708)) ## [0.2.0] diff --git a/packages/shield-controller/package.json b/packages/shield-controller/package.json index 775f4f31671..f423182c3b2 100644 --- a/packages/shield-controller/package.json +++ b/packages/shield-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/shield-controller/src/ShieldController.test.ts b/packages/shield-controller/src/ShieldController.test.ts index 94e77ce8a7c..98c3a775997 100644 --- a/packages/shield-controller/src/ShieldController.test.ts +++ b/packages/shield-controller/src/ShieldController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { SignatureRequest } from '@metamask/signature-controller'; import { SignatureRequestStatus, @@ -34,7 +34,7 @@ function setup({ transactionHistoryLimit?: number; } = {}) { const backend = createMockBackend(); - const { messenger, baseMessenger } = createMockMessenger(); + const { messenger, rootMessenger } = createMockMessenger(); const controller = new ShieldController({ backend, @@ -46,7 +46,7 @@ function setup({ return { controller, messenger, - baseMessenger, + rootMessenger, backend, }; } @@ -54,15 +54,15 @@ function setup({ describe('ShieldController', () => { describe('checkCoverage', () => { it('should trigger checkCoverage when a new transaction is added', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, backend } = setup(); const txMeta = generateMockTxMeta(); const coverageResultReceived = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -72,11 +72,11 @@ describe('ShieldController', () => { }); it('should no longer trigger checkCoverage when controller is stopped', async () => { - const { controller, baseMessenger, backend } = setup(); + const { controller, rootMessenger, backend } = setup(); controller.stop(); const txMeta = generateMockTxMeta(); const coverageResultReceived = new Promise((resolve, reject) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); @@ -85,7 +85,7 @@ describe('ShieldController', () => { 100, ); }); - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -124,17 +124,17 @@ describe('ShieldController', () => { }); it('should check coverage when a transaction is simulated', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, backend } = setup(); const txMeta = generateMockTxMeta(); const coverageResultReceived = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); // Add transaction. - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -146,7 +146,7 @@ describe('ShieldController', () => { txMeta.simulationData = { tokenBalanceChanges: [], }; - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -158,15 +158,15 @@ describe('ShieldController', () => { describe('checkSignatureCoverage', () => { it('should check signature coverage', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, backend } = setup(); const signatureRequest = generateMockSignatureRequest(); const coverageResultReceived = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { [signatureRequest.id]: signatureRequest }, @@ -181,15 +181,15 @@ describe('ShieldController', () => { }); it('should check coverage for multiple signature request', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, backend } = setup(); const signatureRequest1 = generateMockSignatureRequest(); const coverageResultReceived1 = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { @@ -205,12 +205,12 @@ describe('ShieldController', () => { const signatureRequest2 = generateMockSignatureRequest(); const coverageResultReceived2 = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { @@ -240,7 +240,7 @@ describe('ShieldController', () => { updateSignatureRequest?: (signatureRequest: SignatureRequest) => void; }, ) { - const { messenger, baseMessenger } = components; + const { messenger, rootMessenger } = components; // Create a promise that resolves when the state changes const stateUpdated = new Promise((resolve) => @@ -249,7 +249,7 @@ describe('ShieldController', () => { // Publish a signature request const signatureRequest = generateMockSignatureRequest(); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { [signatureRequest.id]: signatureRequest }, @@ -265,7 +265,7 @@ describe('ShieldController', () => { updatedSignatureRequest.status = SignatureRequestStatus.Signed; updatedSignatureRequest.rawSig = '0x00'; options?.updateSignatureRequest?.(updatedSignatureRequest); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { [signatureRequest.id]: updatedSignatureRequest }, @@ -327,7 +327,7 @@ describe('ShieldController', () => { components: ReturnType, options?: { updateTransaction: (txMeta: TransactionMeta) => void }, ) { - const { messenger, baseMessenger } = components; + const { messenger, rootMessenger } = components; // Create a promise that resolves when the state changes const stateUpdated = new Promise((resolve) => messenger.subscribe('ShieldController:stateChange', resolve), @@ -335,7 +335,7 @@ describe('ShieldController', () => { // Publish a transaction const txMeta = generateMockTxMeta(); - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -349,7 +349,7 @@ describe('ShieldController', () => { updatedTxMeta.status = TransactionStatus.submitted; updatedTxMeta.hash = '0x00'; options?.updateTransaction(updatedTxMeta); - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [updatedTxMeta] } as TransactionControllerState, undefined as never, @@ -402,7 +402,7 @@ describe('ShieldController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/shield-controller/src/ShieldController.ts b/packages/shield-controller/src/ShieldController.ts index 1578b258280..d688d153330 100644 --- a/packages/shield-controller/src/ShieldController.ts +++ b/packages/shield-controller/src/ShieldController.ts @@ -1,8 +1,9 @@ -import { BaseController } from '@metamask/base-controller'; -import type { - ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import { SignatureRequestStatus, SignatureRequestType, @@ -54,6 +55,11 @@ export function getDefaultShieldControllerState(): ShieldControllerState { }; } +export type ShieldControllerGetStateAction = ControllerGetStateAction< + typeof controllerName, + ShieldControllerState +>; + export type ShieldControllerCheckCoverageAction = { type: `${typeof controllerName}:checkCoverage`; handler: ShieldController['checkCoverage']; @@ -62,7 +68,9 @@ export type ShieldControllerCheckCoverageAction = { /** * The internal actions available to the ShieldController. */ -export type ShieldControllerActions = ShieldControllerCheckCoverageAction; +export type ShieldControllerActions = + | ShieldControllerGetStateAction + | ShieldControllerCheckCoverageAction; export type ShieldControllerCoverageResultReceivedEvent = { type: `${typeof controllerName}:coverageResultReceived`; @@ -81,11 +89,6 @@ export type ShieldControllerEvents = | ShieldControllerCoverageResultReceivedEvent | ShieldControllerStateChangeEvent; -/** - * The external actions available to the ShieldController. - */ -type AllowedActions = never; - /** * The external events available to the ShieldController. */ @@ -96,12 +99,10 @@ type AllowedEvents = /** * The messenger of the {@link ShieldController}. */ -export type ShieldControllerMessenger = RestrictedMessenger< +export type ShieldControllerMessenger = Messenger< typeof controllerName, - ShieldControllerActions | AllowedActions, - ShieldControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + ShieldControllerActions, + ShieldControllerEvents | AllowedEvents >; /** @@ -112,13 +113,13 @@ const metadata = { coverageResults: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, orderedTransactionHistory: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; @@ -180,13 +181,13 @@ export class ShieldController extends BaseController< } start() { - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:stateChange', this.#transactionControllerStateChangeHandler, (state) => state.transactions, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'SignatureController:stateChange', this.#signatureControllerStateChangeHandler, (state) => state.signatureRequests, @@ -194,12 +195,12 @@ export class ShieldController extends BaseController< } stop() { - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'TransactionController:stateChange', this.#transactionControllerStateChangeHandler, ); - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'SignatureController:stateChange', this.#signatureControllerStateChangeHandler, ); @@ -294,7 +295,7 @@ export class ShieldController extends BaseController< const coverageResult = await this.#backend.checkCoverage(txMeta); // Publish coverage result - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:coverageResultReceived`, coverageResult, ); @@ -319,7 +320,7 @@ export class ShieldController extends BaseController< await this.#backend.checkSignatureCoverage(signatureRequest); // Publish coverage result - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:coverageResultReceived`, coverageResult, ); diff --git a/packages/shield-controller/src/index.ts b/packages/shield-controller/src/index.ts index 07ebbb3b149..4856c1edc75 100644 --- a/packages/shield-controller/src/index.ts +++ b/packages/shield-controller/src/index.ts @@ -8,6 +8,7 @@ export type { ShieldControllerEvents, ShieldControllerMessenger, ShieldControllerState, + ShieldControllerGetStateAction, ShieldControllerCheckCoverageAction, ShieldControllerCoverageResultReceivedEvent, ShieldControllerStateChangeEvent, diff --git a/packages/shield-controller/tests/mocks/messenger.ts b/packages/shield-controller/tests/mocks/messenger.ts index f35b43da9b3..b427b22a8fb 100644 --- a/packages/shield-controller/tests/mocks/messenger.ts +++ b/packages/shield-controller/tests/mocks/messenger.ts @@ -1,37 +1,64 @@ -import { Messenger } from '@metamask/base-controller'; - -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; -import type { ShieldControllerActions } from '../../src'; import { - type ShieldControllerEvents, - type ShieldControllerMessenger, -} from '../../src'; + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; + +import { type ShieldControllerMessenger } from '../../src'; import { controllerName } from '../../src/constants'; +type AllShieldControllerActions = MessengerActions; + +type AllShieldControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllShieldControllerActions, + AllShieldControllerEvents +>; + +/** + * Constructs the root messenger. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Create a mock messenger. * * @returns A mock messenger. */ -export function createMockMessenger() { - const baseMessenger = new Messenger< - ShieldControllerActions | ExtractAvailableAction, - ShieldControllerEvents | ExtractAvailableEvent - >(); - const messenger = baseMessenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [ +export function createMockMessenger(): { + rootMessenger: RootMessenger; + messenger: ShieldControllerMessenger; +} { + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< + typeof controllerName, + AllShieldControllerActions, + AllShieldControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + events: [ 'SignatureController:stateChange', 'TransactionController:stateChange', ], }); return { - baseMessenger, + rootMessenger, messenger, }; } diff --git a/packages/shield-controller/tsconfig.build.json b/packages/shield-controller/tsconfig.build.json index 1cc45a83d78..edfd16e6b74 100644 --- a/packages/shield-controller/tsconfig.build.json +++ b/packages/shield-controller/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../signature-controller/tsconfig.build.json" }, - { "path": "../transaction-controller/tsconfig.build.json" } + { "path": "../transaction-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/shield-controller/tsconfig.json b/packages/shield-controller/tsconfig.json index 0ec16827b92..792a6fa5f7d 100644 --- a/packages/shield-controller/tsconfig.json +++ b/packages/shield-controller/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../signature-controller" }, - { "path": "../transaction-controller" } + { "path": "../transaction-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src", "./tests"] } diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..646136c5ae9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4511,6 +4511,7 @@ __metadata: "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/signature-controller": "npm:^34.0.0" "@metamask/transaction-controller": "npm:^60.6.0" "@metamask/utils": "npm:^11.8.1" From 7cc86e329d29a9d0396ca86d6f00060c76cc61cf Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 9 Sep 2025 12:03:13 +0200 Subject: [PATCH 145/247] refactor: migrate SelectedNetworkController to @metamask/messenger --- .../selected-network-controller/CHANGELOG.md | 2 + .../selected-network-controller/package.json | 1 + .../src/SelectedNetworkController.ts | 74 ++++---- .../tests/SelectedNetworkController.test.ts | 161 ++++++++++-------- .../tests/SelectedNetworkMiddleware.test.ts | 95 +++++++---- .../tsconfig.build.json | 3 +- .../selected-network-controller/tsconfig.json | 3 + yarn.lock | 1 + 8 files changed, 199 insertions(+), 141 deletions(-) diff --git a/packages/selected-network-controller/CHANGELOG.md b/packages/selected-network-controller/CHANGELOG.md index 9aa4122e993..e8124d18c75 100644 --- a/packages/selected-network-controller/CHANGELOG.md +++ b/packages/selected-network-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6500](https://github.com/MetaMask/core/pull/6500)) + - Previously, `SelectedNetworkController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.3.0` to `^8.4.0` ([#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/json-rpc-engine` from `^10.0.3` to `^10.1.0` ([#6678](https://github.com/MetaMask/core/pull/6678)) diff --git a/packages/selected-network-controller/package.json b/packages/selected-network-controller/package.json index 13b7bdd533a..8f82d939339 100644 --- a/packages/selected-network-controller/package.json +++ b/packages/selected-network-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.0", "@metamask/json-rpc-engine": "^10.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/swappable-obj-proxy": "^2.3.0", "@metamask/utils": "^11.8.1" }, diff --git a/packages/selected-network-controller/src/SelectedNetworkController.ts b/packages/selected-network-controller/src/SelectedNetworkController.ts index 0cf0273eea7..d95b1af3b58 100644 --- a/packages/selected-network-controller/src/SelectedNetworkController.ts +++ b/packages/selected-network-controller/src/SelectedNetworkController.ts @@ -1,5 +1,9 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { BlockTrackerProxy, NetworkClientId, @@ -16,15 +20,14 @@ import type { } from '@metamask/permission-controller'; import { createEventEmitterProxy } from '@metamask/swappable-obj-proxy'; import type { Hex } from '@metamask/utils'; -import type { Patch } from 'immer'; -export const controllerName = 'SelectedNetworkController'; +const controllerName = 'SelectedNetworkController'; const stateMetadata = { domains: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -51,15 +54,17 @@ export type SelectedNetworkControllerState = { domains: Record; }; -export type SelectedNetworkControllerStateChangeEvent = { - type: typeof SelectedNetworkControllerEventTypes.stateChange; - payload: [SelectedNetworkControllerState, Patch[]]; -}; +export type SelectedNetworkControllerStateChangeEvent = + ControllerStateChangeEvent< + typeof controllerName, + SelectedNetworkControllerState + >; -export type SelectedNetworkControllerGetSelectedNetworkStateAction = { - type: typeof SelectedNetworkControllerActionTypes.getState; - handler: () => SelectedNetworkControllerState; -}; +export type SelectedNetworkControllerGetSelectedNetworkStateAction = + ControllerGetStateAction< + typeof controllerName, + SelectedNetworkControllerState + >; export type SelectedNetworkControllerGetNetworkClientIdForDomainAction = { type: typeof SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain; @@ -76,7 +81,7 @@ export type SelectedNetworkControllerActions = | SelectedNetworkControllerGetNetworkClientIdForDomainAction | SelectedNetworkControllerSetNetworkClientIdForDomainAction; -export type AllowedActions = +type AllowedActions = | NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetSelectedNetworkClientAction | NetworkControllerGetStateAction @@ -86,16 +91,14 @@ export type AllowedActions = export type SelectedNetworkControllerEvents = SelectedNetworkControllerStateChangeEvent; -export type AllowedEvents = +type AllowedEvents = | NetworkControllerStateChangeEvent | PermissionControllerStateChange; -export type SelectedNetworkControllerMessenger = RestrictedMessenger< +export type SelectedNetworkControllerMessenger = Messenger< typeof controllerName, SelectedNetworkControllerActions | AllowedActions, - SelectedNetworkControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SelectedNetworkControllerEvents | AllowedEvents >; export type SelectedNetworkControllerOptions = { @@ -123,7 +126,7 @@ export class SelectedNetworkController extends BaseController< * Construct a SelectedNetworkController controller. * * @param options - The controller options. - * @param options.messenger - The restricted messenger for the EncryptionPublicKey controller. + * @param options.messenger - The messenger for the SelectedNetworkController controller. * @param options.state - The controllers initial state. * @param options.domainProxyMap - A map for storing domain-specific proxies that are held in memory only during use. */ @@ -142,18 +145,18 @@ export class SelectedNetworkController extends BaseController< this.#registerMessageHandlers(); // this is fetching all the dapp permissions from the PermissionsController and looking for any domains that are not in domains state in this controller. Then we take any missing domains and add them to state here, setting it with the globally selected networkClientId (fetched from the NetworkController) - this.messagingSystem + this.messenger .call('PermissionController:getSubjectNames') .filter((domain) => this.state.domains[domain] === undefined) .forEach((domain) => this.setNetworkClientIdForDomain( domain, - this.messagingSystem.call('NetworkController:getState') + this.messenger.call('NetworkController:getState') .selectedNetworkClientId, ), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'PermissionController:stateChange', (_, patches) => { patches.forEach(({ op, path }) => { @@ -164,7 +167,7 @@ export class SelectedNetworkController extends BaseController< if (op === 'add' && this.state.domains[domain] === undefined) { this.setNetworkClientIdForDomain( domain, - this.messagingSystem.call('NetworkController:getState') + this.messenger.call('NetworkController:getState') .selectedNetworkClientId, ); } else if ( @@ -178,7 +181,7 @@ export class SelectedNetworkController extends BaseController< }, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:stateChange', ( { selectedNetworkClientId, networkConfigurationsByChainId }, @@ -242,11 +245,11 @@ export class SelectedNetworkController extends BaseController< } #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( SelectedNetworkControllerActionTypes.getNetworkClientIdForDomain, this.getNetworkClientIdForDomain.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( SelectedNetworkControllerActionTypes.setNetworkClientIdForDomain, this.setNetworkClientIdForDomain.bind(this), ); @@ -256,7 +259,7 @@ export class SelectedNetworkController extends BaseController< domain: Domain, networkClientId: NetworkClientId, ) { - const networkClient = this.messagingSystem.call( + const networkClient = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -279,7 +282,7 @@ export class SelectedNetworkController extends BaseController< * @param domain - The domain for which to unset the network client ID. */ #unsetNetworkClientIdForDomain(domain: Domain) { - const globallySelectedNetworkClient = this.messagingSystem.call( + const globallySelectedNetworkClient = this.messenger.call( 'NetworkController:getSelectedNetworkClient', ); const networkProxy = this.#domainProxyMap.get(domain); @@ -297,10 +300,7 @@ export class SelectedNetworkController extends BaseController< } #domainHasPermissions(domain: Domain): boolean { - return this.messagingSystem.call( - 'PermissionController:hasPermissions', - domain, - ); + return this.messenger.call('PermissionController:hasPermissions', domain); } setNetworkClientIdForDomain( @@ -324,7 +324,7 @@ export class SelectedNetworkController extends BaseController< getNetworkClientIdForDomain(domain: Domain): NetworkClientId { const { selectedNetworkClientId: metamaskSelectedNetworkClientId } = - this.messagingSystem.call('NetworkController:getState'); + this.messenger.call('NetworkController:getState'); return this.state.domains[domain] ?? metamaskSelectedNetworkClientId; } @@ -338,7 +338,7 @@ export class SelectedNetworkController extends BaseController< getProviderAndBlockTracker(domain: Domain): NetworkProxy { // If the domain is 'metamask', return the NetworkController's globally selected network client proxy if (domain === METAMASK_DOMAIN) { - const networkClient = this.messagingSystem.call( + const networkClient = this.messenger.call( 'NetworkController:getSelectedNetworkClient', ); if (networkClient === undefined) { @@ -352,12 +352,12 @@ export class SelectedNetworkController extends BaseController< let networkClient; if (this.#domainHasPermissions(domain)) { const networkClientId = this.getNetworkClientIdForDomain(domain); - networkClient = this.messagingSystem.call( + networkClient = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); } else { - networkClient = this.messagingSystem.call( + networkClient = this.messenger.call( 'NetworkController:getSelectedNetworkClient', ); if (networkClient === undefined) { diff --git a/packages/selected-network-controller/tests/SelectedNetworkController.test.ts b/packages/selected-network-controller/tests/SelectedNetworkController.test.ts index c58c146ed95..4be27de0454 100644 --- a/packages/selected-network-controller/tests/SelectedNetworkController.test.ts +++ b/packages/selected-network-controller/tests/SelectedNetworkController.test.ts @@ -1,4 +1,11 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { type ProviderProxy, type BlockTrackerProxy, @@ -10,58 +17,61 @@ import { createEventEmitterProxy } from '@metamask/swappable-obj-proxy'; import type { Hex } from '@metamask/utils'; import type { - AllowedActions, - AllowedEvents, - SelectedNetworkControllerActions, - SelectedNetworkControllerEvents, SelectedNetworkControllerState, Domain, NetworkProxy, + SelectedNetworkControllerMessenger, } from '../src/SelectedNetworkController'; import { METAMASK_DOMAIN, SelectedNetworkController, - controllerName, } from '../src/SelectedNetworkController'; +const controllerName = 'SelectedNetworkController'; + +type AllSelectedNetworkControllerActions = + MessengerActions; + +type AllSelectedNetworkControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllSelectedNetworkControllerActions, + AllSelectedNetworkControllerEvents +>; + /** - * Builds a new instance of the Messenger class for the SelectedNetworkController. + * Constructs the root messenger. * - * @returns A new instance of the Messenger class for the SelectedNetworkController. + * @returns A root messenger. */ -function buildMessenger() { - return new Messenger< - SelectedNetworkControllerActions | AllowedActions, - SelectedNetworkControllerEvents | AllowedEvents - >(); +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); } /** * Build a restricted messenger for the selected network controller. * * @param options - The options bag. - * @param options.messenger - A messenger. + * @param options.rootMessenger - A messenger. * @param options.getSubjectNames - Permissions controller list of domains with permissions * @returns The network controller restricted messenger. */ function buildSelectedNetworkControllerMessenger({ - messenger = new Messenger< - SelectedNetworkControllerActions | AllowedActions, - SelectedNetworkControllerEvents | AllowedEvents - >(), + rootMessenger, getSubjectNames, }: { - messenger?: Messenger< - SelectedNetworkControllerActions | AllowedActions, - SelectedNetworkControllerEvents | AllowedEvents - >; + rootMessenger: RootMessenger; getSubjectNames?: string[]; -} = {}) { +}) { const mockGetNetworkClientById = jest.fn().mockReturnValue({ provider: { request: jest.fn() }, blockTracker: { getLatestBlock: jest.fn() }, }); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'NetworkController:getNetworkClientById', mockGetNetworkClientById, ); @@ -69,45 +79,54 @@ function buildSelectedNetworkControllerMessenger({ provider: { request: jest.fn() }, blockTracker: { getLatestBlock: jest.fn() }, }); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'NetworkController:getSelectedNetworkClient', mockGetSelectedNetworkClient, ); const mockNetworkControllerGetState = jest .fn() .mockReturnValue({ selectedNetworkClientId: 'mainnet' }); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'NetworkController:getState', mockNetworkControllerGetState, ); const mockHasPermissions = jest.fn().mockReturnValue(true); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'PermissionController:hasPermissions', mockHasPermissions, ); const mockGetSubjectNames = jest.fn().mockReturnValue(getSubjectNames); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'PermissionController:getSubjectNames', mockGetSubjectNames, ); - const restrictedMessenger = messenger.getRestricted({ - name: controllerName, - allowedActions: [ + const messenger = new Messenger< + typeof controllerName, + AllSelectedNetworkControllerActions, + AllSelectedNetworkControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: [ 'NetworkController:getNetworkClientById', 'NetworkController:getSelectedNetworkClient', 'NetworkController:getState', 'PermissionController:hasPermissions', 'PermissionController:getSubjectNames', ], - allowedEvents: [ + events: [ 'NetworkController:stateChange', 'PermissionController:stateChange', ], }); return { - restrictedMessenger, + messenger, mockGetNetworkClientById, mockGetSelectedNetworkClient, mockNetworkControllerGetState, @@ -161,21 +180,22 @@ const setup = ({ } return mockProviderProxy; }); - const messenger = buildMessenger(); - const { restrictedMessenger, ...mockMessengerActions } = + const rootMessenger = getRootMessenger(); + const { messenger, ...mockMessengerActions } = buildSelectedNetworkControllerMessenger({ - messenger, + rootMessenger, getSubjectNames, }); const controller = new SelectedNetworkController({ - messenger: restrictedMessenger, + messenger, state, domainProxyMap, }); return { controller, + rootMessenger, messenger, mockProviderProxy, mockBlockTrackerProxy, @@ -247,7 +267,7 @@ describe('SelectedNetworkController', () => { const deleteNetwork = ( chainId: Hex, networkControllerState: NetworkState, - messenger: ReturnType, + messenger: RootMessenger, mockNetworkControllerGetState: jest.Mock, ) => { delete networkControllerState.networkConfigurationsByChainId[chainId]; @@ -267,9 +287,10 @@ describe('SelectedNetworkController', () => { }; it('redirects domains to the globally selected network', () => { - const { controller, messenger, mockNetworkControllerGetState } = setup({ - state: { domains: initialDomains }, - }); + const { controller, rootMessenger, mockNetworkControllerGetState } = + setup({ + state: { domains: initialDomains }, + }); const networkControllerState = { ...getDefaultNetworkControllerState(), @@ -279,7 +300,7 @@ describe('SelectedNetworkController', () => { deleteNetwork( '0xaa36a7', networkControllerState, - messenger, + rootMessenger, mockNetworkControllerGetState, ); @@ -293,7 +314,7 @@ describe('SelectedNetworkController', () => { const domainProxyMap = new Map(); const { controller, - messenger, + rootMessenger, mockNetworkControllerGetState, mockGetNetworkClientById, } = setup({ @@ -324,7 +345,7 @@ describe('SelectedNetworkController', () => { deleteNetwork( '0xaa36a7', networkControllerState, - messenger, + rootMessenger, mockNetworkControllerGetState, ); @@ -342,9 +363,10 @@ describe('SelectedNetworkController', () => { 'chain-with-new-default.com': 'sepolia', }; - const { controller, messenger, mockNetworkControllerGetState } = setup({ - state: { domains: initialDomains }, - }); + const { controller, rootMessenger, mockNetworkControllerGetState } = + setup({ + state: { domains: initialDomains }, + }); const networkControllerState = getDefaultNetworkControllerState(); const goerliNetwork = @@ -362,7 +384,7 @@ describe('SelectedNetworkController', () => { networkControllerState, ); - messenger.publish( + rootMessenger.publish( 'NetworkController:stateChange', networkControllerState, [ @@ -385,9 +407,10 @@ describe('SelectedNetworkController', () => { 'chain-with-new-default.com': 'sepolia', }; - const { controller, messenger, mockNetworkControllerGetState } = setup({ - state: { domains: initialDomains }, - }); + const { controller, rootMessenger, mockNetworkControllerGetState } = + setup({ + state: { domains: initialDomains }, + }); const networkControllerState = getDefaultNetworkControllerState(); const goerliNetwork = @@ -406,7 +429,7 @@ describe('SelectedNetworkController', () => { networkControllerState, ); - messenger.publish( + rootMessenger.publish( 'NetworkController:stateChange', networkControllerState, [ @@ -733,7 +756,7 @@ describe('getProviderAndBlockTracker', () => { describe('PermissionController:stateChange', () => { describe('on permission add', () => { it('should add new domain to domains list', async () => { - const { controller, messenger } = setup({}); + const { controller, rootMessenger } = setup({}); const mockPermission = { parentCapability: 'eth_accounts', id: 'example.com', @@ -741,13 +764,17 @@ describe('PermissionController:stateChange', () => { caveats: [{ type: 'restrictToAccounts', value: ['0x...'] }], }; - messenger.publish('PermissionController:stateChange', { subjects: {} }, [ - { - op: 'add', - path: ['subjects', 'example.com', 'permissions'], - value: mockPermission, - }, - ]); + rootMessenger.publish( + 'PermissionController:stateChange', + { subjects: {} }, + [ + { + op: 'add', + path: ['subjects', 'example.com', 'permissions'], + value: mockPermission, + }, + ], + ); const { domains } = controller.state; expect(domains['example.com']).toBeDefined(); @@ -755,11 +782,11 @@ describe('PermissionController:stateChange', () => { describe('on permission removal', () => { it('should remove domain from domains list', async () => { - const { controller, messenger } = setup({ + const { controller, rootMessenger } = setup({ state: { domains: { 'example.com': 'foo' } }, }); - messenger.publish( + rootMessenger.publish( 'PermissionController:stateChange', { subjects: {} }, [ @@ -775,12 +802,12 @@ describe('PermissionController:stateChange', () => { }); it('should set the proxy to the globally selected network if the globally selected network client is initialized and a proxy exists for the domain', async () => { - const { controller, messenger, mockProviderProxy } = setup({ + const { controller, rootMessenger, mockProviderProxy } = setup({ state: { domains: { 'example.com': 'foo' } }, }); controller.getProviderAndBlockTracker('example.com'); - messenger.publish( + rootMessenger.publish( 'PermissionController:stateChange', { subjects: {} }, [ @@ -803,7 +830,7 @@ describe('PermissionController:stateChange', () => { it('should delete the proxy if the globally selected network client is not initialized but a proxy exists for the domain', async () => { const { controller, - messenger, + rootMessenger, domainProxyMap, mockProviderProxy, mockGetSelectedNetworkClient, @@ -814,7 +841,7 @@ describe('PermissionController:stateChange', () => { mockGetSelectedNetworkClient.mockReturnValue(undefined); expect(domainProxyMap.get('example.com')).toBeDefined(); - messenger.publish( + rootMessenger.publish( 'PermissionController:stateChange', { subjects: {} }, [ @@ -839,7 +866,7 @@ describe('PermissionController:stateChange', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/selected-network-controller/tests/SelectedNetworkMiddleware.test.ts b/packages/selected-network-controller/tests/SelectedNetworkMiddleware.test.ts index 13d66bcacd7..34893ed56da 100644 --- a/packages/selected-network-controller/tests/SelectedNetworkMiddleware.test.ts +++ b/packages/selected-network-controller/tests/SelectedNetworkMiddleware.test.ts @@ -1,35 +1,70 @@ -import { Messenger } from '@metamask/base-controller'; import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { JsonRpcResponse } from '@metamask/utils'; import { SelectedNetworkControllerActionTypes } from '../src/SelectedNetworkController'; -import type { - AllowedActions, - AllowedEvents, - SelectedNetworkControllerActions, - SelectedNetworkControllerEvents, -} from '../src/SelectedNetworkController'; +import type { SelectedNetworkControllerMessenger } from '../src/SelectedNetworkController'; import type { SelectedNetworkMiddlewareJsonRpcRequest } from '../src/SelectedNetworkMiddleware'; import { createSelectedNetworkMiddleware } from '../src/SelectedNetworkMiddleware'; -const buildMessenger = () => { +type AllSelectedNetworkControllerActions = + MessengerActions; + +type AllSelectedNetworkControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllSelectedNetworkControllerActions, + AllSelectedNetworkControllerEvents +>; + +const controllerName = 'SelectedNetworkController'; + +/** + * Constructs the root messenger. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + +/** + * Constructs the selected network controller messenger. + * + * @param rootMessenger - A root messenger. + * @returns A selected network controller messenger. + */ +function getSelectedNetworkControllerMessenger( + rootMessenger: RootMessenger, +): SelectedNetworkControllerMessenger { return new Messenger< - SelectedNetworkControllerActions | AllowedActions, - SelectedNetworkControllerEvents | AllowedEvents - >(); -}; + typeof controllerName, + AllSelectedNetworkControllerActions, + AllSelectedNetworkControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); +} const noop = jest.fn(); describe('createSelectedNetworkMiddleware', () => { it('throws if not provided an origin', async () => { - const messenger = buildMessenger(); + const rootMessenger = getRootMessenger(); const middleware = createSelectedNetworkMiddleware( - messenger.getRestricted({ - name: 'SelectedNetworkController', - allowedActions: [], - allowedEvents: [], - }), + getSelectedNetworkControllerMessenger(rootMessenger), ); const req: SelectedNetworkMiddlewareJsonRpcRequest = { id: '123', @@ -47,14 +82,9 @@ describe('createSelectedNetworkMiddleware', () => { }); it('puts networkClientId on request', async () => { - const messenger = buildMessenger(); - const middleware = createSelectedNetworkMiddleware( - messenger.getRestricted({ - name: 'SelectedNetworkController', - allowedActions: [], - allowedEvents: [], - }), - ); + const rootMessenger = getRootMessenger(); + const messenger = getSelectedNetworkControllerMessenger(rootMessenger); + const middleware = createSelectedNetworkMiddleware(messenger); const req = { origin: 'example.com', @@ -78,20 +108,13 @@ describe('createSelectedNetworkMiddleware', () => { it('implements the json-rpc-engine middleware interface appropriately', async () => { const engine = new JsonRpcEngine(); - const messenger = buildMessenger(); + const rootMessenger = getRootMessenger(); + const messenger = getSelectedNetworkControllerMessenger(rootMessenger); engine.push((req: SelectedNetworkMiddlewareJsonRpcRequest, _, next) => { req.origin = 'foobar'; next(); }); - engine.push( - createSelectedNetworkMiddleware( - messenger.getRestricted({ - name: 'SelectedNetworkController', - allowedActions: [], - allowedEvents: [], - }), - ), - ); + engine.push(createSelectedNetworkMiddleware(messenger)); const mockNextMiddleware = jest .fn() .mockImplementation((req, res, _, end) => { diff --git a/packages/selected-network-controller/tsconfig.build.json b/packages/selected-network-controller/tsconfig.build.json index 0113f476410..f387a699564 100644 --- a/packages/selected-network-controller/tsconfig.build.json +++ b/packages/selected-network-controller/tsconfig.build.json @@ -9,7 +9,8 @@ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../network-controller/tsconfig.build.json" }, { "path": "../json-rpc-engine/tsconfig.build.json" }, - { "path": "../permission-controller/tsconfig.build.json" } + { "path": "../permission-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/selected-network-controller/tsconfig.json b/packages/selected-network-controller/tsconfig.json index 9e391177a64..3f45736e967 100644 --- a/packages/selected-network-controller/tsconfig.json +++ b/packages/selected-network-controller/tsconfig.json @@ -16,6 +16,9 @@ }, { "path": "../permission-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "../../tests", "./src", "./tests"] diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..fb99dc40a05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4481,6 +4481,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" "@metamask/json-rpc-engine": "npm:^10.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.2.0" "@metamask/permission-controller": "npm:^11.0.6" "@metamask/swappable-obj-proxy": "npm:^2.3.0" From 779543dbaa00d2d2a6f7e5d5eabee1176c8b2404 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 9 Sep 2025 12:25:22 +0200 Subject: [PATCH 146/247] refactor: migrate RemoteFeatureFlagController to @metamask/messenger --- .../CHANGELOG.md | 2 + .../package.json | 1 + .../remote-feature-flag-controller-types.ts | 2 +- .../remote-feature-flag-controller.test.ts | 63 ++++++++++++------- .../src/remote-feature-flag-controller.ts | 42 ++++--------- .../tsconfig.build.json | 5 +- .../tsconfig.json | 2 +- yarn.lock | 1 + 8 files changed, 64 insertions(+), 54 deletions(-) diff --git a/packages/remote-feature-flag-controller/CHANGELOG.md b/packages/remote-feature-flag-controller/CHANGELOG.md index 622fe02d3d3..de456d30d1b 100644 --- a/packages/remote-feature-flag-controller/CHANGELOG.md +++ b/packages/remote-feature-flag-controller/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6502](https://github.com/MetaMask/core/pull/6502)) + - Previously, `RemoteFeatureFlagController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.4.2` to `^11.8.1` ([#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/controller-utils` from `^11.11.0` to `^11.14.0` ([#6303](https://github.com/MetaMask/core/pull/6303), [#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629)) diff --git a/packages/remote-feature-flag-controller/package.json b/packages/remote-feature-flag-controller/package.json index 501ee03ffa5..1d77536cddb 100644 --- a/packages/remote-feature-flag-controller/package.json +++ b/packages/remote-feature-flag-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "uuid": "^8.3.2" }, diff --git a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller-types.ts b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller-types.ts index 56fed8f1e8e..fe2b75f59df 100644 --- a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller-types.ts +++ b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller-types.ts @@ -1,4 +1,4 @@ -import type { ControllerGetStateAction } from '@metamask/base-controller'; +import type { ControllerGetStateAction } from '@metamask/base-controller/next'; import type { Json } from '@metamask/utils'; // Define accepted values for client, distribution, and environment diff --git a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts index 5cd5daa1aca..ae390caed34 100644 --- a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts +++ b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts @@ -1,19 +1,25 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service'; import { RemoteFeatureFlagController, - controllerName, DEFAULT_CACHE_DURATION, getDefaultRemoteFeatureFlagControllerState, } from './remote-feature-flag-controller'; +import type { RemoteFeatureFlagControllerMessenger } from './remote-feature-flag-controller'; import type { - RemoteFeatureFlagControllerActions, - RemoteFeatureFlagControllerMessenger, + FeatureFlags, RemoteFeatureFlagControllerState, - RemoteFeatureFlagControllerStateChangeEvent, -} from './remote-feature-flag-controller'; -import type { FeatureFlags } from './remote-feature-flag-controller-types'; +} from './remote-feature-flag-controller-types'; + +const controllerName = 'RemoteFeatureFlagController'; const MOCK_FLAGS: FeatureFlags = { feature1: true, @@ -350,7 +356,7 @@ describe('RemoteFeatureFlagController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -412,31 +418,44 @@ describe('RemoteFeatureFlagController', () => { }); }); -type RootAction = RemoteFeatureFlagControllerActions; -type RootEvent = RemoteFeatureFlagControllerStateChangeEvent; +type AllRemoteFeatureFlagControllerActions = + MessengerActions; + +type AllRemoteFeatureFlagControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllRemoteFeatureFlagControllerActions, + AllRemoteFeatureFlagControllerEvents +>; /** * Creates and returns a root messenger for testing * * @returns A messenger instance */ -function getRootMessenger(): Messenger { - return new Messenger(); +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); } /** - * Creates a restricted messenger for testing + * Creates a messenger for the RemoteFeatureFlagController * - * @param rootMessenger - The root messenger to restrict - * @returns A restricted messenger instance + * @returns A messenger instance */ -function getMessenger( - rootMessenger = getRootMessenger(), -): RemoteFeatureFlagControllerMessenger { - return rootMessenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], +function getMessenger(): RemoteFeatureFlagControllerMessenger { + const rootMessenger = getRootMessenger(); + return new Messenger< + typeof controllerName, + AllRemoteFeatureFlagControllerActions, + AllRemoteFeatureFlagControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, }); } diff --git a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.ts b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.ts index 640d5cac700..30e004d81e4 100644 --- a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.ts +++ b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.ts @@ -1,15 +1,16 @@ -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { AbstractClientConfigApiService } from './client-config-api-service/abstract-client-config-api-service'; import type { FeatureFlags, ServiceResponse, FeatureFlagScopeValue, + RemoteFeatureFlagControllerState, + RemoteFeatureFlagControllerGetStateAction, } from './remote-feature-flag-controller-types'; import { generateDeterministicRandomNumber, @@ -18,39 +19,28 @@ import { // === GENERAL === -export const controllerName = 'RemoteFeatureFlagController'; +const controllerName = 'RemoteFeatureFlagController'; export const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day // === STATE === -export type RemoteFeatureFlagControllerState = { - remoteFeatureFlags: FeatureFlags; - cacheTimestamp: number; -}; - const remoteFeatureFlagControllerMetadata = { remoteFeatureFlags: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, cacheTimestamp: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, }; // === MESSENGER === -export type RemoteFeatureFlagControllerGetStateAction = - ControllerGetStateAction< - typeof controllerName, - RemoteFeatureFlagControllerState - >; - export type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = { type: `${typeof controllerName}:updateRemoteFeatureFlags`; handler: RemoteFeatureFlagController['updateRemoteFeatureFlags']; @@ -59,8 +49,6 @@ export type RemoteFeatureFlagControllerGetRemoteFeatureFlagAction = { export type RemoteFeatureFlagControllerActions = RemoteFeatureFlagControllerGetStateAction; -export type AllowedActions = never; - export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent< typeof controllerName, @@ -70,14 +58,10 @@ export type RemoteFeatureFlagControllerStateChangeEvent = export type RemoteFeatureFlagControllerEvents = RemoteFeatureFlagControllerStateChangeEvent; -export type AllowedEvents = never; - -export type RemoteFeatureFlagControllerMessenger = RestrictedMessenger< +export type RemoteFeatureFlagControllerMessenger = Messenger< typeof controllerName, - RemoteFeatureFlagControllerActions | AllowedActions, - RemoteFeatureFlagControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + RemoteFeatureFlagControllerActions, + RemoteFeatureFlagControllerEvents >; /** diff --git a/packages/remote-feature-flag-controller/tsconfig.build.json b/packages/remote-feature-flag-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 100644 --- a/packages/remote-feature-flag-controller/tsconfig.build.json +++ b/packages/remote-feature-flag-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/remote-feature-flag-controller/tsconfig.json b/packages/remote-feature-flag-controller/tsconfig.json index 831cc7b8670..68c3ddfc2cd 100644 --- a/packages/remote-feature-flag-controller/tsconfig.json +++ b/packages/remote-feature-flag-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../../packages/base-controller" }], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..c1794641c8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4378,6 +4378,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" From b4e2db07c01e68cdb9dd71cb6286ab29679a5ca2 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 9 Sep 2025 12:21:08 +0200 Subject: [PATCH 147/247] refactor: migrate RateLimitController to @metamask/messenger --- eslint-warning-thresholds.json | 4 +- packages/rate-limit-controller/CHANGELOG.md | 2 + packages/rate-limit-controller/package.json | 1 + .../src/RateLimitController.test.ts | 91 ++++++++++++------- .../src/RateLimitController.ts | 49 +++++----- .../tsconfig.build.json | 5 +- .../tsconfig.json | 2 +- yarn.lock | 1 + 8 files changed, 95 insertions(+), 60 deletions(-) diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index b09cda9dd5e..7000aa46281 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -305,9 +305,7 @@ "@typescript-eslint/prefer-readonly": 1 }, "packages/rate-limit-controller/src/RateLimitController.ts": { - "jsdoc/check-tag-names": 4, - "jsdoc/require-returns": 1, - "jsdoc/tag-lines": 3 + "jsdoc/check-tag-names": 4 }, "packages/remote-feature-flag-controller/src/client-config-api-service/client-config-api-service.test.ts": { "import-x/order": 1, diff --git a/packages/rate-limit-controller/CHANGELOG.md b/packages/rate-limit-controller/CHANGELOG.md index 06852cb16f7..3c16b57c2b0 100644 --- a/packages/rate-limit-controller/CHANGELOG.md +++ b/packages/rate-limit-controller/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6503](https://github.com/MetaMask/core/pull/6503)) + - Previously, `RateLimitController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.2.0` to `^11.8.1` ([#6054](https://github.com/MetaMask/core/pull/6054), [#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.0.0` to `^8.4.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) diff --git a/packages/rate-limit-controller/package.json b/packages/rate-limit-controller/package.json index a13cc190cfd..a4f6ccae643 100644 --- a/packages/rate-limit-controller/package.json +++ b/packages/rate-limit-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", + "@metamask/messenger": "^0.3.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/utils": "^11.8.1" }, diff --git a/packages/rate-limit-controller/src/RateLimitController.test.ts b/packages/rate-limit-controller/src/RateLimitController.test.ts index 7bb31f0641b..3f7fc1ba528 100644 --- a/packages/rate-limit-controller/src/RateLimitController.test.ts +++ b/packages/rate-limit-controller/src/RateLimitController.test.ts @@ -1,12 +1,16 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; - -import type { - RateLimitControllerActions, - RateLimitControllerEvents, -} from './RateLimitController'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; + +import type { RateLimitMessenger } from './RateLimitController'; import { RateLimitController } from './RateLimitController'; -const name = 'RateLimitController'; +const controllerName = 'RateLimitController'; const implementations = { apiWithoutCustomLimit: { @@ -21,29 +25,48 @@ const implementations = { type RateLimitedApis = typeof implementations; +type AllRateLimitControllerActions = MessengerActions< + RateLimitMessenger +>; + +type AllRateLimitControllerEvents = MessengerEvents< + RateLimitMessenger +>; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllRateLimitControllerActions, + AllRateLimitControllerEvents +>; + /** - * Constructs an unrestricted messenger. + * Creates and returns a root messenger for testing * - * @returns An unrestricted messenger. + * @returns A messenger instance */ -function getUnrestrictedMessenger() { - return new Messenger< - RateLimitControllerActions, - RateLimitControllerEvents - >(); +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); } /** - * Constructs a restricted messenger. + * Constructs a messenger for the RateLimitController. * - * @param messenger - An optional unrestricted messenger - * @returns A restricted messenger. + * @param rootMessenger - An optional root messenger + * @returns A messenger for the RateLimitController. */ -function getRestrictedMessenger(messenger = getUnrestrictedMessenger()) { - return messenger.getRestricted({ - name, - allowedActions: [], - allowedEvents: [], +function getMessenger( + rootMessenger = getRootMessenger(), +): RateLimitMessenger { + return new Messenger< + typeof controllerName, + AllRateLimitControllerActions, + AllRateLimitControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, }); } @@ -62,8 +85,8 @@ describe('RateLimitController', () => { }); it('action: RateLimitController:call', async () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getMessenger(rootMessenger); // Registers action handlers new RateLimitController({ @@ -72,7 +95,7 @@ describe('RateLimitController', () => { }); expect( - await unrestricted.call( + await rootMessenger.call( 'RateLimitController:call', origin, 'apiWithoutCustomLimit', @@ -88,7 +111,7 @@ describe('RateLimitController', () => { }); it('uses apiWithoutCustomLimit method', async () => { - const messenger = getRestrictedMessenger(); + const messenger = getMessenger(); const controller = new RateLimitController({ implementations, @@ -105,7 +128,7 @@ describe('RateLimitController', () => { }); it('returns false if rate-limited', async () => { - const messenger = getRestrictedMessenger(); + const messenger = getMessenger(); const controller = new RateLimitController({ implementations, messenger, @@ -154,7 +177,7 @@ describe('RateLimitController', () => { }); it('rate limit is reset after timeout', async () => { - const messenger = getRestrictedMessenger(); + const messenger = getMessenger(); const controller = new RateLimitController({ implementations, messenger, @@ -198,7 +221,7 @@ describe('RateLimitController', () => { }); it('timeout is only applied once per window', async () => { - const messenger = getRestrictedMessenger(); + const messenger = getMessenger(); const controller = new RateLimitController({ implementations, messenger, @@ -225,14 +248,14 @@ describe('RateLimitController', () => { it('includes expected state in debug snapshots', () => { const controller = new RateLimitController({ implementations, - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); expect( deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -240,7 +263,7 @@ describe('RateLimitController', () => { it('includes expected state in state logs', () => { const controller = new RateLimitController({ implementations, - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); expect( @@ -255,7 +278,7 @@ describe('RateLimitController', () => { it('persists expected state', () => { const controller = new RateLimitController({ implementations, - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); expect( @@ -270,7 +293,7 @@ describe('RateLimitController', () => { it('exposes expected state to UI', () => { const controller = new RateLimitController({ implementations, - messenger: getRestrictedMessenger(), + messenger: getMessenger(), }); expect( diff --git a/packages/rate-limit-controller/src/RateLimitController.ts b/packages/rate-limit-controller/src/RateLimitController.ts index 768c677a8c7..404539193bf 100644 --- a/packages/rate-limit-controller/src/RateLimitController.ts +++ b/packages/rate-limit-controller/src/RateLimitController.ts @@ -1,15 +1,15 @@ -import type { - ActionConstraint, - RestrictedMessenger, - ControllerGetStateAction, - ControllerStateChangeEvent, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; +import type { Messenger, ActionConstraint } from '@metamask/messenger'; import { rpcErrors } from '@metamask/rpc-errors'; import { getKnownPropertyNames } from '@metamask/utils'; /** * A rate-limited API endpoint. + * * @property method - The method that is rate-limited. * @property rateLimitTimeout - The time window in which the rate limit is applied (in ms). * @property rateLimitCount - The amount of calls an origin can make in the rate limit time window. @@ -27,6 +27,7 @@ export type RateLimitedApiMap = Record; /** * A map of rate-limited API types to the number of requests made in a given interval for each origin and api type combination. + * * @template RateLimitedApis - A {@link RateLimitedApiMap} containing the rate-limited API endpoints that is used by the {@link RateLimitController}. */ export type RateLimitedRequests = @@ -34,6 +35,7 @@ export type RateLimitedRequests = /** * The state of the {@link RateLimitController}. + * * @template RateLimitedApis - A {@link RateLimitedApiMap} containing the rate-limited API endpoints that is used by the {@link RateLimitController}. * @property requests - An object containing the number of requests made in a given interval for each origin and api type combination. */ @@ -41,20 +43,26 @@ export type RateLimitState = { requests: RateLimitedRequests; }; -const name = 'RateLimitController'; +const controllerName = 'RateLimitController'; export type RateLimitControllerStateChangeEvent< RateLimitedApis extends RateLimitedApiMap, -> = ControllerStateChangeEvent>; +> = ControllerStateChangeEvent< + typeof controllerName, + RateLimitState +>; export type RateLimitControllerGetStateAction< RateLimitedApis extends RateLimitedApiMap, -> = ControllerGetStateAction>; +> = ControllerGetStateAction< + typeof controllerName, + RateLimitState +>; export type RateLimitControllerCallApiAction< RateLimitedApis extends RateLimitedApiMap, > = { - type: `${typeof name}:call`; + type: `${typeof controllerName}:call`; handler: RateLimitController['call']; }; @@ -69,19 +77,17 @@ export type RateLimitControllerEvents< > = RateLimitControllerStateChangeEvent; export type RateLimitMessenger = - RestrictedMessenger< - typeof name, + Messenger< + typeof controllerName, RateLimitControllerActions, - RateLimitControllerEvents, - never, - never + RateLimitControllerEvents >; const metadata = { requests: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; @@ -92,7 +98,7 @@ const metadata = { export class RateLimitController< RateLimitedApis extends RateLimitedApiMap, > extends BaseController< - typeof name, + typeof controllerName, RateLimitState, RateLimitMessenger > { @@ -131,7 +137,7 @@ export class RateLimitController< >((acc, key) => ({ ...acc, [key]: {} }), {} as never), }; super({ - name, + name: controllerName, metadata, messenger, state: { ...defaultState, ...state }, @@ -140,8 +146,8 @@ export class RateLimitController< this.rateLimitTimeout = rateLimitTimeout; this.rateLimitCount = rateLimitCount; - this.messagingSystem.registerActionHandler( - `${name}:call`, + this.messenger.registerActionHandler( + `${controllerName}:call`, ( origin: string, type: keyof RateLimitedApis, @@ -156,6 +162,7 @@ export class RateLimitController< * @param origin - The requesting origin. * @param type - The type of API call to make. * @param args - Arguments for the API call. + * @returns The result of the API call. */ async call( origin: string, diff --git a/packages/remote-feature-flag-controller/tsconfig.build.json b/packages/remote-feature-flag-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 100644 --- a/packages/remote-feature-flag-controller/tsconfig.build.json +++ b/packages/remote-feature-flag-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/remote-feature-flag-controller/tsconfig.json b/packages/remote-feature-flag-controller/tsconfig.json index 831cc7b8670..68c3ddfc2cd 100644 --- a/packages/remote-feature-flag-controller/tsconfig.json +++ b/packages/remote-feature-flag-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../../packages/base-controller" }], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..88c4c3cfab1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4358,6 +4358,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" From 9e1042e9e39d2922b2e6e1e66954895721714c04 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 11:11:31 +0200 Subject: [PATCH 148/247] refactor: migrate PreferencesController to @metamask/messenger --- packages/preferences-controller/CHANGELOG.md | 5 ++ packages/preferences-controller/package.json | 3 +- .../src/PreferencesController.test.ts | 84 +++++++++++-------- .../src/PreferencesController.ts | 58 +++++++------ .../tsconfig.build.json | 3 +- packages/preferences-controller/tsconfig.json | 3 +- yarn.lock | 1 + 7 files changed, 88 insertions(+), 69 deletions(-) diff --git a/packages/preferences-controller/CHANGELOG.md b/packages/preferences-controller/CHANGELOG.md index 58869896ac9..20ec952d4ce 100644 --- a/packages/preferences-controller/CHANGELOG.md +++ b/packages/preferences-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6534](https://github.com/MetaMask/core/pull/6534)) + - Previously, `PreferencesController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [20.0.1] ### Changed diff --git a/packages/preferences-controller/package.json b/packages/preferences-controller/package.json index eac2b5b5c08..37104ba930a 100644 --- a/packages/preferences-controller/package.json +++ b/packages/preferences-controller/package.json @@ -48,7 +48,8 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", - "@metamask/controller-utils": "^11.14.0" + "@metamask/controller-utils": "^11.14.0", + "@metamask/messenger": "^0.3.0" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/preferences-controller/src/PreferencesController.test.ts b/packages/preferences-controller/src/PreferencesController.test.ts index 6847f2130dd..9bd22317048 100644 --- a/packages/preferences-controller/src/PreferencesController.test.ts +++ b/packages/preferences-controller/src/PreferencesController.test.ts @@ -1,13 +1,18 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { getDefaultKeyringState } from '@metamask/keyring-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { cloneDeep } from 'lodash'; import { ETHERSCAN_SUPPORTED_CHAIN_IDS } from './constants'; import type { - AllowedEvents, EtherscanSupportedHexChainId, - PreferencesControllerActions, - PreferencesControllerEvents, + PreferencesControllerMessenger, } from './PreferencesController'; import { PreferencesController } from './PreferencesController'; @@ -55,7 +60,7 @@ describe('PreferencesController', () => { describe('KeyringController:stateChange', () => { it('should update identities state to reflect new keyring accounts', () => { - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -103,7 +108,7 @@ describe('PreferencesController', () => { }); it('should update identities state to reflect removed keyring accounts', () => { - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -142,7 +147,7 @@ describe('PreferencesController', () => { }); it('should update selected address to first identity if the selected address was removed', () => { - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -184,7 +189,7 @@ describe('PreferencesController', () => { '0x01': { address: '0x01', importTime: 2, name: 'Account 2' }, '0x02': { address: '0x02', importTime: 3, name: 'Account 3' }, }; - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -222,7 +227,7 @@ describe('PreferencesController', () => { '0x01': { address: '0x01', importTime: 2, name: 'Account 2' }, '0x02': { address: '0x02', importTime: 3, name: 'Account 3' }, }; - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -260,7 +265,7 @@ describe('PreferencesController', () => { '0x01': { address: '0x01', importTime: 2, name: 'Account 2' }, '0x02': { address: '0x02', importTime: 3, name: 'Account 3' }, }; - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -306,7 +311,7 @@ describe('PreferencesController', () => { '0x01': { address: '0x01', importTime: 2, name: 'Account 2' }, '0x02': { address: '0x02', importTime: 3, name: 'Account 3' }, }; - const messenger = getMessenger(); + const messenger = getRootMessenger(); const controller = setupPreferencesController({ options: { state: { @@ -591,7 +596,7 @@ describe('PreferencesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -835,22 +840,27 @@ describe('PreferencesController', () => { }); }); +type AllPreferencesControllerActions = + MessengerActions; + +type AllPreferencesControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllPreferencesControllerActions, + AllPreferencesControllerEvents +>; + /** - * Construct a messenger for use in PreferencesController tests. - * - * This is a utility function that saves us from manually entering the correct - * type parameters for the Messenger each time we construct it. + * Creates and returns a root messenger for testing * - * @returns A messenger + * @returns A messenger instance */ -function getMessenger(): Messenger< - PreferencesControllerActions, - PreferencesControllerEvents | AllowedEvents -> { - return new Messenger< - PreferencesControllerActions, - PreferencesControllerEvents | AllowedEvents - >(); +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); } /** @@ -863,22 +873,24 @@ function getMessenger(): Messenger< */ function setupPreferencesController({ options = {}, - messenger = getMessenger(), + messenger = getRootMessenger(), }: { options?: Partial[0]>; - messenger?: Messenger< - PreferencesControllerActions, - PreferencesControllerEvents | AllowedEvents - >; + messenger?: RootMessenger; } = {}) { - const preferencesControllerMessenger = messenger.getRestricted< + const preferencesControllerMessenger = new Messenger< 'PreferencesController', - never, - AllowedEvents['type'] + AllPreferencesControllerActions, + AllPreferencesControllerEvents, + RootMessenger >({ - name: 'PreferencesController', - allowedActions: [], - allowedEvents: ['KeyringController:stateChange'], + namespace: 'PreferencesController', + parent: messenger, + }); + messenger.delegate({ + messenger: preferencesControllerMessenger, + actions: [], + events: ['KeyringController:stateChange'], }); return new PreferencesController({ messenger: preferencesControllerMessenger, diff --git a/packages/preferences-controller/src/PreferencesController.ts b/packages/preferences-controller/src/PreferencesController.ts index 578af2f32ab..b15d23036a3 100644 --- a/packages/preferences-controller/src/PreferencesController.ts +++ b/packages/preferences-controller/src/PreferencesController.ts @@ -2,13 +2,13 @@ import { BaseController, type ControllerStateChangeEvent, type ControllerGetStateAction, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { toChecksumHexAddress } from '@metamask/controller-utils'; import type { KeyringControllerState, KeyringControllerStateChangeEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import { ETHERSCAN_SUPPORTED_CHAIN_IDS } from './constants'; @@ -157,139 +157,139 @@ const metadata = { featureFlags: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, identities: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, ipfsGateway: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isIpfsGatewayEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isMultiAccountBalancesEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, lostIdentities: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, displayNftMedia: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, securityAlertsEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, selectedAddress: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, showTestNetworks: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, showIncomingTransactions: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, useNftDetection: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, useTokenDetection: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, smartTransactionsOptInStatus: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, useTransactionSimulations: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, showMultiRpcModal: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, useSafeChainsListValidation: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, tokenSortConfig: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, privacyMode: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, dismissSmartAccountSuggestionEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, smartAccountOptIn: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, smartAccountOptInForAccounts: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, tokenNetworkFilter: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -310,14 +310,12 @@ export type PreferencesControllerActions = PreferencesControllerGetStateAction; export type PreferencesControllerEvents = PreferencesControllerStateChangeEvent; -export type AllowedEvents = KeyringControllerStateChangeEvent; +type AllowedEvents = KeyringControllerStateChangeEvent; -export type PreferencesControllerMessenger = RestrictedMessenger< +export type PreferencesControllerMessenger = Messenger< typeof name, PreferencesControllerActions, - PreferencesControllerEvents | AllowedEvents, - never, - AllowedEvents['type'] + PreferencesControllerEvents | AllowedEvents >; /** diff --git a/packages/preferences-controller/tsconfig.build.json b/packages/preferences-controller/tsconfig.build.json index 6a7f4f10acf..a467f840c9b 100644 --- a/packages/preferences-controller/tsconfig.build.json +++ b/packages/preferences-controller/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, - { "path": "../keyring-controller/tsconfig.build.json" } + { "path": "../keyring-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/preferences-controller/tsconfig.json b/packages/preferences-controller/tsconfig.json index 0a81b60369b..0d78738b3cc 100644 --- a/packages/preferences-controller/tsconfig.json +++ b/packages/preferences-controller/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../controller-utils" }, - { "path": "../keyring-controller" } + { "path": "../keyring-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..43d8ac74811 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4275,6 +4275,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" "@metamask/keyring-controller": "npm:^23.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" From 05e48153dd3ea758cf47d53c266bda5d911e89ed Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 12:24:30 +0200 Subject: [PATCH 149/247] refactor: migrate {Authentication,UserStorage}Controller to @metamask/messenger --- packages/profile-sync-controller/CHANGELOG.md | 5 ++ packages/profile-sync-controller/package.json | 1 + .../AuthenticationController.test.ts | 67 +++++++++++++---- .../AuthenticationController.ts | 58 +++++++-------- .../user-storage/UserStorageController.ts | 72 +++++++++---------- .../__fixtures__/mockMessenger.ts | 51 ++++++++++--- .../tsconfig.build.json | 3 +- .../profile-sync-controller/tsconfig.json | 3 +- yarn.lock | 1 + 9 files changed, 164 insertions(+), 97 deletions(-) diff --git a/packages/profile-sync-controller/CHANGELOG.md b/packages/profile-sync-controller/CHANGELOG.md index 2ce826ff3a2..b675a2b3c86 100644 --- a/packages/profile-sync-controller/CHANGELOG.md +++ b/packages/profile-sync-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6533](https://github.com/MetaMask/core/pull/6533)) + - Previously, `AuthenticationController` and `UserStorageController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [25.1.0] ### Changed diff --git a/packages/profile-sync-controller/package.json b/packages/profile-sync-controller/package.json index 33d3dd634f8..6d147875d8e 100644 --- a/packages/profile-sync-controller/package.json +++ b/packages/profile-sync-controller/package.json @@ -101,6 +101,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.0", + "@metamask/messenger": "^0.3.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/utils": "^11.8.1", diff --git a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts index 50933b12378..ef7f1622431 100644 --- a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts +++ b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.test.ts @@ -1,9 +1,15 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import AuthenticationController from './AuthenticationController'; import type { - AllowedActions, - AllowedEvents, + AuthenticationControllerMessenger, AuthenticationControllerState, } from './AuthenticationController'; import { @@ -560,7 +566,7 @@ describe('metadata', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -726,23 +732,52 @@ describe('metadata', () => { }); }); +type AllAuthenticationControllerActions = + MessengerActions; + +type AllAuthenticationControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllAuthenticationControllerActions, + AllAuthenticationControllerEvents +>; + +/** + * Constructs the root messenger. + * + * @returns A root messenger. + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); +} + +const controllerName = 'AuthenticationController'; + /** * Jest Test Utility - create Auth Messenger * * @returns Auth Messenger */ function createAuthenticationMessenger() { - const baseMessenger = new Messenger(); - const messenger = baseMessenger.getRestricted({ - name: 'AuthenticationController', - allowedActions: [ - 'KeyringController:getState', - 'SnapController:handleRequest', - ], - allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'], + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< + typeof controllerName, + AllAuthenticationControllerActions, + AllAuthenticationControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: ['KeyringController:getState', 'SnapController:handleRequest'], + events: ['KeyringController:lock', 'KeyringController:unlock'], }); - return { messenger, baseMessenger }; + return { messenger, baseMessenger: rootMessenger }; } /** @@ -771,6 +806,12 @@ function createMockAuthenticationMessenger() { mockCall.mockImplementation((...args) => { const [actionType, params] = args; if (actionType === 'SnapController:handleRequest') { + if (typeof params === 'string') { + throw new Error( + `MOCK_FAIL - unsupported SnapController:handleRequest call: ${params}`, + ); + } + if (params?.request.method === 'getPublicKey') { return mockSnapGetPublicKey(); } diff --git a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts index 7e9a62443a8..f807a68a6e3 100644 --- a/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts +++ b/packages/profile-sync-controller/src/controllers/authentication/AuthenticationController.ts @@ -1,15 +1,15 @@ -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, - StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, + type StateMetadata, +} from '@metamask/base-controller/next'; import type { KeyringControllerGetStateAction, KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import type { Json } from '@metamask/utils'; @@ -46,7 +46,7 @@ const metadata: StateMetadata = { isSignedIn: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, srpSessionData: { @@ -74,7 +74,7 @@ const metadata: StateMetadata = { ); }, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -125,21 +125,15 @@ export type AuthenticationControllerStateChangeEvent = export type Events = AuthenticationControllerStateChangeEvent; // Allowed Actions -export type AllowedActions = - | HandleSnapRequest - | KeyringControllerGetStateAction; +type AllowedActions = HandleSnapRequest | KeyringControllerGetStateAction; -export type AllowedEvents = - | KeyringControllerLockEvent - | KeyringControllerUnlockEvent; +type AllowedEvents = KeyringControllerLockEvent | KeyringControllerUnlockEvent; // Messenger -export type AuthenticationControllerMessenger = RestrictedMessenger< +export type AuthenticationControllerMessenger = Messenger< typeof controllerName, Actions | AllowedActions, - Events | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + Events | AllowedEvents >; /** @@ -163,16 +157,14 @@ export default class AuthenticationController extends BaseController< readonly #keyringController = { setupLockedStateSubscriptions: () => { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { isUnlocked } = this.messenger.call('KeyringController:getState'); this.#isUnlocked = isUnlocked; - this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.messenger.subscribe('KeyringController:unlock', () => { this.#isUnlocked = true; }); - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.#isUnlocked = false; }); }, @@ -239,32 +231,32 @@ export default class AuthenticationController extends BaseController< * actions. */ #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'AuthenticationController:getBearerToken', this.getBearerToken.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'AuthenticationController:getSessionProfile', this.getSessionProfile.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'AuthenticationController:isSignedIn', this.isSignedIn.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'AuthenticationController:performSignIn', this.performSignIn.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'AuthenticationController:performSignOut', this.performSignOut.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'AuthenticationController:getUserProfileLineage', this.getUserProfileLineage.bind(this), ); @@ -388,7 +380,7 @@ export default class AuthenticationController extends BaseController< async #snapGetPublicKey(entropySourceId?: string): Promise { this.#assertIsUnlocked('#snapGetPublicKey'); - const result = (await this.messagingSystem.call( + const result = (await this.messenger.call( 'SnapController:handleRequest', createSnapPublicKeyRequest(entropySourceId), )) as string; @@ -404,7 +396,7 @@ export default class AuthenticationController extends BaseController< async #snapGetAllPublicKeys(): Promise<[string, string][]> { this.#assertIsUnlocked('#snapGetAllPublicKeys'); - const result = (await this.messagingSystem.call( + const result = (await this.messenger.call( 'SnapController:handleRequest', createSnapAllPublicKeysRequest(), )) as [string, string][]; @@ -434,7 +426,7 @@ export default class AuthenticationController extends BaseController< this.#assertIsUnlocked('#snapSignMessage'); - const result = (await this.messagingSystem.call( + const result = (await this.messenger.call( 'SnapController:handleRequest', createSnapSignMessageRequest(message, entropySourceId), )) as string; diff --git a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts index 7d231a4c5b8..972972e2dd4 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts @@ -6,13 +6,12 @@ import type { AddressBookControllerSetAction, AddressBookControllerDeleteAction, } from '@metamask/address-book-controller'; -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, - StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + type ControllerStateChangeEvent, + type StateMetadata, +} from '@metamask/base-controller/next'; import type { TraceCallback, TraceContext, @@ -24,6 +23,7 @@ import { type KeyringControllerLockEvent, type KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import { BACKUPANDSYNC_FEATURES } from './constants'; @@ -83,31 +83,31 @@ const metadata: StateMetadata = { isBackupAndSyncEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isBackupAndSyncUpdateLoading: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isAccountSyncingEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isContactSyncingEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isContactSyncingInProgress: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -209,12 +209,10 @@ export type AllowedEvents = | AddressBookControllerContactDeletedEvent; // Messenger -export type UserStorageControllerMessenger = RestrictedMessenger< +export type UserStorageControllerMessenger = Messenger< typeof controllerName, Actions | AllowedActions, - Events | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + Events | AllowedEvents >; /** @@ -234,17 +232,17 @@ export default class UserStorageController extends BaseController< readonly #auth = { getProfileId: async (entropySourceId?: string) => { - const sessionProfile = await this.messagingSystem.call( + const sessionProfile = await this.messenger.call( 'AuthenticationController:getSessionProfile', entropySourceId, ); return sessionProfile?.profileId; }, isSignedIn: () => { - return this.messagingSystem.call('AuthenticationController:isSignedIn'); + return this.messenger.call('AuthenticationController:isSignedIn'); }, signIn: async () => { - return await this.messagingSystem.call( + return await this.messenger.call( 'AuthenticationController:performSignIn', ); }, @@ -262,16 +260,14 @@ export default class UserStorageController extends BaseController< readonly #keyringController = { setupLockedStateSubscriptions: () => { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { isUnlocked } = this.messenger.call('KeyringController:getState'); this.#isUnlocked = isUnlocked; - this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.messenger.subscribe('KeyringController:unlock', () => { this.#isUnlocked = true; }); - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.#isUnlocked = false; }); }, @@ -322,12 +318,12 @@ export default class UserStorageController extends BaseController< env: this.#config.env, auth: { getAccessToken: (entropySourceId?: string) => - this.messagingSystem.call( + this.messenger.call( 'AuthenticationController:getBearerToken', entropySourceId, ), getUserProfile: async (entropySourceId?: string) => { - return await this.messagingSystem.call( + return await this.messenger.call( 'AuthenticationController:getSessionProfile', entropySourceId, ); @@ -357,7 +353,7 @@ export default class UserStorageController extends BaseController< // Contact Syncing setupContactSyncingSubscriptions({ getUserStorageControllerInstance: () => this, - getMessenger: () => this.messagingSystem, + getMessenger: () => this.messenger, trace: this.#trace, }); } @@ -367,37 +363,37 @@ export default class UserStorageController extends BaseController< * actions. */ #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:performGetStorage', this.performGetStorage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:performGetStorageAllFeatureEntries', this.performGetStorageAllFeatureEntries.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:performSetStorage', this.performSetStorage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:performBatchSetStorage', this.performBatchSetStorage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:performDeleteStorage', this.performDeleteStorage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:performBatchDeleteStorage', this.performBatchDeleteStorage.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'UserStorageController:getStorageKey', this.getStorageKey.bind(this), ); @@ -565,9 +561,7 @@ export default class UserStorageController extends BaseController< ); } - const { keyrings } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { keyrings } = this.messenger.call('KeyringController:getState'); return keyrings .filter((keyring) => keyring.type === KeyringTypes.hd.toString()) .map((keyring) => keyring.metadata.id); @@ -598,7 +592,7 @@ export default class UserStorageController extends BaseController< ); } - const result = (await this.messagingSystem.call( + const result = (await this.messenger.call( 'SnapController:handleRequest', createSnapSignMessageRequest(message, entropySourceId), )) as string; @@ -697,7 +691,7 @@ export default class UserStorageController extends BaseController< }; await syncContactsWithUserStorage(config, { - getMessenger: () => this.messagingSystem, + getMessenger: () => this.messenger, getUserStorageControllerInstance: () => this, trace: this.#trace, }); diff --git a/packages/profile-sync-controller/src/controllers/user-storage/__fixtures__/mockMessenger.ts b/packages/profile-sync-controller/src/controllers/user-storage/__fixtures__/mockMessenger.ts index 399f1dc6535..1856d732b20 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/__fixtures__/mockMessenger.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/__fixtures__/mockMessenger.ts @@ -1,5 +1,11 @@ -import type { NotNamespacedBy } from '@metamask/base-controller'; -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, + type NotNamespacedBy, +} from '@metamask/messenger'; import type { AllowedActions, @@ -9,6 +15,8 @@ import type { import { MOCK_LOGIN_RESPONSE } from '../../authentication/mocks'; import { MOCK_STORAGE_KEY_SIGNATURE } from '../mocks'; +const controllerName = 'UserStorageController'; + type GetHandler = Extract< AllowedActions, { type: ActionType } @@ -33,10 +41,22 @@ const typedMockFn = < ) => jest.fn, Parameters>(); type ExternalEvents = NotNamespacedBy< - 'UserStorageController', + typeof controllerName, AllowedEvents['type'] >; +type AllUserStorageControllerActions = + MessengerActions; + +type AllUserStorageControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllUserStorageControllerActions, + AllUserStorageControllerEvents +>; + /** * creates a custom user storage messenger, in case tests need different permissions * @@ -47,10 +67,21 @@ type ExternalEvents = NotNamespacedBy< export function createCustomUserStorageMessenger(props?: { overrideEvents?: ExternalEvents[]; }) { - const baseMessenger = new Messenger(); - const messenger = baseMessenger.getRestricted({ - name: 'UserStorageController', - allowedActions: [ + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); + const messenger = new Messenger< + typeof controllerName, + AllUserStorageControllerActions, + AllUserStorageControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: [ 'KeyringController:getState', 'SnapController:handleRequest', 'AuthenticationController:getBearerToken', @@ -58,7 +89,7 @@ export function createCustomUserStorageMessenger(props?: { 'AuthenticationController:isSignedIn', 'AuthenticationController:performSignIn', ], - allowedEvents: props?.overrideEvents ?? [ + events: props?.overrideEvents ?? [ 'KeyringController:lock', 'KeyringController:unlock', 'AddressBookController:contactUpdated', @@ -67,13 +98,13 @@ export function createCustomUserStorageMessenger(props?: { }); return { - baseMessenger, + baseMessenger: rootMessenger, messenger, }; } type OverrideMessengers = { - baseMessenger: Messenger; + baseMessenger: RootMessenger; messenger: UserStorageControllerMessenger; }; diff --git a/packages/profile-sync-controller/tsconfig.build.json b/packages/profile-sync-controller/tsconfig.build.json index ca9500d8729..df960063ed8 100644 --- a/packages/profile-sync-controller/tsconfig.build.json +++ b/packages/profile-sync-controller/tsconfig.build.json @@ -9,7 +9,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, - { "path": "../address-book-controller/tsconfig.build.json" } + { "path": "../address-book-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"], "exclude": [ diff --git a/packages/profile-sync-controller/tsconfig.json b/packages/profile-sync-controller/tsconfig.json index bbd45ba561c..e6966e7a7c7 100644 --- a/packages/profile-sync-controller/tsconfig.json +++ b/packages/profile-sync-controller/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../keyring-controller" }, - { "path": "../address-book-controller" } + { "path": "../address-book-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 4c490b218f9..81c5809da98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4301,6 +4301,7 @@ __metadata: "@metamask/keyring-api": "npm:^21.0.0" "@metamask/keyring-controller": "npm:^23.1.0" "@metamask/keyring-internal-api": "npm:^9.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" From 7ebeb14d404accc3e54e2b1b948d282b67ee3065 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 9 Sep 2025 18:39:47 +0200 Subject: [PATCH 150/247] refactor: migrate {Permission,SubjectMetadata}Controller to @metamask/messenger --- eslint-warning-thresholds.json | 3 - packages/permission-controller/CHANGELOG.md | 2 + packages/permission-controller/package.json | 1 + packages/permission-controller/src/Caveat.ts | 2 - .../permission-controller/src/Permission.ts | 35 +++-- .../src/PermissionController.test.ts | 122 ++++++++++-------- .../src/PermissionController.ts | 82 +++++------- .../src/SubjectMetadataController.test.ts | 71 +++++++--- .../src/SubjectMetadataController.ts | 18 ++- .../src/permission-middleware.ts | 4 - .../permission-controller/tsconfig.build.json | 3 +- packages/permission-controller/tsconfig.json | 3 +- yarn.lock | 1 + 13 files changed, 183 insertions(+), 164 deletions(-) diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index b09cda9dd5e..c64d30f4260 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -275,9 +275,6 @@ "import-x/no-named-as-default-member": 1, "promise/catch-or-return": 1 }, - "packages/permission-controller/src/Permission.ts": { - "prettier/prettier": 11 - }, "packages/permission-controller/src/PermissionController.test.ts": { "jest/no-conditional-in-test": 4 }, diff --git a/packages/permission-controller/CHANGELOG.md b/packages/permission-controller/CHANGELOG.md index 3b72f7e63ac..36694782dbc 100644 --- a/packages/permission-controller/CHANGELOG.md +++ b/packages/permission-controller/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6537](https://github.com/MetaMask/core/pull/6537)) + - Previously, `PermissionController` and `SubjectMetadataController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.1.0` to `^11.8.1` ([#5301](https://github.com/MetaMask/core/pull/5301), [#6054](https://github.com/MetaMask/core/pull/6054), [#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.0.0` to `^8.4.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/controller-utils` from `^11.5.0` to `^11.14.0` ([#5439](https://github.com/MetaMask/core/pull/5439), [#5583](https://github.com/MetaMask/core/pull/5583), [#5765](https://github.com/MetaMask/core/pull/5765), [#5812](https://github.com/MetaMask/core/pull/5812), [#5935](https://github.com/MetaMask/core/pull/5935), [#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303), [#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629)) diff --git a/packages/permission-controller/package.json b/packages/permission-controller/package.json index a9430efc0b1..38d9253514d 100644 --- a/packages/permission-controller/package.json +++ b/packages/permission-controller/package.json @@ -50,6 +50,7 @@ "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", "@metamask/json-rpc-engine": "^10.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/utils": "^11.8.1", "@types/deep-freeze-strict": "^1.1.0", diff --git a/packages/permission-controller/src/Caveat.ts b/packages/permission-controller/src/Caveat.ts index 63f455560bc..e1eea89e25a 100644 --- a/packages/permission-controller/src/Caveat.ts +++ b/packages/permission-controller/src/Caveat.ts @@ -12,8 +12,6 @@ import type { RestrictedMethodParameters, } from './Permission'; import { PermissionType } from './Permission'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { PermissionController } from './PermissionController'; export type CaveatConstraint = { /** diff --git a/packages/permission-controller/src/Permission.ts b/packages/permission-controller/src/Permission.ts index 809cc168dd0..75e31d783f1 100644 --- a/packages/permission-controller/src/Permission.ts +++ b/packages/permission-controller/src/Permission.ts @@ -1,16 +1,10 @@ -import type { - ActionConstraint, - EventConstraint, -} from '@metamask/base-controller'; import type { NonEmptyArray } from '@metamask/controller-utils'; +import type { ActionConstraint, EventConstraint } from '@metamask/messenger'; import type { Json } from '@metamask/utils'; import { nanoid } from 'nanoid'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { CaveatConstraint, Caveat } from './Caveat'; +import type { CaveatConstraint } from './Caveat'; import type { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - PermissionController, PermissionsRequest, SideEffectMessenger, } from './PermissionController'; @@ -117,8 +111,8 @@ export type ValidPermission< type ExtractArrayMembers = ArrayType extends [] ? never : ArrayType extends unknown[] | readonly unknown[] - ? ArrayType[number] - : never; + ? ArrayType[number] + : never; /** * A utility type for extracting the allowed caveat types for a particular @@ -317,7 +311,7 @@ export type SideEffectParams< Events extends EventConstraint, > = { requestData: PermissionsRequest; - messagingSystem: SideEffectMessenger; + messenger: SideEffectMessenger; }; /** @@ -538,11 +532,12 @@ export type PermissionSpecificationBuilderExportConstraint = { type ValidRestrictedMethodSpecification< Specification extends RestrictedMethodSpecificationConstraint, -> = Specification['methodImplementation'] extends ValidRestrictedMethod< - Specification['methodImplementation'] -> - ? Specification - : never; +> = + Specification['methodImplementation'] extends ValidRestrictedMethod< + Specification['methodImplementation'] + > + ? Specification + : never; /** * Constraint for {@link PermissionSpecificationConstraint} objects that @@ -556,10 +551,10 @@ export type ValidPermissionSpecification< ? Specification['permissionType'] extends PermissionType.Endowment ? Specification : Specification['permissionType'] extends PermissionType.RestrictedMethod - ? ValidRestrictedMethodSpecification< - Extract - > - : never + ? ValidRestrictedMethodSpecification< + Extract + > + : never : never; /** diff --git a/packages/permission-controller/src/PermissionController.test.ts b/packages/permission-controller/src/PermissionController.test.ts index a3d65116bd5..631bc4d5600 100644 --- a/packages/permission-controller/src/PermissionController.test.ts +++ b/packages/permission-controller/src/PermissionController.test.ts @@ -1,12 +1,13 @@ -import type { - AcceptRequest as AcceptApprovalRequest, - AddApprovalRequest, - HasApprovalRequest, - RejectRequest as RejectApprovalRequest, -} from '@metamask/approval-controller'; -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { isPlainObject } from '@metamask/controller-utils'; import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Json, JsonRpcRequest } from '@metamask/utils'; import { assertIsJsonRpcFailure, @@ -22,8 +23,7 @@ import type { CaveatMutator, ExtractSpecifications, PermissionConstraint, - PermissionControllerActions, - PermissionControllerEvents, + PermissionControllerMessenger, PermissionOptions, PermissionsRequest, RestrictedMethodOptions, @@ -40,7 +40,6 @@ import { import * as errors from './errors'; import type { EndowmentGetterParams } from './Permission'; import { SubjectType } from './SubjectMetadataController'; -import type { GetSubjectMetadata } from './SubjectMetadataController'; // Caveat types and specifications @@ -546,13 +545,6 @@ type DefaultPermissionSpecifications = ExtractSpecifications< const controllerName = 'PermissionController' as const; -type AllowedActions = - | HasApprovalRequest - | AddApprovalRequest - | AcceptApprovalRequest - | RejectApprovalRequest - | GetSubjectMetadata; - /** * Params for `ApprovalController:addRequest` of type `wallet_requestPermissions`. */ @@ -565,41 +557,59 @@ type AddPermissionRequestParams = { type AddPermissionRequestArgs = [string, AddPermissionRequestParams]; +type AllPermissionControllerActions = + MessengerActions; + +type AllPermissionControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllPermissionControllerActions, + AllPermissionControllerEvents +>; + /** - * Gets an unrestricted messenger. Used for tests. + * Creates and returns a root messenger for testing * - * @returns The unrestricted messenger. + * @returns A messenger instance */ -function getUnrestrictedMessenger() { - return new Messenger< - PermissionControllerActions | AllowedActions, - PermissionControllerEvents - >(); +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); } /** - * Gets a restricted messenger. + * Gets a messenger for the permission controller. * Used as a default in {@link getPermissionControllerOptions}. * - * @param messenger - Optional parameter to pass in a messenger - * @returns The restricted messenger. + * @param rootMessenger - Optional parameter to pass in a root messenger + * @returns The messenger for the permission controller. */ function getPermissionControllerMessenger( - messenger = getUnrestrictedMessenger(), -) { - return messenger.getRestricted( - { - name: controllerName, - allowedActions: [ - 'ApprovalController:hasRequest', - 'ApprovalController:addRequest', - 'ApprovalController:acceptRequest', - 'ApprovalController:rejectRequest', - 'SubjectMetadataController:getSubjectMetadata', - ], - allowedEvents: [], - }, - ); + rootMessenger = getRootMessenger(), +): PermissionControllerMessenger { + const messenger = new Messenger< + typeof controllerName, + AllPermissionControllerActions, + AllPermissionControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: [ + 'ApprovalController:hasRequest', + 'ApprovalController:addRequest', + 'ApprovalController:acceptRequest', + 'ApprovalController:rejectRequest', + 'SubjectMetadataController:getSubjectMetadata', + ], + messenger, + }); + return messenger; } /** @@ -5572,7 +5582,7 @@ describe('PermissionController', () => { describe('controller actions', () => { it('action: PermissionController:clearPermissions', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5597,7 +5607,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:getEndowments', async () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5670,7 +5680,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:getSubjectNames', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5698,7 +5708,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:hasPermission', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5760,7 +5770,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:hasPermissions', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5790,7 +5800,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:getPermissions', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5824,7 +5834,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:revokeAllPermissions', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5858,7 +5868,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:revokePermissionForAllSubjects', () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5898,7 +5908,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:grantPermissions', async () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5919,7 +5929,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:grantPermissionsIncremental', async () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5943,7 +5953,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:requestPermissions', async () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5970,7 +5980,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:requestPermissionsIncremental', async () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const options = getPermissionControllerOptions({ messenger: getPermissionControllerMessenger(messenger), }); @@ -5997,7 +6007,7 @@ describe('PermissionController', () => { }); it('action: PermissionController:updateCaveat', async () => { - const messenger = getUnrestrictedMessenger(); + const messenger = getRootMessenger(); const state = { subjects: { 'metamask.io': { @@ -6295,7 +6305,7 @@ describe('PermissionController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/permission-controller/src/PermissionController.ts b/packages/permission-controller/src/PermissionController.ts index 78cd5584fd7..0e587ad8e27 100644 --- a/packages/permission-controller/src/PermissionController.ts +++ b/packages/permission-controller/src/PermissionController.ts @@ -1,4 +1,3 @@ -/* eslint-enable @typescript-eslint/no-unused-vars */ import type { AcceptRequest as AcceptApprovalRequest, AddApprovalRequest, @@ -7,19 +6,21 @@ import type { } from '@metamask/approval-controller'; import type { StateMetadata, - RestrictedMessenger, - ActionConstraint, - EventConstraint, ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import type { NonEmptyArray } from '@metamask/controller-utils'; import { isNonEmptyArray, isPlainObject, isValidJson, } from '@metamask/controller-utils'; +import type { + Messenger, + ActionConstraint, + EventConstraint, +} from '@metamask/messenger'; import { JsonRpcError } from '@metamask/rpc-errors'; import { hasProperty } from '@metamask/utils'; import type { Json, Mutable } from '@metamask/utils'; @@ -222,7 +223,7 @@ function getStateMetadata() { return { subjects: { includeInStateLogs: true, - anonymous: true, + includeInDebugSnapshot: true, persist: true, usedInUi: true, }, @@ -280,7 +281,7 @@ export type HasPermission = { }; /** - * Directly grants given permissions for a specificed origin without requesting user approval + * Directly grants given permissions for a specified origin without requesting user approval */ export type GrantPermissions = { type: `${typeof controllerName}:grantPermissions`; @@ -288,7 +289,7 @@ export type GrantPermissions = { }; /** - * Directly grants given permissions for a specificed origin without requesting user approval + * Directly grants given permissions for a specified origin without requesting user approval */ export type GrantPermissionsIncremental = { type: `${typeof controllerName}:grantPermissionsIncremental`; @@ -411,24 +412,16 @@ type AllowedActions = /** * The messenger of the {@link PermissionController}. */ -export type PermissionControllerMessenger = RestrictedMessenger< +export type PermissionControllerMessenger = Messenger< typeof controllerName, PermissionControllerActions | AllowedActions, - PermissionControllerEvents, - AllowedActions['type'], - never + PermissionControllerEvents >; export type SideEffectMessenger< Actions extends ActionConstraint, Events extends EventConstraint, -> = RestrictedMessenger< - typeof controllerName, - Actions | AllowedActions, - Events, - AllowedActions['type'] | Actions['type'], - Events['type'] ->; +> = Messenger; /** * A generic {@link PermissionController}. @@ -810,70 +803,69 @@ export class PermissionController< } /** - * Constructor helper for registering the controller's messaging system - * actions. + * Constructor helper for registering the controller's messenger actions. */ private registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:clearPermissions` as const, () => this.clearState(), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getEndowments` as const, (origin: string, targetName: string, requestData?: unknown) => this.getEndowments(origin, targetName, requestData), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getSubjectNames` as const, () => this.getSubjectNames(), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getPermissions` as const, (origin: OriginString) => this.getPermissions(origin), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:hasPermission` as const, (origin: OriginString, targetName: string) => this.hasPermission(origin, targetName), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:hasPermissions` as const, (origin: OriginString) => this.hasPermissions(origin), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:grantPermissions` as const, this.grantPermissions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:grantPermissionsIncremental` as const, this.grantPermissionsIncremental.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:requestPermissions` as const, (subject: PermissionSubjectMetadata, permissions: RequestedPermissions) => this.requestPermissions(subject, permissions), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:requestPermissionsIncremental` as const, (subject: PermissionSubjectMetadata, permissions: RequestedPermissions) => this.requestPermissionsIncremental(subject, permissions), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:revokeAllPermissions` as const, (origin: OriginString) => this.revokeAllPermissions(origin), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:revokePermissionForAllSubjects` as const, ( target: ExtractPermission< @@ -883,12 +875,12 @@ export class PermissionController< ) => this.revokePermissionForAllSubjects(target), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:revokePermissions` as const, this.revokePermissions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateCaveat` as const, (origin, target, caveatType, caveatValue) => { this.updateCaveat( @@ -1869,7 +1861,7 @@ export class PermissionController< specification.subjectTypes?.length && specification.subjectTypes.length > 0 ) { - const metadata = this.messagingSystem.call( + const metadata = this.messenger.call( 'SubjectMetadataController:getSubjectMetadata', origin, ); @@ -2444,7 +2436,7 @@ export class PermissionController< */ private async requestUserApproval(permissionsRequest: PermissionsRequest) { const { origin, id } = permissionsRequest.metadata; - const approvedRequest = await this.messagingSystem.call( + const approvedRequest = await this.messenger.call( 'ApprovalController:addRequest', { id, @@ -2543,7 +2535,7 @@ export class PermissionController< /** * Executes the side-effects of the approved permissions while handling the errors if any. - * It will pass an instance of the {@link messagingSystem} and the request data associated with the permission request to the handlers through its params. + * It will pass an instance of the {@link messenger} and the request data associated with the permission request to the handlers through its params. * * @param sideEffects - the side-effect record created by {@link getSideEffects} * @param requestData - the permissions requestData. @@ -2556,7 +2548,7 @@ export class PermissionController< const { permittedHandlers, failureHandlers } = sideEffects; const params = { requestData, - messagingSystem: this.messagingSystem, + messenger: this.messenger, }; const promiseResults = await Promise.allSettled( @@ -2689,7 +2681,7 @@ export class PermissionController< } try { - await this.messagingSystem.call( + await this.messenger.call( 'ApprovalController:acceptRequest', id, request, @@ -2727,7 +2719,7 @@ export class PermissionController< * @returns Whether the specified request exists. */ private hasApprovalRequest(options: { id: string }): boolean { - return this.messagingSystem.call('ApprovalController:hasRequest', options); + return this.messenger.call('ApprovalController:hasRequest', options); } /** @@ -2742,11 +2734,7 @@ export class PermissionController< * @returns Nothing */ private _rejectPermissionsRequest(id: string, error: unknown): void { - return this.messagingSystem.call( - 'ApprovalController:rejectRequest', - id, - error, - ); + return this.messenger.call('ApprovalController:rejectRequest', id, error); } /** diff --git a/packages/permission-controller/src/SubjectMetadataController.test.ts b/packages/permission-controller/src/SubjectMetadataController.test.ts index 32697dff728..67c663f9c73 100644 --- a/packages/permission-controller/src/SubjectMetadataController.test.ts +++ b/packages/permission-controller/src/SubjectMetadataController.test.ts @@ -1,11 +1,14 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Json } from '@metamask/utils'; -import type { HasPermissions } from './PermissionController'; -import type { - SubjectMetadataControllerActions, - SubjectMetadataControllerEvents, -} from './SubjectMetadataController'; +import type { SubjectMetadataControllerMessenger } from './SubjectMetadataController'; import { SubjectMetadataController, SubjectType, @@ -13,31 +16,59 @@ import { const controllerName = 'SubjectMetadataController'; +type AllSubjectMetadataControllerActions = + MessengerActions; + +type AllSubjectMetadataControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllSubjectMetadataControllerActions, + AllSubjectMetadataControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Utility function for creating a messenger. * * @returns A tuple containing the messenger and a spy for the "hasPermission" action handler */ function getSubjectMetadataControllerMessenger() { - const messenger = new Messenger< - SubjectMetadataControllerActions | HasPermissions, - SubjectMetadataControllerEvents - >(); + const rootMessenger = getRootMessenger(); const hasPermissionsSpy = jest.fn(); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'PermissionController:hasPermissions', hasPermissionsSpy, ); - return [ - messenger.getRestricted({ - name: controllerName, - allowedActions: ['PermissionController:hasPermissions'], - allowedEvents: [], - }), - hasPermissionsSpy, - ] as const; + const messenger = new Messenger< + typeof controllerName, + AllSubjectMetadataControllerActions, + AllSubjectMetadataControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + + rootMessenger.delegate({ + actions: ['PermissionController:hasPermissions'], + messenger, + }); + + return [messenger, hasPermissionsSpy] as const; } /** @@ -351,7 +382,7 @@ describe('SubjectMetadataController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/permission-controller/src/SubjectMetadataController.ts b/packages/permission-controller/src/SubjectMetadataController.ts index 032165f87ca..02b2504fdda 100644 --- a/packages/permission-controller/src/SubjectMetadataController.ts +++ b/packages/permission-controller/src/SubjectMetadataController.ts @@ -1,9 +1,9 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { Json } from '@metamask/utils'; import type { @@ -51,7 +51,7 @@ const stateMetadata = { subjectMetadata: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -89,12 +89,10 @@ export type SubjectMetadataControllerEvents = SubjectMetadataStateChange; type AllowedActions = HasPermissions; -export type SubjectMetadataControllerMessenger = RestrictedMessenger< +export type SubjectMetadataControllerMessenger = Messenger< typeof controllerName, SubjectMetadataControllerActions | AllowedActions, - SubjectMetadataControllerEvents, - AllowedActions['type'], - never + SubjectMetadataControllerEvents >; type SubjectMetadataControllerOptions = { @@ -146,14 +144,14 @@ export class SubjectMetadataController extends BaseController< this.subjectCacheLimit = subjectCacheLimit; this.subjectsWithoutPermissionsEncounteredSinceStartup = new Set(); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // ESLint is confused by the string literal type. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:getSubjectMetadata`, this.getSubjectMetadata.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( // ESLint is confused by the string literal type. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${this.name}:addSubjectMetadata`, diff --git a/packages/permission-controller/src/permission-middleware.ts b/packages/permission-controller/src/permission-middleware.ts index e661464211c..dd5df346cc8 100644 --- a/packages/permission-controller/src/permission-middleware.ts +++ b/packages/permission-controller/src/permission-middleware.ts @@ -1,7 +1,5 @@ import { createAsyncMiddleware } from '@metamask/json-rpc-engine'; import type { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - JsonRpcEngine, JsonRpcMiddleware, AsyncJsonRpcEngineNextCallback, } from '@metamask/json-rpc-engine'; @@ -17,8 +15,6 @@ import type { RestrictedMethodParameters, } from '.'; import { internalError } from './errors'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { PermissionController } from './PermissionController'; type PermissionMiddlewareFactoryOptions = { executeRestrictedMethod: GenericPermissionController['_executeRestrictedMethod']; diff --git a/packages/permission-controller/tsconfig.build.json b/packages/permission-controller/tsconfig.build.json index 5034efd3f1a..586d1ddeb4c 100644 --- a/packages/permission-controller/tsconfig.build.json +++ b/packages/permission-controller/tsconfig.build.json @@ -9,7 +9,8 @@ { "path": "../approval-controller/tsconfig.build.json" }, { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, - { "path": "../json-rpc-engine/tsconfig.build.json" } + { "path": "../json-rpc-engine/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/permission-controller/tsconfig.json b/packages/permission-controller/tsconfig.json index fadbdc2be0f..1f4f13deae5 100644 --- a/packages/permission-controller/tsconfig.json +++ b/packages/permission-controller/tsconfig.json @@ -7,7 +7,8 @@ { "path": "../approval-controller" }, { "path": "../base-controller" }, { "path": "../controller-utils" }, - { "path": "../json-rpc-engine" } + { "path": "../json-rpc-engine" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index c810bdf6c73..028c78aff0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4151,6 +4151,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" "@metamask/json-rpc-engine": "npm:^10.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.8.1" "@types/deep-freeze-strict": "npm:^1.1.0" From b36974ef9f4c99056e82f47127053975c0389e32 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 12:50:12 +0200 Subject: [PATCH 151/247] refactor: migrate PermissionLogController to @metamask/messenger --- .../permission-log-controller/CHANGELOG.md | 3 ++ .../permission-log-controller/package.json | 1 + .../src/PermissionLogController.ts | 32 ++++++++---- .../permission-log-controller/src/index.ts | 18 ++++++- .../tests/PermissionLogController.test.ts | 50 ++++++++++++++++--- .../tsconfig.build.json | 3 +- .../permission-log-controller/tsconfig.json | 3 +- yarn.lock | 1 + 8 files changed, 92 insertions(+), 19 deletions(-) diff --git a/packages/permission-log-controller/CHANGELOG.md b/packages/permission-log-controller/CHANGELOG.md index 66e829b3cff..67b0f38f8a6 100644 --- a/packages/permission-log-controller/CHANGELOG.md +++ b/packages/permission-log-controller/CHANGELOG.md @@ -10,9 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add two new controller state metadata properties: `includeInStateLogs` and `usedInUi` ([#6525](https://github.com/MetaMask/core/pull/6525)) +- Export new Messenger action/event types: `PermissionLogControllerActions`, `PermissionLogControllerGetStateAction`, `PermissionLogControllerEvents`, and `PermissionLogControllerStateChangeEvent` ([#6536](https://github.com/MetaMask/core/pull/6536)) ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6536](https://github.com/MetaMask/core/pull/6536)) + - Previously, `PermissionLogController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.4.2` to `^11.8.1` ([#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.0` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/json-rpc-engine` from `^10.0.3` to `^10.1.0` ([#6678](https://github.com/MetaMask/core/pull/6678)) diff --git a/packages/permission-log-controller/package.json b/packages/permission-log-controller/package.json index 9bc3d8c66dd..17bcf6f05ca 100644 --- a/packages/permission-log-controller/package.json +++ b/packages/permission-log-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.0", "@metamask/json-rpc-engine": "^10.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/permission-log-controller/src/PermissionLogController.ts b/packages/permission-log-controller/src/PermissionLogController.ts index ad824a8eee6..b9fc78708d2 100644 --- a/packages/permission-log-controller/src/PermissionLogController.ts +++ b/packages/permission-log-controller/src/PermissionLogController.ts @@ -1,8 +1,10 @@ import { BaseController, - type RestrictedMessenger, -} from '@metamask/base-controller'; + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; +import type { Messenger } from '@metamask/messenger'; import { type Json, type JsonRpcRequest, @@ -70,12 +72,24 @@ export type PermissionLogControllerOptions = { messenger: PermissionLogControllerMessenger; }; -export type PermissionLogControllerMessenger = RestrictedMessenger< +export type PermissionLogControllerGetStateAction = ControllerGetStateAction< typeof name, - never, - never, - never, - never + PermissionLogControllerState +>; + +export type PermissionLogControllerActions = + PermissionLogControllerGetStateAction; + +export type PermissionLogControllerStateChangeEvent = + ControllerStateChangeEvent; + +export type PermissionLogControllerEvents = + PermissionLogControllerStateChangeEvent; + +export type PermissionLogControllerMessenger = Messenger< + typeof name, + PermissionLogControllerActions, + PermissionLogControllerEvents >; const defaultState: PermissionLogControllerState = { @@ -108,13 +122,13 @@ export class PermissionLogController extends BaseController< permissionHistory: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, permissionActivityLog: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }, diff --git a/packages/permission-log-controller/src/index.ts b/packages/permission-log-controller/src/index.ts index 63cef4301a4..297218d2cc1 100644 --- a/packages/permission-log-controller/src/index.ts +++ b/packages/permission-log-controller/src/index.ts @@ -1 +1,17 @@ -export * from './PermissionLogController'; +export { PermissionLogController } from './PermissionLogController'; +export type { + JsonRpcRequestWithOrigin, + Caveat, + Permission, + PermissionActivityLog, + PermissionLog, + PermissionEntry, + PermissionHistory, + PermissionLogControllerActions, + PermissionLogControllerGetStateAction, + PermissionLogControllerStateChangeEvent, + PermissionLogControllerEvents, + PermissionLogControllerMessenger, + PermissionLogControllerState, + PermissionLogControllerOptions, +} from './PermissionLogController'; diff --git a/packages/permission-log-controller/tests/PermissionLogController.test.ts b/packages/permission-log-controller/tests/PermissionLogController.test.ts index 97b5df6038b..372e3aecfb7 100644 --- a/packages/permission-log-controller/tests/PermissionLogController.test.ts +++ b/packages/permission-log-controller/tests/PermissionLogController.test.ts @@ -1,8 +1,15 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { JsonRpcEngineReturnHandler, JsonRpcEngineNextCallback, } from '@metamask/json-rpc-engine'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { type PendingJsonRpcResponse, type JsonRpcRequest, @@ -13,9 +20,10 @@ import { nanoid } from 'nanoid'; import { constants, getters, noop } from './helpers'; import { LOG_LIMIT, LOG_METHOD_TYPES } from '../src/enums'; import { + PermissionLogController, type Permission, type PermissionLogControllerState, - PermissionLogController, + type PermissionLogControllerMessenger, } from '../src/PermissionLogController'; const { PERMS, RPC_REQUESTS } = getters; @@ -33,6 +41,29 @@ class CustomError extends Error { const name = 'PermissionLogController'; +type AllPermissionLogControllerActions = + MessengerActions; + +type AllPermissionLogControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllPermissionLogControllerActions, + AllPermissionLogControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + const initController = ({ restrictedMethods, state, @@ -40,10 +71,15 @@ const initController = ({ restrictedMethods: Set; state?: Partial; }): PermissionLogController => { - const messenger = new Messenger().getRestricted({ - name, - allowedActions: [], - allowedEvents: [], + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< + typeof name, + AllPermissionLogControllerActions, + AllPermissionLogControllerEvents, + RootMessenger + >({ + namespace: name, + parent: rootMessenger, }); return new PermissionLogController({ messenger, @@ -824,7 +860,7 @@ describe('PermissionLogController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/permission-log-controller/tsconfig.build.json b/packages/permission-log-controller/tsconfig.build.json index 5c05375f6fe..eb701f1cf26 100644 --- a/packages/permission-log-controller/tsconfig.build.json +++ b/packages/permission-log-controller/tsconfig.build.json @@ -7,7 +7,8 @@ }, "references": [ { "path": "../base-controller/tsconfig.build.json" }, - { "path": "../json-rpc-engine/tsconfig.build.json" } + { "path": "../json-rpc-engine/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/permission-log-controller/tsconfig.json b/packages/permission-log-controller/tsconfig.json index 469f0da5438..79ee5fbbc9a 100644 --- a/packages/permission-log-controller/tsconfig.json +++ b/packages/permission-log-controller/tsconfig.json @@ -5,7 +5,8 @@ }, "references": [ { "path": "../base-controller" }, - { "path": "../json-rpc-engine" } + { "path": "../json-rpc-engine" }, + { "path": "../messenger" } ], "include": ["../../types", "./src", "./tests"] } diff --git a/yarn.lock b/yarn.lock index c810bdf6c73..a6f6fffd093 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4176,6 +4176,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" "@metamask/json-rpc-engine": "npm:^10.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/deep-freeze-strict": "npm:^1.1.0" "@types/jest": "npm:^27.4.1" From 1df462289c23f2fd68df4b1e73e485ad6b9faf79 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 13:27:35 +0200 Subject: [PATCH 152/247] refactor: migrate {NotificationServices,NotificationServicesPush}Controller to @metamask/messenger --- .../CHANGELOG.md | 9 ++ .../package.json | 1 + .../NotificationServicesController.test.ts | 58 +++++++++++-- .../NotificationServicesController.ts | 82 +++++++++---------- ...NotificationServicesPushController.test.ts | 4 +- .../NotificationServicesPushController.ts | 32 ++++---- .../__fixtures__/mockMessenger.ts | 62 +++++++++++--- .../tsconfig.build.json | 3 + .../tsconfig.json | 3 + yarn.lock | 1 + 10 files changed, 169 insertions(+), 86 deletions(-) diff --git a/packages/notification-services-controller/CHANGELOG.md b/packages/notification-services-controller/CHANGELOG.md index a4c9403a961..b33aba7bab4 100644 --- a/packages/notification-services-controller/CHANGELOG.md +++ b/packages/notification-services-controller/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6538](https://github.com/MetaMask/core/pull/6538)) + - Previously, `NotificationServicesController` and `NotificationServicesPushController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + +### Removed + +- **BREAKING:** Remove package-level exports of `AllowedActions` and `AllowedEvents` from `NotificationServicesController` and `NotificationServicesPushController` ([#6538](https://github.com/MetaMask/core/pull/6538)) + ## [18.2.0] ### Added diff --git a/packages/notification-services-controller/package.json b/packages/notification-services-controller/package.json index 2e8fe9e2d62..dd06a27aae3 100644 --- a/packages/notification-services-controller/package.json +++ b/packages/notification-services-controller/package.json @@ -112,6 +112,7 @@ "@contentful/rich-text-html-renderer": "^16.5.2", "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "bignumber.js": "^9.1.2", "firebase": "^11.2.0", diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts index 33a39ed0475..3f3414f1860 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.test.ts @@ -1,10 +1,17 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import * as ControllerUtils from '@metamask/controller-utils'; import { KeyringTypes, type KeyringControllerGetStateAction, type KeyringControllerState, } from '@metamask/keyring-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { AuthenticationController } from '@metamask/profile-sync-controller'; import log from 'loglevel'; import type nock from 'nock'; @@ -30,8 +37,6 @@ import NotificationServicesController, { defaultState, } from './NotificationServicesController'; import type { - AllowedActions, - AllowedEvents, NotificationServicesControllerMessenger, NotificationServicesControllerState, } from './NotificationServicesController'; @@ -1262,7 +1267,7 @@ describe('NotificationServicesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -1360,17 +1365,52 @@ type AnyFunc = (...args: any[]) => any; const typedMockAction = () => jest.fn, Parameters>(); +const controllerName = 'NotificationServicesController'; + +type AllNotificationServicesControllerActions = + MessengerActions; + +type AllNotificationServicesControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllNotificationServicesControllerActions, + AllNotificationServicesControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Jest Mock Utility - Mock Notification Messenger * * @returns mock notification messenger and other messenger mocks */ function mockNotificationMessenger() { - const globalMessenger = new Messenger(); + const globalMessenger = getRootMessenger(); + + const messenger = new Messenger< + typeof controllerName, + AllNotificationServicesControllerActions, + AllNotificationServicesControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: globalMessenger, + }); - const messenger = globalMessenger.getRestricted({ - name: 'NotificationServicesController', - allowedActions: [ + globalMessenger.delegate({ + messenger, + actions: [ 'KeyringController:getState', 'AuthenticationController:getBearerToken', 'AuthenticationController:isSignedIn', @@ -1379,7 +1419,7 @@ function mockNotificationMessenger() { 'NotificationServicesPushController:enablePushNotifications', 'NotificationServicesPushController:subscribeToPushNotifications', ], - allowedEvents: [ + events: [ 'KeyringController:stateChange', 'KeyringController:lock', 'KeyringController:unlock', diff --git a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts index bb1e0a77085..f0dcf46d799 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/NotificationServicesController.ts @@ -1,10 +1,9 @@ import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { isValidHexAddress, toChecksumHexAddress, @@ -17,6 +16,7 @@ import { KeyringTypes, type KeyringControllerState, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; import { assert } from '@metamask/utils'; import log from 'loglevel'; @@ -51,7 +51,7 @@ const controllerName = 'NotificationServicesController'; */ export type NotificationServicesControllerState = { /** - * We store and manage accounts that have been seen/visted through the + * We store and manage accounts that have been seen/visited through the * account subscription. This allows us to track and add notifications for new accounts and not previous accounts added. */ subscriptionAccountsSeen: string[]; @@ -104,62 +104,62 @@ const metadata: StateMetadata = { subscriptionAccountsSeen: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isMetamaskNotificationsFeatureSeen: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isNotificationServicesEnabled: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isFeatureAnnouncementsEnabled: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, metamaskNotificationsList: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, metamaskNotificationsReadList: { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isUpdatingMetamaskNotifications: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isFetchingMetamaskNotifications: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isUpdatingMetamaskNotificationsAccount: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, isCheckingAccountsPresence: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -215,7 +215,7 @@ export type Actions = | NotificationServicesControllerDeleteNotificationsById; // Allowed Actions -export type AllowedActions = +type AllowedActions = // Keyring Controller Requests | KeyringControllerGetStateAction // Auth Controller Requests @@ -251,7 +251,7 @@ export type Events = | MarkNotificationsAsReadEvent; // Allowed Events -export type AllowedEvents = +type AllowedEvents = // Keyring Events | KeyringControllerStateChangeEvent | KeyringControllerLockEvent @@ -261,12 +261,10 @@ export type AllowedEvents = | NotificationServicesPushControllerStateChangeEvent; // Type for the messenger of NotificationServicesController -export type NotificationServicesControllerMessenger = RestrictedMessenger< +export type NotificationServicesControllerMessenger = Messenger< typeof controllerName, Actions | AllowedActions, - Events | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + Events | AllowedEvents >; type FeatureAnnouncementEnv = { @@ -288,12 +286,10 @@ export default class NotificationServicesController extends BaseController< isUnlocked: false, setupLockedStateSubscriptions: (onUnlock: () => Promise) => { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { isUnlocked } = this.messenger.call('KeyringController:getState'); this.#keyringController.isUnlocked = isUnlocked; - this.messagingSystem.subscribe('KeyringController:unlock', () => { + this.messenger.subscribe('KeyringController:unlock', () => { this.#keyringController.isUnlocked = true; // messaging system cannot await promises // we don't need to wait for a result on this. @@ -301,7 +297,7 @@ export default class NotificationServicesController extends BaseController< onUnlock(); }); - this.messagingSystem.subscribe('KeyringController:lock', () => { + this.messenger.subscribe('KeyringController:lock', () => { this.#keyringController.isUnlocked = false; }); }, @@ -309,15 +305,15 @@ export default class NotificationServicesController extends BaseController< readonly #auth = { getBearerToken: async () => { - return await this.messagingSystem.call( + return await this.messenger.call( 'AuthenticationController:getBearerToken', ); }, isSignedIn: () => { - return this.messagingSystem.call('AuthenticationController:isSignedIn'); + return this.messenger.call('AuthenticationController:isSignedIn'); }, signIn: async () => { - return await this.messagingSystem.call( + return await this.messenger.call( 'AuthenticationController:performSignIn', ); }, @@ -330,13 +326,13 @@ export default class NotificationServicesController extends BaseController< isSetup: false, subscribeToPushNotifications: async () => { - await this.messagingSystem.call( + await this.messenger.call( 'NotificationServicesPushController:subscribeToPushNotifications', ); }, enablePushNotifications: async (addresses: string[]) => { try { - await this.messagingSystem.call( + await this.messenger.call( 'NotificationServicesPushController:enablePushNotifications', addresses, ); @@ -346,7 +342,7 @@ export default class NotificationServicesController extends BaseController< }, disablePushNotifications: async () => { try { - await this.messagingSystem.call( + await this.messenger.call( 'NotificationServicesPushController:disablePushNotifications', ); } catch (e) { @@ -354,7 +350,7 @@ export default class NotificationServicesController extends BaseController< } }, subscribe: () => { - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NotificationServicesPushController:onNewNotifications', (notification) => { // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -393,9 +389,7 @@ export default class NotificationServicesController extends BaseController< isNotificationAccountsSetup: false, getNotificationAccounts: () => { - const { keyrings } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { keyrings } = this.messenger.call('KeyringController:getState'); const firstHDKeyring = keyrings.find( (k) => k.type === KeyringTypes.hd.toString(), ); @@ -472,7 +466,7 @@ export default class NotificationServicesController extends BaseController< * And call effects to subscribe/unsubscribe to notifications. */ subscribe: () => { - this.messagingSystem.subscribe( + this.messenger.subscribe( 'KeyringController:stateChange', async (totalAccounts, prevTotalAccounts) => { @@ -556,22 +550,22 @@ export default class NotificationServicesController extends BaseController< } #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateMetamaskNotificationsList`, this.updateMetamaskNotificationsList.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:disableNotificationServices`, this.disableNotificationServices.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getNotificationsByType`, this.getNotificationsByType.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:deleteNotificationsById`, this.deleteNotificationsById.bind(this), ); @@ -1033,7 +1027,7 @@ export default class NotificationServicesController extends BaseController< state.metamaskNotificationsList = metamaskNotifications; }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:notificationsListUpdated`, this.state.metamaskNotificationsList, ); @@ -1114,7 +1108,7 @@ export default class NotificationServicesController extends BaseController< await this.deleteNotificationById(id); } - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:notificationsListUpdated`, this.state.metamaskNotificationsList, ); @@ -1229,7 +1223,7 @@ export default class NotificationServicesController extends BaseController< ); }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:markNotificationsAsRead`, this.state.metamaskNotificationsList, ); @@ -1267,7 +1261,7 @@ export default class NotificationServicesController extends BaseController< } }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:notificationsListUpdated`, this.state.metamaskNotificationsList, ); diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts index cc56003b1ed..dfc2543ce26 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; import log from 'loglevel'; @@ -280,7 +280,7 @@ describe('NotificationServicesPushController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts index 3c9267c1939..8a6e17841e6 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/NotificationServicesPushController.ts @@ -1,10 +1,10 @@ import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; import log from 'loglevel'; @@ -55,7 +55,7 @@ export type Actions = | NotificationServicesPushControllerUpdateTriggerPushNotificationsAction | NotificationServicesPushControllerSubscribeToNotificationsAction; -export type AllowedActions = +type AllowedActions = AuthenticationController.AuthenticationControllerGetBearerToken; export type NotificationServicesPushControllerStateChangeEvent = @@ -79,14 +79,10 @@ export type Events = | NotificationServicesPushControllerOnNewNotificationEvent | NotificationServicesPushControllerPushNotificationClickedEvent; -export type AllowedEvents = never; - -export type NotificationServicesPushControllerMessenger = RestrictedMessenger< +export type NotificationServicesPushControllerMessenger = Messenger< typeof controllerName, Actions | AllowedActions, - Events | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + Events >; export const defaultState: NotificationServicesPushControllerState = { @@ -98,19 +94,19 @@ const metadata: StateMetadata = { isPushEnabled: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, fcmToken: { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isUpdatingFCMToken: { includeInStateLogs: false, persist: false, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; @@ -202,19 +198,19 @@ export default class NotificationServicesPushController extends BaseController< } #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'NotificationServicesPushController:enablePushNotifications', this.enablePushNotifications.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'NotificationServicesPushController:disablePushNotifications', this.disablePushNotifications.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'NotificationServicesPushController:updateTriggerPushNotifications', this.updateTriggerPushNotifications.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'NotificationServicesPushController:subscribeToPushNotifications', this.subscribeToPushNotifications.bind(this), ); @@ -227,7 +223,7 @@ export default class NotificationServicesPushController extends BaseController< } async #getAndAssertBearerToken() { - const bearerToken = await this.messagingSystem.call( + const bearerToken = await this.messenger.call( 'AuthenticationController:getBearerToken', ); if (!bearerToken) { diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockMessenger.ts b/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockMessenger.ts index 7215287db3e..6b3185207f2 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockMessenger.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/__fixtures__/mockMessenger.ts @@ -1,21 +1,57 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; -import type { - AllowedActions, - AllowedEvents, - NotificationServicesPushControllerMessenger, -} from '..'; +import type { NotificationServicesPushControllerMessenger } from '..'; + +const controllerName = 'NotificationServicesPushController'; + +type AllNotificationServicesPushControllerActions = + MessengerActions; + +type AllNotificationServicesPushControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllNotificationServicesPushControllerActions, + AllNotificationServicesPushControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} export const buildPushPlatformNotificationsControllerMessenger = (): NotificationServicesPushControllerMessenger => { - const globalMessenger = new Messenger(); + const rootMessenger = getRootMessenger(); - return globalMessenger.getRestricted< - 'NotificationServicesPushController', - AllowedActions['type'] + const messenger = new Messenger< + typeof controllerName, + AllNotificationServicesPushControllerActions, + AllNotificationServicesPushControllerEvents, + RootMessenger >({ - name: 'NotificationServicesPushController', - allowedActions: ['AuthenticationController:getBearerToken'], - allowedEvents: [], + namespace: controllerName, + parent: rootMessenger, + }); + + rootMessenger.delegate({ + messenger, + actions: ['AuthenticationController:getBearerToken'], + events: [], }); + + return messenger; }; diff --git a/packages/notification-services-controller/tsconfig.build.json b/packages/notification-services-controller/tsconfig.build.json index d45ae90fe48..5bc179c42cf 100644 --- a/packages/notification-services-controller/tsconfig.build.json +++ b/packages/notification-services-controller/tsconfig.build.json @@ -15,6 +15,9 @@ }, { "path": "../keyring-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"], diff --git a/packages/notification-services-controller/tsconfig.json b/packages/notification-services-controller/tsconfig.json index 7f8e46e8b0c..f846578229e 100644 --- a/packages/notification-services-controller/tsconfig.json +++ b/packages/notification-services-controller/tsconfig.json @@ -12,6 +12,9 @@ }, { "path": "../keyring-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src"] diff --git a/yarn.lock b/yarn.lock index 8dbaec41939..e252568e6c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4097,6 +4097,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" "@metamask/keyring-controller": "npm:^23.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/profile-sync-controller": "npm:^25.1.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" From c7b38839ff6cf5387f79be262ee53cffcc2eac3a Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 13:51:39 +0200 Subject: [PATCH 153/247] refactor: migrate NetworkEnablementController to @metamask/messenger --- .../CHANGELOG.md | 5 + .../package.json | 1 + .../src/NetworkEnablementController.test.ts | 246 ++++++++++-------- .../src/NetworkEnablementController.ts | 24 +- .../tsconfig.build.json | 3 +- .../tsconfig.json | 3 +- yarn.lock | 1 + 7 files changed, 159 insertions(+), 124 deletions(-) diff --git a/packages/network-enablement-controller/CHANGELOG.md b/packages/network-enablement-controller/CHANGELOG.md index 1e6c591749f..c386146850c 100644 --- a/packages/network-enablement-controller/CHANGELOG.md +++ b/packages/network-enablement-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6540](https://github.com/MetaMask/core/pull/6540)) + - Previously, `NetworkEnablementController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [2.1.0] ### Added diff --git a/packages/network-enablement-controller/package.json b/packages/network-enablement-controller/package.json index 7bef3c50e3f..605de8e0531 100644 --- a/packages/network-enablement-controller/package.json +++ b/packages/network-enablement-controller/package.json @@ -64,6 +64,7 @@ "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", "@metamask/keyring-api": "^21.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "reselect": "^5.1.1" }, diff --git a/packages/network-enablement-controller/src/NetworkEnablementController.test.ts b/packages/network-enablement-controller/src/NetworkEnablementController.test.ts index 9b55e31433b..1a278b52b1a 100644 --- a/packages/network-enablement-controller/src/NetworkEnablementController.test.ts +++ b/packages/network-enablement-controller/src/NetworkEnablementController.test.ts @@ -1,6 +1,13 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { BuiltInNetworkName, ChainId } from '@metamask/controller-utils'; import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { RpcEndpointType } from '@metamask/network-controller'; import { TransactionStatus, @@ -16,42 +23,70 @@ import { useFakeTimers } from 'sinon'; import { POPULAR_NETWORKS } from './constants'; import { NetworkEnablementController } from './NetworkEnablementController'; -import type { - NetworkEnablementControllerActions, - NetworkEnablementControllerEvents, - AllowedEvents, - AllowedActions, - NetworkEnablementControllerMessenger, -} from './NetworkEnablementController'; +import type { NetworkEnablementControllerMessenger } from './NetworkEnablementController'; import { advanceTime } from '../../../tests/helpers'; +const controllerName = 'NetworkEnablementController'; + +type AllNetworkEnablementControllerActions = + MessengerActions; + +type AllNetworkEnablementControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllNetworkEnablementControllerActions, + AllNetworkEnablementControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + const setupController = ({ config, }: { config?: Partial< ConstructorParameters[0] >; -} = {}) => { - const messenger = new Messenger< - NetworkEnablementControllerActions | AllowedActions, - NetworkEnablementControllerEvents | AllowedEvents - >(); - - const networkEnablementControllerMessenger: NetworkEnablementControllerMessenger = - messenger.getRestricted({ - name: 'NetworkEnablementController', - allowedActions: [ - 'NetworkController:getState', - 'MultichainNetworkController:getState', - ], - allowedEvents: [ - 'NetworkController:networkAdded', - 'NetworkController:networkRemoved', - 'TransactionController:transactionSubmitted', - ], - }); +} = {}): { + controller: NetworkEnablementController; + rootMessenger: RootMessenger; +} => { + const rootMessenger = getRootMessenger(); + + const networkEnablementControllerMessenger = new Messenger< + typeof controllerName, + AllNetworkEnablementControllerActions, + AllNetworkEnablementControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); - messenger.registerActionHandler( + rootMessenger.delegate({ + messenger: networkEnablementControllerMessenger, + actions: [ + 'NetworkController:getState', + 'MultichainNetworkController:getState', + ], + events: [ + 'NetworkController:networkAdded', + 'NetworkController:networkRemoved', + 'TransactionController:transactionSubmitted', + ], + }); + + rootMessenger.registerActionHandler( 'NetworkController:getState', jest.fn().mockImplementation(() => ({ networkConfigurationsByChainId: { @@ -78,20 +113,10 @@ const setupController = ({ return { controller, - messenger, + rootMessenger, }; }; -// Helper function to setup controller with default state (no init needed) -const setupInitializedController = ( - config?: Partial< - ConstructorParameters[0] - >, -) => { - const setup = setupController({ config }); - return setup; -}; - describe('NetworkEnablementController', () => { let clock: sinon.SinonFakeTimers; @@ -133,12 +158,12 @@ describe('NetworkEnablementController', () => { }); it('subscribes to NetworkController:networkAdded', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // Publish an update with avax network added // Avalanche is a popular network, and we already have >2 popular networks enabled // So the new behavior should keep current selection (add but don't enable) - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0xa86a', blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -183,10 +208,10 @@ describe('NetworkEnablementController', () => { }); it('subscribes to NetworkController:networkRemoved', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // Publish an update with linea network removed - messenger.publish('NetworkController:networkRemoved', { + rootMessenger.publish('NetworkController:networkRemoved', { chainId: '0xe708', // Linea Mainnet blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -229,12 +254,12 @@ describe('NetworkEnablementController', () => { }); it('handles TransactionController:transactionSubmitted with missing chainId gracefully', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); const initialState = { ...controller.state }; // Publish a transaction submitted event without chainId - messenger.publish('TransactionController:transactionSubmitted', { + rootMessenger.publish('TransactionController:transactionSubmitted', { transactionMeta: { networkClientId: 'test-network', id: 'test-tx-id', @@ -256,13 +281,13 @@ describe('NetworkEnablementController', () => { }); it('handles TransactionController:transactionSubmitted with malformed structure gracefully', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); const initialState = { ...controller.state }; // Publish a transaction submitted event with malformed structure // @ts-expect-error - Testing runtime safety for malformed payload - messenger.publish('TransactionController:transactionSubmitted', { + rootMessenger.publish('TransactionController:transactionSubmitted', { // Missing transactionMeta entirely }); @@ -273,12 +298,12 @@ describe('NetworkEnablementController', () => { }); it('handles TransactionController:transactionSubmitted with null/undefined transactionMeta gracefully', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); const initialState = { ...controller.state }; // Test with null transactionMeta - messenger.publish('TransactionController:transactionSubmitted', { + rootMessenger.publish('TransactionController:transactionSubmitted', { // @ts-expect-error - Testing runtime safety for null transactionMeta transactionMeta: null, }); @@ -289,7 +314,7 @@ describe('NetworkEnablementController', () => { expect(controller.state).toStrictEqual(initialState); // Test with undefined transactionMeta - messenger.publish('TransactionController:transactionSubmitted', { + rootMessenger.publish('TransactionController:transactionSubmitted', { // @ts-expect-error - Testing runtime safety for undefined transactionMeta transactionMeta: undefined, }); @@ -301,14 +326,14 @@ describe('NetworkEnablementController', () => { }); it('does fallback to ethereum when removing the last enabled network', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // disable all networks except linea controller.disableNetwork('0x1'); // Ethereum Mainnet controller.disableNetwork('0x2105'); // Base Mainnet // Publish an update with linea network removed - messenger.publish('NetworkController:networkRemoved', { + rootMessenger.publish('NetworkController:networkRemoved', { chainId: '0xe708', // Linea Mainnet blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -356,7 +381,7 @@ describe('NetworkEnablementController', () => { jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -428,7 +453,7 @@ describe('NetworkEnablementController', () => { it('only enables popular networks that exist in NetworkController configurations', () => { // Create a separate controller setup for this test to avoid handler conflicts - const { controller, messenger } = setupController({ + const { controller } = setupController({ config: { state: { enabledNetworkMap: { @@ -439,7 +464,8 @@ describe('NetworkEnablementController', () => { }, }); - jest.spyOn(messenger, 'call').mockImplementation( + // eslint-disable-next-line dot-notation + jest.spyOn(controller['messenger'], 'call').mockImplementation( // eslint-disable-next-line @typescript-eslint/no-explicit-any (actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -492,10 +518,11 @@ describe('NetworkEnablementController', () => { }); it('handles missing MultichainNetworkController gracefully', () => { - const { controller, messenger } = setupController(); + const { controller } = setupController(); jest - .spyOn(messenger, 'call') + // eslint-disable-next-line dot-notation + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -536,7 +563,7 @@ describe('NetworkEnablementController', () => { jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -597,7 +624,7 @@ describe('NetworkEnablementController', () => { jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: unknown[]): any => { const responses = { @@ -649,7 +676,7 @@ describe('NetworkEnablementController', () => { // Mock MultichainNetworkController to include Bitcoin testnet BEFORE calling init jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -701,7 +728,7 @@ describe('NetworkEnablementController', () => { // Mock MultichainNetworkController to include Bitcoin signet BEFORE calling init jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -750,12 +777,12 @@ describe('NetworkEnablementController', () => { describe('enableAllPopularNetworks', () => { it('enables all popular networks that exist in controller configurations and Solana mainnet', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Mock the network configurations jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -855,10 +882,11 @@ describe('NetworkEnablementController', () => { }); it('enables all popular networks from constants', () => { - const { controller, messenger } = setupController(); + const { controller } = setupController(); // Mock all popular networks to be available in configurations - jest.spyOn(messenger, 'call').mockImplementation( + // eslint-disable-next-line dot-notation + jest.spyOn(controller['messenger'], 'call').mockImplementation( // eslint-disable-next-line @typescript-eslint/no-explicit-any (actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -937,12 +965,12 @@ describe('NetworkEnablementController', () => { }); it('disables existing networks and enables only popular networks (exclusive behavior)', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // Mock the network configurations to include popular networks jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -981,7 +1009,7 @@ describe('NetworkEnablementController', () => { }); // Add a non-popular network - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0x2', // A network not in POPULAR_NETWORKS blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -1026,7 +1054,7 @@ describe('NetworkEnablementController', () => { // Mock the network configurations to include Bitcoin jest // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(controller['messenger'], 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: unknown[]): any => { const responses = { @@ -1071,7 +1099,7 @@ describe('NetworkEnablementController', () => { describe('enableNetwork', () => { it('enables a network and clears all others in all namespaces', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Disable a popular network (Ethereum Mainnet) controller.disableNetwork('0x1'); @@ -1131,10 +1159,10 @@ describe('NetworkEnablementController', () => { }); it('enables any network and clears all others (exclusive behavior)', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // Add a non-popular network - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0x2', blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -1311,10 +1339,10 @@ describe('NetworkEnablementController', () => { }); it('handle no namespace bucket', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // add new network with no namespace bucket - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { // @ts-expect-error Intentionally passing an invalid chain ID chainId: 'bip122:000000000019d6689c085ae165831e93', blockExplorerUrls: [], @@ -1361,7 +1389,7 @@ describe('NetworkEnablementController', () => { describe('disableNetwork', () => { it('disables an EVM network using hex chain ID', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Disable a network (but not the last one) controller.disableNetwork('0xe708'); // Linea Mainnet @@ -1402,7 +1430,7 @@ describe('NetworkEnablementController', () => { }); it('disables the last active network for an EVM namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // disable all networks except one controller.disableNetwork('0xe708'); // Linea Mainnet @@ -1456,7 +1484,7 @@ describe('NetworkEnablementController', () => { describe('isNetworkEnabled', () => { it('returns true for enabled networks using hex chain ID', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Test default enabled networks expect(controller.isNetworkEnabled('0x1')).toBe(true); // Ethereum Mainnet @@ -1465,7 +1493,7 @@ describe('NetworkEnablementController', () => { }); it('returns false for disabled networks using hex chain ID', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Disable a network and test controller.disableNetwork('0xe708'); // Linea Mainnet (not the last one) @@ -1477,7 +1505,7 @@ describe('NetworkEnablementController', () => { }); it('returns true for enabled networks using CAIP chain ID', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Test EVM networks with CAIP format expect(controller.isNetworkEnabled('eip155:1')).toBe(true); // Ethereum Mainnet @@ -1491,7 +1519,7 @@ describe('NetworkEnablementController', () => { }); it('returns false for disabled networks using CAIP chain ID', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Disable a network using hex and test with CAIP controller.disableNetwork('0xe708'); // Linea Mainnet (not the last one) @@ -1526,7 +1554,7 @@ describe('NetworkEnablementController', () => { }); it('works correctly after enabling/disabling networks', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Initially enabled expect(controller.isNetworkEnabled('0xe708')).toBe(true); @@ -1541,7 +1569,7 @@ describe('NetworkEnablementController', () => { }); it('maintains consistency between hex and CAIP formats for same network', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Both formats should return the same result for the same network expect(controller.isNetworkEnabled('0x1')).toBe( @@ -1563,14 +1591,14 @@ describe('NetworkEnablementController', () => { }); it('works with dynamically added networks', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // Initially, Avalanche network should not be enabled (doesn't exist) expect(controller.isNetworkEnabled('0xa86a')).toBe(false); // Add Avalanche network (popular network in popular mode) // Should keep current selection (add but don't enable) - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0xa86a', blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -1593,7 +1621,7 @@ describe('NetworkEnablementController', () => { }); it('handles disabling networks across different namespaces independently, but adding networks has exclusive behavior', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // EVM networks should not affect Solana network status when disabling expect( @@ -1610,7 +1638,7 @@ describe('NetworkEnablementController', () => { ).toBe(true); // Add a Bitcoin network (this triggers enabling, which disables all others) - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { // @ts-expect-error Intentionally testing with Bitcoin network chainId: 'bip122:000000000019d6689c085ae165831e93', blockExplorerUrls: [], @@ -1814,10 +1842,10 @@ describe('NetworkEnablementController', () => { }); it('handles Bitcoin network addition dynamically', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // Add Bitcoin testnet dynamically - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { // @ts-expect-error Testing with Bitcoin network chainId: BtcScope.Testnet, blockExplorerUrls: [], @@ -2048,10 +2076,10 @@ describe('NetworkEnablementController', () => { }); it('handles Tron network addition dynamically', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // Add Tron Nile dynamically - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { // @ts-expect-error Testing with Tron network chainId: TrxScope.Nile, blockExplorerUrls: [], @@ -2116,7 +2144,7 @@ describe('NetworkEnablementController', () => { }); it('enables a Tron network in the Tron namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Enable Tron Nile in the Tron namespace controller.enableNetworkInNamespace( @@ -2138,7 +2166,7 @@ describe('NetworkEnablementController', () => { }); it('throws error when Tron chainId namespace does not match provided namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Try to enable Tron network in Solana namespace expect(() => { @@ -2161,7 +2189,7 @@ describe('NetworkEnablementController', () => { describe('enableNetworkInNamespace', () => { it('enables a network in the specified namespace and disables others in same namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Initially multiple EVM networks are enabled expect(controller.isNetworkEnabled('0x1')).toBe(true); @@ -2183,7 +2211,7 @@ describe('NetworkEnablementController', () => { }); it('enables a network using CAIP chain ID in the specified namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Enable Ethereum mainnet using CAIP format controller.enableNetworkInNamespace( @@ -2198,7 +2226,7 @@ describe('NetworkEnablementController', () => { }); it('enables a Solana network in the Solana namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Enable Solana testnet in the Solana namespace controller.enableNetworkInNamespace( @@ -2219,7 +2247,7 @@ describe('NetworkEnablementController', () => { }); it('enables a Bitcoin network in the Bitcoin namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Enable Bitcoin testnet in the Bitcoin namespace controller.enableNetworkInNamespace( @@ -2240,7 +2268,7 @@ describe('NetworkEnablementController', () => { }); it('throws error when chainId namespace does not match provided namespace', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Try to enable Ethereum network in Solana namespace expect(() => { @@ -2271,7 +2299,7 @@ describe('NetworkEnablementController', () => { }); it('throws error with CAIP chain ID when namespace does not match', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Try to enable Ethereum network using CAIP format in Solana namespace expect(() => { @@ -2284,7 +2312,7 @@ describe('NetworkEnablementController', () => { ); }); it('handles enabling an already enabled network', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Ethereum mainnet is already enabled expect(controller.isNetworkEnabled('0x1')).toBe(true); @@ -2317,7 +2345,7 @@ describe('NetworkEnablementController', () => { }); it('maintains consistency between hex and CAIP formats', () => { - const { controller } = setupInitializedController(); + const { controller } = setupController(); // Enable using hex format controller.enableNetworkInNamespace('0x1', KnownCaipNamespace.Eip155); @@ -2368,7 +2396,7 @@ describe('NetworkEnablementController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -2512,7 +2540,7 @@ describe('NetworkEnablementController', () => { describe('new onAddNetwork behavior', () => { it('switches to newly added popular network when NOT in popular networks mode', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // Start with only 1 popular network enabled (not in popular networks mode) controller.disableNetwork('0xe708'); // Disable Linea @@ -2524,7 +2552,7 @@ describe('NetworkEnablementController', () => { expect(controller.isNetworkEnabled('0x2105')).toBe(false); // Add Avalanche (popular network) when NOT in popular networks mode - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0xa86a', // Avalanche - popular network blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -2549,7 +2577,7 @@ describe('NetworkEnablementController', () => { }); it('switches to newly added non-popular network even when in popular networks mode', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // Default state has 3 popular networks enabled (in popular networks mode) expect(controller.isNetworkEnabled('0x1')).toBe(true); @@ -2557,7 +2585,7 @@ describe('NetworkEnablementController', () => { expect(controller.isNetworkEnabled('0x2105')).toBe(true); // Add a non-popular network when in popular networks mode - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0x999', // Non-popular network blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -2582,7 +2610,7 @@ describe('NetworkEnablementController', () => { }); it('keeps current selection when adding popular network in popular networks mode', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger } = setupController(); // Default state has 3 popular networks enabled (in popular networks mode) expect(controller.isNetworkEnabled('0x1')).toBe(true); @@ -2590,7 +2618,7 @@ describe('NetworkEnablementController', () => { expect(controller.isNetworkEnabled('0x2105')).toBe(true); // Add another popular network when in popular networks mode - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0x89', // Polygon - popular network blockExplorerUrls: [], defaultRpcEndpointIndex: 0, @@ -2615,7 +2643,7 @@ describe('NetworkEnablementController', () => { }); it('handles edge case: exactly 2 popular networks enabled (not in popular mode)', async () => { - const { controller, messenger } = setupController(); + const { controller, rootMessenger } = setupController(); // Start with exactly 2 popular networks enabled (not >2, so not in popular mode) controller.disableNetwork('0x2105'); // Disable Base, keep only Ethereum and Linea @@ -2624,7 +2652,7 @@ describe('NetworkEnablementController', () => { expect(controller.isNetworkEnabled('0x2105')).toBe(false); // Add another popular network when NOT in popular networks mode (exactly 2 enabled) - messenger.publish('NetworkController:networkAdded', { + rootMessenger.publish('NetworkController:networkAdded', { chainId: '0xa86a', // Avalanche - popular network blockExplorerUrls: [], defaultRpcEndpointIndex: 0, diff --git a/packages/network-enablement-controller/src/NetworkEnablementController.ts b/packages/network-enablement-controller/src/NetworkEnablementController.ts index 3e72e9b7f36..f437d846741 100644 --- a/packages/network-enablement-controller/src/NetworkEnablementController.ts +++ b/packages/network-enablement-controller/src/NetworkEnablementController.ts @@ -1,11 +1,11 @@ -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { BuiltInNetworkName, ChainId } from '@metamask/controller-utils'; import { BtcScope, SolScope, TrxScope } from '@metamask/keyring-api'; +import type { Messenger } from '@metamask/messenger'; import type { MultichainNetworkControllerGetStateAction } from '@metamask/multichain-network-controller'; import type { NetworkControllerGetStateAction, @@ -94,12 +94,10 @@ export type AllowedEvents = | NetworkControllerStateChangeEvent | TransactionControllerTransactionSubmittedEvent; -export type NetworkEnablementControllerMessenger = RestrictedMessenger< +export type NetworkEnablementControllerMessenger = Messenger< typeof controllerName, NetworkEnablementControllerActions | AllowedActions, - NetworkEnablementControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + NetworkEnablementControllerEvents | AllowedEvents >; /** @@ -138,7 +136,7 @@ const metadata = { enabledNetworkMap: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; @@ -293,10 +291,10 @@ export class NetworkEnablementController extends BaseController< }); // Get current network configurations to check if networks exist - const networkControllerState = this.messagingSystem.call( + const networkControllerState = this.messenger.call( 'NetworkController:getState', ); - const multichainState = this.messagingSystem.call( + const multichainState = this.messenger.call( 'MultichainNetworkController:getState', ); @@ -371,12 +369,12 @@ export class NetworkEnablementController extends BaseController< init(): void { this.update((s) => { // Get network configurations from NetworkController (EVM networks) - const networkControllerState = this.messagingSystem.call( + const networkControllerState = this.messenger.call( 'NetworkController:getState', ); // Get network configurations from MultichainNetworkController (all networks) - const multichainState = this.messagingSystem.call( + const multichainState = this.messenger.call( 'MultichainNetworkController:getState', ); @@ -475,7 +473,7 @@ export class NetworkEnablementController extends BaseController< */ #isInPopularNetworksMode(): boolean { // Get current network configurations to check which popular networks exist - const networkControllerState = this.messagingSystem.call( + const networkControllerState = this.messenger.call( 'NetworkController:getState', ); diff --git a/packages/network-enablement-controller/tsconfig.build.json b/packages/network-enablement-controller/tsconfig.build.json index a4d958a3017..11328a92b94 100644 --- a/packages/network-enablement-controller/tsconfig.build.json +++ b/packages/network-enablement-controller/tsconfig.build.json @@ -10,7 +10,8 @@ { "path": "../network-controller/tsconfig.build.json" }, { "path": "../multichain-network-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, - { "path": "../transaction-controller/tsconfig.build.json" } + { "path": "../transaction-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/network-enablement-controller/tsconfig.json b/packages/network-enablement-controller/tsconfig.json index 557e433b745..e1ebf385335 100644 --- a/packages/network-enablement-controller/tsconfig.json +++ b/packages/network-enablement-controller/tsconfig.json @@ -9,7 +9,8 @@ { "path": "../network-controller" }, { "path": "../multichain-network-controller" }, { "path": "../controller-utils" }, - { "path": "../transaction-controller" } + { "path": "../transaction-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 8dbaec41939..90fafd00f7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4054,6 +4054,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" "@metamask/keyring-api": "npm:^21.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/multichain-network-controller": "npm:^1.0.0" "@metamask/network-controller": "npm:^24.2.0" "@metamask/transaction-controller": "npm:^60.6.0" From ecd695cccd8467779c518813dd911d60e3b06495 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 14:33:52 +0200 Subject: [PATCH 154/247] refactor: migrate NameController to @metamask/messenger --- packages/name-controller/CHANGELOG.md | 2 ++ packages/name-controller/package.json | 1 + .../name-controller/src/NameController.test.ts | 4 ++-- packages/name-controller/src/NameController.ts | 16 +++++++--------- packages/name-controller/tsconfig.build.json | 3 +++ packages/name-controller/tsconfig.json | 3 +++ yarn.lock | 1 + 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/name-controller/CHANGELOG.md b/packages/name-controller/CHANGELOG.md index 9c7f33e1220..7bfad19dd87 100644 --- a/packages/name-controller/CHANGELOG.md +++ b/packages/name-controller/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6541](https://github.com/MetaMask/core/pull/6541)) + - Previously, `NameController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.2.0` to `^11.8.1` ([#6054](https://github.com/MetaMask/core/pull/6054)[#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.0.0` to `^8.4.0` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/controller-utils` from `^11.5.0` to `^11.14.0` ([#5439](https://github.com/MetaMask/core/pull/5439), [#5583](https://github.com/MetaMask/core/pull/5583), [#5765](https://github.com/MetaMask/core/pull/5765), [#5812](https://github.com/MetaMask/core/pull/5812), [#5935](https://github.com/MetaMask/core/pull/5935), [#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303), [#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629)) diff --git a/packages/name-controller/package.json b/packages/name-controller/package.json index 8386951201e..3496546950f 100644 --- a/packages/name-controller/package.json +++ b/packages/name-controller/package.json @@ -50,6 +50,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "async-mutex": "^0.5.0" }, diff --git a/packages/name-controller/src/NameController.test.ts b/packages/name-controller/src/NameController.test.ts index 83b197261fd..f466bd792f0 100644 --- a/packages/name-controller/src/NameController.test.ts +++ b/packages/name-controller/src/NameController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { SetNameRequest, @@ -2765,7 +2765,7 @@ describe('NameController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/name-controller/src/NameController.ts b/packages/name-controller/src/NameController.ts index b37327916e6..9bd5dd8b3dd 100644 --- a/packages/name-controller/src/NameController.ts +++ b/packages/name-controller/src/NameController.ts @@ -1,10 +1,10 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { isSafeDynamicKey } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NameProvider, @@ -44,13 +44,13 @@ const stateMetadata = { names: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, nameSources: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -99,12 +99,10 @@ export type NameControllerActions = GetNameState; export type NameControllerEvents = NameStateChange; -export type NameControllerMessenger = RestrictedMessenger< +export type NameControllerMessenger = Messenger< typeof controllerName, NameControllerActions, - NameControllerEvents, - never, - never + NameControllerEvents >; export type NameControllerOptions = { diff --git a/packages/name-controller/tsconfig.build.json b/packages/name-controller/tsconfig.build.json index 779d385a6ab..249f327913d 100644 --- a/packages/name-controller/tsconfig.build.json +++ b/packages/name-controller/tsconfig.build.json @@ -8,6 +8,9 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/name-controller/tsconfig.json b/packages/name-controller/tsconfig.json index f2d7b67ff66..cb296895b28 100644 --- a/packages/name-controller/tsconfig.json +++ b/packages/name-controller/tsconfig.json @@ -6,6 +6,9 @@ "references": [ { "path": "../base-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src"] diff --git a/yarn.lock b/yarn.lock index 8dbaec41939..f70ae81eeb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3989,6 +3989,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" async-mutex: "npm:^0.5.0" From f10d8590658c1f1442d2ec9f4438d2a2c7bb131a Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 9 Sep 2025 22:45:53 +0200 Subject: [PATCH 155/247] refactor: migrate MultichainTransactionsController to @metamask/messenger --- .../CHANGELOG.md | 2 + .../package.json | 1 + .../MultichainTransactionsController.test.ts | 157 ++++++++++++------ .../src/MultichainTransactionsController.ts | 36 ++-- .../tsconfig.build.json | 3 +- .../tsconfig.json | 3 +- yarn.lock | 1 + 7 files changed, 126 insertions(+), 77 deletions(-) diff --git a/packages/multichain-transactions-controller/CHANGELOG.md b/packages/multichain-transactions-controller/CHANGELOG.md index f3b5f5d7107..e707efa4dd4 100644 --- a/packages/multichain-transactions-controller/CHANGELOG.md +++ b/packages/multichain-transactions-controller/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6542](https://github.com/MetaMask/core/pull/6542)) + - Previously, `MultichainTransactionsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.1.0` to `^8.4.0` ([#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632)) - Bump `@metamask/keyring-api` from `^20.1.0` to `^21.0.0` ([#6560](https://github.com/MetaMask/core/pull/6560)) - Bump `@metamask/keyring-internal-api` from `^8.1.0` to `^9.0.0` ([#6560](https://github.com/MetaMask/core/pull/6560)) diff --git a/packages/multichain-transactions-controller/package.json b/packages/multichain-transactions-controller/package.json index 1c47b1add14..29ab419b0db 100644 --- a/packages/multichain-transactions-controller/package.json +++ b/packages/multichain-transactions-controller/package.json @@ -51,6 +51,7 @@ "@metamask/keyring-api": "^21.0.0", "@metamask/keyring-internal-api": "^9.0.0", "@metamask/keyring-snap-client": "^8.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/polling-controller": "^14.0.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", diff --git a/packages/multichain-transactions-controller/src/MultichainTransactionsController.test.ts b/packages/multichain-transactions-controller/src/MultichainTransactionsController.test.ts index d15208b8b34..cd08a82e923 100644 --- a/packages/multichain-transactions-controller/src/MultichainTransactionsController.test.ts +++ b/packages/multichain-transactions-controller/src/MultichainTransactionsController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { AccountTransactionsUpdatedEventPayload, CaipAssetType, @@ -15,6 +15,13 @@ import { } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { CaipChainId } from '@metamask/utils'; import { v4 as uuidv4 } from 'uuid'; @@ -22,8 +29,6 @@ import { MultichainNetwork } from './constants'; import { MultichainTransactionsController, getDefaultMultichainTransactionsControllerState, - type AllowedActions, - type AllowedEvents, type MultichainTransactionsControllerState, type MultichainTransactionsControllerMessenger, } from './MultichainTransactionsController'; @@ -127,6 +132,31 @@ const mockTransactionResult = { next: null, }; +const controllerName = 'MultichainTransactionsController'; + +type AllMultichainTransactionsControllerActions = + MessengerActions; + +type AllMultichainTransactionsControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllMultichainTransactionsControllerActions, + AllMultichainTransactionsControllerEvents +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + const setupController = ({ state = getDefaultMultichainTransactionsControllerState(), mocks, @@ -136,26 +166,41 @@ const setupController = ({ listMultichainAccounts?: InternalAccount[]; handleRequestReturnValue?: TransactionsPage; }; -} = {}) => { - const messenger = new Messenger(); - - const multichainTransactionsControllerMessenger: MultichainTransactionsControllerMessenger = - messenger.getRestricted({ - name: 'MultichainTransactionsController', - allowedActions: [ - 'SnapController:handleRequest', - 'AccountsController:listMultichainAccounts', - 'KeyringController:getState', - ], - allowedEvents: [ - 'AccountsController:accountAdded', - 'AccountsController:accountRemoved', - 'AccountsController:accountTransactionsUpdated', - ], - }); +} = {}): { + controller: MultichainTransactionsController; + rootMessenger: RootMessenger; + messenger: MultichainTransactionsControllerMessenger; + mockSnapHandleRequest: jest.Mock; + mockListMultichainAccounts: jest.Mock; + mockGetKeyringState: jest.Mock; +} => { + const rootMessenger = getRootMessenger(); + + const multichainTransactionsControllerMessenger = new Messenger< + typeof controllerName, + AllMultichainTransactionsControllerActions, + AllMultichainTransactionsControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger: multichainTransactionsControllerMessenger, + actions: [ + 'SnapController:handleRequest', + 'AccountsController:listMultichainAccounts', + 'KeyringController:getState', + ], + events: [ + 'AccountsController:accountAdded', + 'AccountsController:accountRemoved', + 'AccountsController:accountTransactionsUpdated', + ], + }); const mockSnapHandleRequest = jest.fn(); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'SnapController:handleRequest', mockSnapHandleRequest.mockReturnValue( mocks?.handleRequestReturnValue ?? mockTransactionResult, @@ -163,7 +208,7 @@ const setupController = ({ ); const mockListMultichainAccounts = jest.fn(); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:listMultichainAccounts', mockListMultichainAccounts.mockReturnValue( mocks?.listMultichainAccounts ?? [mockBtcAccount, mockEthAccount], @@ -173,7 +218,7 @@ const setupController = ({ const mockGetKeyringState = jest.fn().mockReturnValue({ isUnlocked: true, }); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'KeyringController:getState', mockGetKeyringState, ); @@ -185,7 +230,8 @@ const setupController = ({ return { controller, - messenger, + rootMessenger, + messenger: multichainTransactionsControllerMessenger, mockSnapHandleRequest, mockListMultichainAccounts, mockGetKeyringState, @@ -216,7 +262,7 @@ describe('MultichainTransactionsController', () => { }); it('updates transactions when "AccountsController:accountAdded" is fired', async () => { - const { controller, messenger, mockListMultichainAccounts } = + const { controller, rootMessenger, mockListMultichainAccounts } = setupController({ mocks: { listMultichainAccounts: [], @@ -224,7 +270,7 @@ describe('MultichainTransactionsController', () => { }); mockListMultichainAccounts.mockReturnValue([mockBtcAccount]); - messenger.publish('AccountsController:accountAdded', mockBtcAccount); + rootMessenger.publish('AccountsController:accountAdded', mockBtcAccount); await waitForAllPromises(); @@ -239,7 +285,7 @@ describe('MultichainTransactionsController', () => { }); it('updates transactions when "AccountsController:accountRemoved" is fired', async () => { - const { controller, messenger, mockListMultichainAccounts } = + const { controller, rootMessenger, mockListMultichainAccounts } = setupController(); await controller.updateTransactionsForAccount(mockBtcAccount.id); @@ -253,14 +299,17 @@ describe('MultichainTransactionsController', () => { lastUpdated: expect.any(Number), }); - messenger.publish('AccountsController:accountRemoved', mockBtcAccount.id); + rootMessenger.publish( + 'AccountsController:accountRemoved', + mockBtcAccount.id, + ); mockListMultichainAccounts.mockReturnValue([]); expect(controller.state.nonEvmTransactions).toStrictEqual({}); }); it('does not track balances for EVM accounts', async () => { - const { controller, messenger, mockListMultichainAccounts } = + const { controller, rootMessenger, mockListMultichainAccounts } = setupController({ mocks: { listMultichainAccounts: [], @@ -268,7 +317,7 @@ describe('MultichainTransactionsController', () => { }); mockListMultichainAccounts.mockReturnValue([mockEthAccount]); - messenger.publish('AccountsController:accountAdded', mockEthAccount); + rootMessenger.publish('AccountsController:accountAdded', mockEthAccount); expect(controller.state).toStrictEqual({ nonEvmTransactions: {}, @@ -503,7 +552,7 @@ describe('MultichainTransactionsController', () => { chain, }; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: { [mockSolAccountWithId.id]: { @@ -517,7 +566,7 @@ describe('MultichainTransactionsController', () => { }, }); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [mockSolAccountWithId.id]: [updatedExistingTransaction, newTransaction], }, @@ -536,7 +585,7 @@ describe('MultichainTransactionsController', () => { it('handles empty transaction updates gracefully', async () => { const { chain } = mockTransactionResult.data[0]; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: { [TEST_ACCOUNT_ID]: { @@ -550,7 +599,7 @@ describe('MultichainTransactionsController', () => { }, }); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: {}, }); @@ -568,13 +617,13 @@ describe('MultichainTransactionsController', () => { it('initializes new accounts with empty transactions array when receiving updates', async () => { const { chain } = mockTransactionResult.data[0]; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: {}, }, }); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [NEW_ACCOUNT_ID]: mockTransactionResult.data, }, @@ -592,7 +641,7 @@ describe('MultichainTransactionsController', () => { it('handles undefined transactions in update payload', async () => { const { chain } = mockTransactionResult.data[0]; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: { [TEST_ACCOUNT_ID]: { @@ -622,7 +671,7 @@ describe('MultichainTransactionsController', () => { }, }; - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: undefined, } as unknown as AccountTransactionsUpdatedEventPayload); @@ -646,7 +695,7 @@ describe('MultichainTransactionsController', () => { timestamp: 2000, }; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: { [TEST_ACCOUNT_ID]: { @@ -660,7 +709,7 @@ describe('MultichainTransactionsController', () => { }, }); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [TEST_ACCOUNT_ID]: [newerTransaction], }, @@ -694,7 +743,7 @@ describe('MultichainTransactionsController', () => { timestamp: 1000, }; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: { [TEST_ACCOUNT_ID]: { @@ -708,7 +757,7 @@ describe('MultichainTransactionsController', () => { }, }); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [TEST_ACCOUNT_ID]: [withTimestampTx, nullTimestampTx2], }, @@ -783,7 +832,7 @@ describe('MultichainTransactionsController', () => { chain: MultichainNetwork.SolanaDevnet, }; - const { controller, messenger } = setupController({ + const { controller, rootMessenger } = setupController({ state: { nonEvmTransactions: { [mockSolAccountWithId.id]: { @@ -797,7 +846,7 @@ describe('MultichainTransactionsController', () => { }, }); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [mockSolAccountWithId.id]: [mainnetTransaction, devnetTransaction], }, @@ -833,7 +882,7 @@ describe('MultichainTransactionsController', () => { }); it('publishes transactionConfirmed event when transaction is confirmed', async () => { - const { messenger } = setupController(); + const { rootMessenger, messenger } = setupController(); const confirmedTransaction = { ...mockTransactionResult.data[0], @@ -843,7 +892,7 @@ describe('MultichainTransactionsController', () => { const publishSpy = jest.spyOn(messenger, 'publish'); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [mockBtcAccount.id]: [confirmedTransaction], }, @@ -858,7 +907,7 @@ describe('MultichainTransactionsController', () => { }); it('publishes transactionSubmitted event when transaction is submitted', async () => { - const { messenger } = setupController(); + const { rootMessenger, messenger } = setupController(); const submittedTransaction = { ...mockTransactionResult.data[0], @@ -868,7 +917,7 @@ describe('MultichainTransactionsController', () => { const publishSpy = jest.spyOn(messenger, 'publish'); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [mockBtcAccount.id]: [submittedTransaction], }, @@ -883,7 +932,7 @@ describe('MultichainTransactionsController', () => { }); it('does not publish events for other transaction statuses', async () => { - const { messenger } = setupController(); + const { rootMessenger } = setupController(); const pendingTransaction = { ...mockTransactionResult.data[0], @@ -891,9 +940,9 @@ describe('MultichainTransactionsController', () => { status: 'unconfirmed' as const, }; - const publishSpy = jest.spyOn(messenger, 'publish'); + const publishSpy = jest.spyOn(rootMessenger, 'publish'); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [mockBtcAccount.id]: [pendingTransaction], }, @@ -912,7 +961,7 @@ describe('MultichainTransactionsController', () => { }); it('publishes correct events for multiple transactions with different statuses', async () => { - const { messenger } = setupController(); + const { rootMessenger, messenger } = setupController(); const transactions = [ { @@ -934,7 +983,7 @@ describe('MultichainTransactionsController', () => { const publishSpy = jest.spyOn(messenger, 'publish'); - messenger.publish('AccountsController:accountTransactionsUpdated', { + rootMessenger.publish('AccountsController:accountTransactionsUpdated', { transactions: { [mockBtcAccount.id]: transactions, }, @@ -960,7 +1009,7 @@ describe('MultichainTransactionsController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/multichain-transactions-controller/src/MultichainTransactionsController.ts b/packages/multichain-transactions-controller/src/MultichainTransactionsController.ts index 021f3fce71a..43527357518 100644 --- a/packages/multichain-transactions-controller/src/MultichainTransactionsController.ts +++ b/packages/multichain-transactions-controller/src/MultichainTransactionsController.ts @@ -8,8 +8,7 @@ import { BaseController, type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { isEvmAccountType, type Transaction, @@ -19,6 +18,7 @@ import { import type { KeyringControllerGetStateAction } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import { KeyringClient } from '@metamask/keyring-snap-client'; +import type { Messenger } from '@metamask/messenger'; import type { HandleSnapRequest } from '@metamask/snaps-controllers'; import type { SnapId } from '@metamask/snaps-sdk'; import { HandlerType } from '@metamask/snaps-utils'; @@ -116,18 +116,16 @@ export type MultichainTransactionsControllerEvents = /** * Messenger type for the MultichainTransactionsController. */ -export type MultichainTransactionsControllerMessenger = RestrictedMessenger< +export type MultichainTransactionsControllerMessenger = Messenger< typeof controllerName, MultichainTransactionsControllerActions | AllowedActions, - MultichainTransactionsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + MultichainTransactionsControllerEvents | AllowedEvents >; /** * Actions that this controller is allowed to call. */ -export type AllowedActions = +type AllowedActions = | HandleSnapRequest | KeyringControllerGetStateAction | AccountsControllerListMultichainAccountsAction; @@ -135,7 +133,7 @@ export type AllowedActions = /** * Events that this controller is allowed to subscribe. */ -export type AllowedEvents = +type AllowedEvents = | AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent | AccountsControllerAccountTransactionsUpdatedEvent; @@ -151,7 +149,7 @@ const multichainTransactionsControllerMetadata = { nonEvmTransactions: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -201,15 +199,15 @@ export class MultichainTransactionsController extends BaseController< }); } - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountAdded', (account: InternalAccount) => this.#handleOnAccountAdded(account), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountRemoved', (accountId: string) => this.#handleOnAccountRemoved(accountId), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountTransactionsUpdated', (transactionsUpdate: AccountTransactionsUpdatedEventPayload) => this.#handleOnAccountTransactionsUpdated(transactionsUpdate), @@ -222,9 +220,7 @@ export class MultichainTransactionsController extends BaseController< * @returns A list of multichain accounts. */ #listMultichainAccounts(): InternalAccount[] { - return this.messagingSystem.call( - 'AccountsController:listMultichainAccounts', - ); + return this.messenger.call('AccountsController:listMultichainAccounts'); } /** @@ -266,9 +262,7 @@ export class MultichainTransactionsController extends BaseController< * @param accountId - The ID of the account to get transactions for. */ async updateTransactionsForAccount(accountId: string) { - const { isUnlocked } = this.messagingSystem.call( - 'KeyringController:getState', - ); + const { isUnlocked } = this.messenger.call('KeyringController:getState'); if (!isUnlocked) { return; @@ -373,14 +367,14 @@ export class MultichainTransactionsController extends BaseController< */ #publishTransactionUpdateEvent(updatedTransaction: Transaction) { if (updatedTransaction.status === TransactionStatus.Confirmed) { - this.messagingSystem.publish( + this.messenger.publish( 'MultichainTransactionsController:transactionConfirmed', updatedTransaction, ); } if (updatedTransaction.status === TransactionStatus.Submitted) { - this.messagingSystem.publish( + this.messenger.publish( 'MultichainTransactionsController:transactionSubmitted', updatedTransaction, ); @@ -481,7 +475,7 @@ export class MultichainTransactionsController extends BaseController< #getClient(snapId: string): KeyringClient { return new KeyringClient({ send: async (request: JsonRpcRequest) => - (await this.messagingSystem.call('SnapController:handleRequest', { + (await this.messenger.call('SnapController:handleRequest', { snapId: snapId as SnapId, origin: 'metamask', handler: HandlerType.OnKeyringRequest, diff --git a/packages/multichain-transactions-controller/tsconfig.build.json b/packages/multichain-transactions-controller/tsconfig.build.json index 048cb0e3bef..695c01cd3df 100644 --- a/packages/multichain-transactions-controller/tsconfig.build.json +++ b/packages/multichain-transactions-controller/tsconfig.build.json @@ -9,7 +9,8 @@ { "path": "../accounts-controller/tsconfig.build.json" }, { "path": "../base-controller/tsconfig.build.json" }, { "path": "../keyring-controller/tsconfig.build.json" }, - { "path": "../polling-controller/tsconfig.build.json" } + { "path": "../polling-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/multichain-transactions-controller/tsconfig.json b/packages/multichain-transactions-controller/tsconfig.json index e0331deb7e0..ce215a73944 100644 --- a/packages/multichain-transactions-controller/tsconfig.json +++ b/packages/multichain-transactions-controller/tsconfig.json @@ -7,7 +7,8 @@ { "path": "../accounts-controller" }, { "path": "../base-controller" }, { "path": "../keyring-controller" }, - { "path": "../polling-controller" } + { "path": "../polling-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index 8dbaec41939..eab8a135357 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3961,6 +3961,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.1.0" "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-snap-client": "npm:^8.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/polling-controller": "npm:^14.0.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" From f823aa3d6311005cf49c41c2c5c5a4ff0432ca73 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 15:20:09 +0200 Subject: [PATCH 156/247] chore: bump @metmask/messenger to 0.3.0 --- packages/multichain-account-service/package.json | 2 +- packages/multichain-account-service/tsconfig.build.json | 3 ++- packages/multichain-account-service/tsconfig.json | 3 ++- yarn.lock | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/multichain-account-service/package.json b/packages/multichain-account-service/package.json index ae19353be28..6686f1b1a45 100644 --- a/packages/multichain-account-service/package.json +++ b/packages/multichain-account-service/package.json @@ -55,7 +55,7 @@ "@metamask/keyring-internal-api": "^9.0.0", "@metamask/keyring-snap-client": "^8.0.0", "@metamask/keyring-utils": "^3.1.0", - "@metamask/messenger": "^0.2.0", + "@metamask/messenger": "^0.3.0", "@metamask/snaps-sdk": "^9.0.0", "@metamask/snaps-utils": "^11.0.0", "@metamask/superstruct": "^3.1.0", diff --git a/packages/multichain-account-service/tsconfig.build.json b/packages/multichain-account-service/tsconfig.build.json index c01fbe218d1..006207ec850 100644 --- a/packages/multichain-account-service/tsconfig.build.json +++ b/packages/multichain-account-service/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../accounts-controller/tsconfig.build.json" }, - { "path": "../keyring-controller/tsconfig.build.json" } + { "path": "../keyring-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/multichain-account-service/tsconfig.json b/packages/multichain-account-service/tsconfig.json index c67da70b6eb..e1b9b25e4a4 100644 --- a/packages/multichain-account-service/tsconfig.json +++ b/packages/multichain-account-service/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../accounts-controller" }, - { "path": "../keyring-controller" } + { "path": "../keyring-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index f2f6e651c7f..6994e56d5b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3861,7 +3861,7 @@ __metadata: "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-snap-client": "npm:^8.0.0" "@metamask/keyring-utils": "npm:^3.1.0" - "@metamask/messenger": "npm:^0.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" From f9fc126e957d3e3afba7fc43564109328eb2cdab Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 15:24:29 +0200 Subject: [PATCH 157/247] fix: MultichainAccountGroup.test.ts --- .../src/MultichainAccountGroup.test.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/multichain-account-service/src/MultichainAccountGroup.test.ts b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts index ae915804aaa..7153f6d9b3a 100644 --- a/packages/multichain-account-service/src/MultichainAccountGroup.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts @@ -22,13 +22,8 @@ import { setupNamedAccountProvider, getMultichainAccountServiceMessenger, getRootMessenger, + type RootMessenger, } from './tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from './types'; function setup({ groupIndex = 0, @@ -44,10 +39,7 @@ function setup({ ], }: { groupIndex?: number; - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[][]; } = {}): { wallet: MultichainAccountWallet>; From 04a52680703afdc15eb602f1de1183fda3af685e Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 15:29:25 +0200 Subject: [PATCH 158/247] fix: MultichainAccountGroup.test.ts --- .../src/MultichainAccountService.test.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index 60728878563..875c8255721 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.test.ts @@ -211,17 +211,17 @@ describe('MultichainAccountService', () => { }, }; - const { mocks, rootMessenger } = setup({ + const { mocks, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_SOL_ACCOUNT_1], providerConfigs, }); expect(mocks.EvmAccountProvider.constructor).toHaveBeenCalledWith( - rootMessenger, + messenger, providerConfigs[EvmAccountProvider.NAME], ); expect(mocks.SolAccountProvider.constructor).toHaveBeenCalledWith( - rootMessenger, + messenger, providerConfigs[SolAccountProvider.NAME], ); }); @@ -806,9 +806,9 @@ describe('MultichainAccountService', () => { describe('actions', () => { it('gets a multichain account with MultichainAccountService:getMultichainAccount', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { rootMessenger } = setup({ accounts }); + const { messenger } = setup({ accounts }); - const group = rootMessenger.call( + const group = messenger.call( 'MultichainAccountService:getMultichainAccountGroup', { entropySource: MOCK_HD_KEYRING_1.metadata.id, groupIndex: 0 }, ); @@ -817,9 +817,9 @@ describe('MultichainAccountService', () => { it('gets multichain accounts with MultichainAccountService:getMultichainAccounts', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { rootMessenger } = setup({ accounts }); + const { messenger } = setup({ accounts }); - const groups = rootMessenger.call( + const groups = messenger.call( 'MultichainAccountService:getMultichainAccountGroups', { entropySource: MOCK_HD_KEYRING_1.metadata.id }, ); @@ -828,9 +828,9 @@ describe('MultichainAccountService', () => { it('gets multichain account wallet with MultichainAccountService:getMultichainAccountWallet', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { rootMessenger } = setup({ accounts }); + const { messenger } = setup({ accounts }); - const wallet = rootMessenger.call( + const wallet = messenger.call( 'MultichainAccountService:getMultichainAccountWallet', { entropySource: MOCK_HD_KEYRING_1.metadata.id }, ); @@ -839,9 +839,9 @@ describe('MultichainAccountService', () => { it('gets multichain account wallet with MultichainAccountService:getMultichainAccountWallets', () => { const accounts = [MOCK_HD_ACCOUNT_1]; - const { rootMessenger } = setup({ accounts }); + const { messenger } = setup({ accounts }); - const wallets = rootMessenger.call( + const wallets = messenger.call( 'MultichainAccountService:getMultichainAccountWallets', ); expect(wallets.length).toBeGreaterThan(0); From 230d965ccf6aeab39356d1a69c866ed1ccb5d6fa Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 15:34:32 +0200 Subject: [PATCH 159/247] fix: BtcAccountProvider.test.ts --- .../src/providers/BtcAccountProvider.test.ts | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts b/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts index a064532d4c2..f9e0c8014ff 100644 --- a/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/BtcAccountProvider.test.ts @@ -1,5 +1,4 @@ import { isBip44Account } from '@metamask/account-api'; -import type { Messenger } from '@metamask/base-controller'; import type { SnapKeyring } from '@metamask/eth-snap-keyring'; import { BtcAccountType } from '@metamask/keyring-api'; import type { KeyringMetadata } from '@metamask/keyring-controller'; @@ -19,13 +18,8 @@ import { MOCK_HD_ACCOUNT_1, MOCK_HD_KEYRING_1, MockAccountBuilder, + type RootMessenger, } from '../tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from '../types'; class MockBtcKeyring { readonly type = 'MockBtcKeyring'; @@ -111,17 +105,11 @@ function setup({ messenger = getRootMessenger(), accounts = [], }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[]; } = {}): { provider: AccountProviderWrapper; - messenger: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger: RootMessenger; keyring: MockBtcKeyring; mocks: { handleRequest: jest.Mock; From 0ab7b748d16f4277e279dde36461bdae27ca9306 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 15:39:44 +0200 Subject: [PATCH 160/247] refactor: migrate PhishingController to @metamask/messenger --- packages/phishing-controller/CHANGELOG.md | 5 + packages/phishing-controller/package.json | 1 + .../src/PhishingController.test.ts | 91 +++++++++++++------ .../src/PhishingController.ts | 46 +++++----- .../phishing-controller/tsconfig.build.json | 3 +- packages/phishing-controller/tsconfig.json | 3 +- yarn.lock | 1 + 7 files changed, 98 insertions(+), 52 deletions(-) diff --git a/packages/phishing-controller/CHANGELOG.md b/packages/phishing-controller/CHANGELOG.md index e1919def703..96058b52dd9 100644 --- a/packages/phishing-controller/CHANGELOG.md +++ b/packages/phishing-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6535](https://github.com/MetaMask/core/pull/6535)) + - Previously, `PhishingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [14.1.0] ### Added diff --git a/packages/phishing-controller/package.json b/packages/phishing-controller/package.json index 73514b3aeb9..df368d2c6a6 100644 --- a/packages/phishing-controller/package.json +++ b/packages/phishing-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.0", "@metamask/controller-utils": "^11.14.0", + "@metamask/messenger": "^0.3.0", "@noble/hashes": "^1.8.0", "@types/punycode": "^2.1.0", "ethereum-cryptography": "^2.1.2", diff --git a/packages/phishing-controller/src/PhishingController.test.ts b/packages/phishing-controller/src/PhishingController.test.ts index e2a2ff23d6f..b22e2e6c017 100644 --- a/packages/phishing-controller/src/PhishingController.test.ts +++ b/packages/phishing-controller/src/PhishingController.test.ts @@ -1,5 +1,11 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; -import type { TransactionControllerStateChangeEvent } from '@metamask/transaction-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { strict as assert } from 'assert'; import nock, { cleanAll, isDone, pendingMocks } from 'nock'; import sinon from 'sinon'; @@ -10,8 +16,6 @@ import { METAMASK_STALELIST_FILE, PhishingController, PHISHING_CONFIG_BASE_URL, - type PhishingControllerActions, - type PhishingControllerEvents, type PhishingControllerOptions, CLIENT_SIDE_DETECION_BASE_URL, C2_DOMAIN_BLOCKLIST_ENDPOINT, @@ -19,6 +23,7 @@ import { PHISHING_DETECTION_SCAN_ENDPOINT, PHISHING_DETECTION_BULK_SCAN_ENDPOINT, type BulkPhishingDetectionScanResponse, + type PhishingControllerMessenger, } from './PhishingController'; import { createMockStateChangePayload, @@ -32,24 +37,58 @@ import { getHostnameFromUrl } from './utils'; const controllerName = 'PhishingController'; +type AllPhishingControllerActions = + MessengerActions; + +type AllPhishingControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllPhishingControllerActions, + AllPhishingControllerEvents +>; + /** - * Constructs a restricted messenger with transaction events enabled. + * Creates and returns a root messenger for testing * - * @returns A restricted messenger that can listen to TransactionController events. + * @returns A messenger instance */ -function getRestrictedMessengerWithTransactionEvents() { +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + +/** + * Constructs a messenger for use in PhishingController tests. + * + * @returns A messenger and the root messenger. + */ +function setupMessenger(): { + messenger: PhishingControllerMessenger; + rootMessenger: RootMessenger; +} { + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< - PhishingControllerActions, - PhishingControllerEvents | TransactionControllerStateChangeEvent - >(); + typeof controllerName, + AllPhishingControllerActions, + AllPhishingControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + + rootMessenger.delegate({ + actions: [], + events: ['TransactionController:stateChange'], + messenger, + }); return { - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: ['TransactionController:stateChange'], - }), - globalMessenger: messenger, + messenger, + rootMessenger, }; } @@ -60,8 +99,9 @@ function getRestrictedMessengerWithTransactionEvents() { * @returns The constructed Phishing Controller. */ function getPhishingController(options?: Partial) { + const { messenger } = setupMessenger(); return new PhishingController({ - messenger: getRestrictedMessengerWithTransactionEvents().messenger, + messenger, ...options, }); } @@ -407,8 +447,9 @@ describe('PhishingController', () => { }); it('replaces existing phishing lists with completely new list from phishing detection API', async () => { + const { messenger } = setupMessenger(); const controller = new PhishingController({ - messenger: getRestrictedMessengerWithTransactionEvents().messenger, + messenger, stalelistRefreshInterval: 10, state: { phishingLists: [ @@ -3495,7 +3536,7 @@ describe('URL Scan Cache', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -3561,18 +3602,16 @@ describe('URL Scan Cache', () => { describe('Transaction Controller State Change Integration', () => { let controller: PhishingController; - let globalMessenger: Messenger< - PhishingControllerActions, - PhishingControllerEvents | TransactionControllerStateChangeEvent - >; + let globalMessenger: RootMessenger; let bulkScanTokensSpy: jest.SpyInstance; beforeEach(() => { - const messengerSetup = getRestrictedMessengerWithTransactionEvents(); - globalMessenger = messengerSetup.globalMessenger; + const { messenger, rootMessenger } = setupMessenger(); + + globalMessenger = rootMessenger; controller = new PhishingController({ - messenger: messengerSetup.messenger, + messenger, }); bulkScanTokensSpy = jest diff --git a/packages/phishing-controller/src/PhishingController.ts b/packages/phishing-controller/src/PhishingController.ts index 2bd79231fdd..c3bb20c6b9c 100644 --- a/packages/phishing-controller/src/PhishingController.ts +++ b/packages/phishing-controller/src/PhishingController.ts @@ -1,14 +1,14 @@ -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, - StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type StateMetadata, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import { safelyExecute, safelyExecuteWithTimeout, } from '@metamask/controller-utils'; +import { type Messenger } from '@metamask/messenger'; import type { TransactionControllerStateChangeEvent, TransactionMeta, @@ -238,49 +238,49 @@ const metadata: StateMetadata = { phishingLists: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, whitelist: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, whitelistPaths: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, hotlistLastFetched: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, stalelistLastFetched: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, c2DomainBlocklistLastFetched: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, urlScanCache: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, tokenScanCache: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -399,12 +399,10 @@ type AllowedActions = never; */ export type AllowedEvents = TransactionControllerStateChangeEvent; -export type PhishingControllerMessenger = RestrictedMessenger< +export type PhishingControllerMessenger = Messenger< typeof controllerName, PhishingControllerActions | AllowedActions, - PhishingControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + PhishingControllerEvents | AllowedEvents >; /** @@ -521,7 +519,7 @@ export class PhishingController extends BaseController< } #subscribeToTransactionControllerStateChange() { - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:stateChange', this.#transactionControllerStateChangeHandler, ); @@ -532,22 +530,22 @@ export class PhishingController extends BaseController< * actions. */ #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:maybeUpdateState` as const, this.maybeUpdateState.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:testOrigin` as const, this.test.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:bulkScanUrls` as const, this.bulkScanUrls.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:bulkScanTokens` as const, this.bulkScanTokens.bind(this), ); diff --git a/packages/phishing-controller/tsconfig.build.json b/packages/phishing-controller/tsconfig.build.json index ef633b78ac6..25a4d3c697a 100644 --- a/packages/phishing-controller/tsconfig.build.json +++ b/packages/phishing-controller/tsconfig.build.json @@ -8,7 +8,8 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, - { "path": "../transaction-controller/tsconfig.build.json" } + { "path": "../transaction-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] } diff --git a/packages/phishing-controller/tsconfig.json b/packages/phishing-controller/tsconfig.json index 9c91d666a84..5f32f34e8aa 100644 --- a/packages/phishing-controller/tsconfig.json +++ b/packages/phishing-controller/tsconfig.json @@ -6,7 +6,8 @@ "references": [ { "path": "../base-controller" }, { "path": "../controller-utils" }, - { "path": "../transaction-controller" } + { "path": "../transaction-controller" }, + { "path": "../messenger" } ], "include": ["../../types", "./src", "./tests"] } diff --git a/yarn.lock b/yarn.lock index 8dbaec41939..b1a3502530c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4212,6 +4212,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.0" "@metamask/controller-utils": "npm:^11.14.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/transaction-controller": "npm:^60.6.0" "@noble/hashes": "npm:^1.8.0" "@types/jest": "npm:^27.4.1" From 651317c14c5bfafa791f067e5525f2e027976bb3 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 15:41:55 +0200 Subject: [PATCH 161/247] fix: remove unused import --- .../src/MultichainAccountGroup.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/multichain-account-service/src/MultichainAccountGroup.test.ts b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts index 7153f6d9b3a..753393b0799 100644 --- a/packages/multichain-account-service/src/MultichainAccountGroup.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts @@ -5,7 +5,6 @@ import { toMultichainAccountGroupId, toMultichainAccountWalletId, } from '@metamask/account-api'; -import type { Messenger } from '@metamask/base-controller'; import { EthScope, SolScope } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; From 72b3357772a5729ab70b0ba20486ea6bc51f8fc6 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 16:01:32 +0200 Subject: [PATCH 162/247] fix: MultichainAccountWallet.test.ts --- .../src/MultichainAccountWallet.test.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts index 09d1b0e9420..5617e745830 100644 --- a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts @@ -31,14 +31,9 @@ import { setupNamedAccountProvider, getMultichainAccountServiceMessenger, getRootMessenger, + type RootMessenger, } from './tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, - MultichainAccountServiceMessenger, -} from './types'; +import type { MultichainAccountServiceMessenger } from './types'; function setup({ entropySource = MOCK_WALLET_1_ENTROPY_SOURCE, @@ -55,10 +50,7 @@ function setup({ ], }: { entropySource?: EntropySourceId; - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; providers?: MockAccountProvider[]; accounts?: InternalAccount[][]; } = {}): { From b8ff22a4ef7d0de177d598b5f2b89bcfedb018b8 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 16:08:06 +0200 Subject: [PATCH 163/247] fix: TrxAccountProvider.test.ts --- .../src/providers/TrxAccountProvider.test.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts index d52aaa25f95..7cfd053a08e 100644 --- a/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts @@ -17,13 +17,8 @@ import { MOCK_TRX_ACCOUNT_1, MOCK_TRX_DISCOVERED_ACCOUNT_1, MockAccountBuilder, + type RootMessenger, } from '../tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from '../types'; class MockTronKeyring { readonly type = 'MockTronKeyring'; @@ -94,17 +89,11 @@ function setup({ messenger = getRootMessenger(), accounts = [], }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[]; } = {}): { provider: AccountProviderWrapper; - messenger: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger: RootMessenger; keyring: MockTronKeyring; mocks: { handleRequest: jest.Mock; From 2def7afeb1b9555523b6174ae0f947e4f8b1a73c Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 16:23:47 +0200 Subject: [PATCH 164/247] fix: remove unused import --- .../src/providers/TrxAccountProvider.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts index 7cfd053a08e..8ae4c30856c 100644 --- a/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts @@ -1,5 +1,4 @@ import { isBip44Account } from '@metamask/account-api'; -import type { Messenger } from '@metamask/base-controller'; import type { SnapKeyring } from '@metamask/eth-snap-keyring'; import type { KeyringMetadata } from '@metamask/keyring-controller'; import type { From f41c0b65b2091d19e8a45c26324d1949b475037c Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 16:30:24 +0200 Subject: [PATCH 165/247] fix: remove unused import --- .../src/MultichainAccountWallet.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts index 5617e745830..d87a2361881 100644 --- a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts @@ -7,7 +7,6 @@ import { toMultichainAccountGroupId, toMultichainAccountWalletId, } from '@metamask/account-api'; -import type { Messenger } from '@metamask/base-controller'; import { EthAccountType, SolAccountType, From b5c8520fcc14531f2eef3947569e48fe65ca52bc Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 20:44:26 +0200 Subject: [PATCH 166/247] fix: wrong updated tsconfig --- packages/remote-feature-flag-controller/tsconfig.build.json | 5 +---- packages/remote-feature-flag-controller/tsconfig.json | 2 +- packages/signature-controller/tsconfig.build.json | 3 +++ packages/signature-controller/tsconfig.json | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/remote-feature-flag-controller/tsconfig.build.json b/packages/remote-feature-flag-controller/tsconfig.build.json index 931c4d6594b..e5fd7422b9a 100644 --- a/packages/remote-feature-flag-controller/tsconfig.build.json +++ b/packages/remote-feature-flag-controller/tsconfig.build.json @@ -5,9 +5,6 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [ - { "path": "../base-controller/tsconfig.build.json" }, - { "path": "../messenger/tsconfig.build.json" } - ], + "references": [{ "path": "../base-controller/tsconfig.build.json" }], "include": ["../../types", "./src"] } diff --git a/packages/remote-feature-flag-controller/tsconfig.json b/packages/remote-feature-flag-controller/tsconfig.json index 68c3ddfc2cd..34354c4b09d 100644 --- a/packages/remote-feature-flag-controller/tsconfig.json +++ b/packages/remote-feature-flag-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], + "references": [{ "path": "../base-controller" }], "include": ["../../types", "./src"] } diff --git a/packages/signature-controller/tsconfig.build.json b/packages/signature-controller/tsconfig.build.json index 1574be2f695..a89237f1d75 100644 --- a/packages/signature-controller/tsconfig.build.json +++ b/packages/signature-controller/tsconfig.build.json @@ -32,6 +32,9 @@ }, { "path": "../network-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/signature-controller/tsconfig.json b/packages/signature-controller/tsconfig.json index 99b81e54d19..0e27711c20a 100644 --- a/packages/signature-controller/tsconfig.json +++ b/packages/signature-controller/tsconfig.json @@ -30,6 +30,9 @@ }, { "path": "../network-controller" + }, + { + "path": "../messenger" } ], "include": ["../../types", "./src"] From 880f3f71f16d9723bb118a6a957d862a047279fa Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 20:45:44 +0200 Subject: [PATCH 167/247] fix: rollback remote-feature-flag changes --- packages/remote-feature-flag-controller/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/remote-feature-flag-controller/tsconfig.json b/packages/remote-feature-flag-controller/tsconfig.json index 34354c4b09d..831cc7b8670 100644 --- a/packages/remote-feature-flag-controller/tsconfig.json +++ b/packages/remote-feature-flag-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../base-controller" }], + "references": [{ "path": "../../packages/base-controller" }], "include": ["../../types", "./src"] } From d950e40491f4bc37b933d43aa7e4222e4cde0b01 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 20:47:06 +0200 Subject: [PATCH 168/247] fix: tsconfig --- packages/rate-limit-controller/tsconfig.build.json | 5 ++++- packages/rate-limit-controller/tsconfig.json | 2 +- packages/signature-controller/tsconfig.build.json | 3 --- packages/signature-controller/tsconfig.json | 3 --- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/rate-limit-controller/tsconfig.build.json b/packages/rate-limit-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 100644 --- a/packages/rate-limit-controller/tsconfig.build.json +++ b/packages/rate-limit-controller/tsconfig.build.json @@ -5,6 +5,9 @@ "outDir": "./dist", "rootDir": "./src" }, - "references": [{ "path": "../base-controller/tsconfig.build.json" }], + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" } + ], "include": ["../../types", "./src"] } diff --git a/packages/rate-limit-controller/tsconfig.json b/packages/rate-limit-controller/tsconfig.json index 34354c4b09d..68c3ddfc2cd 100644 --- a/packages/rate-limit-controller/tsconfig.json +++ b/packages/rate-limit-controller/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [{ "path": "../base-controller" }], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } diff --git a/packages/signature-controller/tsconfig.build.json b/packages/signature-controller/tsconfig.build.json index a89237f1d75..1574be2f695 100644 --- a/packages/signature-controller/tsconfig.build.json +++ b/packages/signature-controller/tsconfig.build.json @@ -32,9 +32,6 @@ }, { "path": "../network-controller/tsconfig.build.json" - }, - { - "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/signature-controller/tsconfig.json b/packages/signature-controller/tsconfig.json index 0e27711c20a..99b81e54d19 100644 --- a/packages/signature-controller/tsconfig.json +++ b/packages/signature-controller/tsconfig.json @@ -30,9 +30,6 @@ }, { "path": "../network-controller" - }, - { - "path": "../messenger" } ], "include": ["../../types", "./src"] From 48cb9a851b57ed4f1869af2936a0894f67e356e7 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Tue, 7 Oct 2025 20:53:00 +0200 Subject: [PATCH 169/247] refactor: NetworkEnablementController setupController messenger mock --- .../src/NetworkEnablementController.test.ts | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/packages/network-enablement-controller/src/NetworkEnablementController.test.ts b/packages/network-enablement-controller/src/NetworkEnablementController.test.ts index 1a278b52b1a..cf885764a05 100644 --- a/packages/network-enablement-controller/src/NetworkEnablementController.test.ts +++ b/packages/network-enablement-controller/src/NetworkEnablementController.test.ts @@ -60,6 +60,7 @@ const setupController = ({ } = {}): { controller: NetworkEnablementController; rootMessenger: RootMessenger; + messenger: NetworkEnablementControllerMessenger; } => { const rootMessenger = getRootMessenger(); @@ -114,6 +115,7 @@ const setupController = ({ return { controller, rootMessenger, + messenger: networkEnablementControllerMessenger, }; }; @@ -377,11 +379,10 @@ describe('NetworkEnablementController', () => { describe('init', () => { it('initializes network enablement state from controller configurations', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -453,7 +454,7 @@ describe('NetworkEnablementController', () => { it('only enables popular networks that exist in NetworkController configurations', () => { // Create a separate controller setup for this test to avoid handler conflicts - const { controller } = setupController({ + const { controller, messenger } = setupController({ config: { state: { enabledNetworkMap: { @@ -464,8 +465,7 @@ describe('NetworkEnablementController', () => { }, }); - // eslint-disable-next-line dot-notation - jest.spyOn(controller['messenger'], 'call').mockImplementation( + jest.spyOn(messenger, 'call').mockImplementation( // eslint-disable-next-line @typescript-eslint/no-explicit-any (actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -518,11 +518,10 @@ describe('NetworkEnablementController', () => { }); it('handles missing MultichainNetworkController gracefully', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -559,11 +558,10 @@ describe('NetworkEnablementController', () => { }); it('creates namespace buckets for all configured networks', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -614,7 +612,7 @@ describe('NetworkEnablementController', () => { }); it('creates new namespace buckets for networks that do not exist', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); // Start with empty state to test namespace bucket creation // eslint-disable-next-line dot-notation @@ -623,8 +621,7 @@ describe('NetworkEnablementController', () => { }); jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: unknown[]): any => { const responses = { @@ -671,12 +668,11 @@ describe('NetworkEnablementController', () => { }); it('sets Bitcoin testnet to false when it exists in MultichainNetworkController configurations', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); // Mock MultichainNetworkController to include Bitcoin testnet BEFORE calling init jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -723,12 +719,11 @@ describe('NetworkEnablementController', () => { }); it('sets Bitcoin signet to false when it exists in MultichainNetworkController configurations', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); // Mock MultichainNetworkController to include Bitcoin signet BEFORE calling init jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -777,12 +772,11 @@ describe('NetworkEnablementController', () => { describe('enableAllPopularNetworks', () => { it('enables all popular networks that exist in controller configurations and Solana mainnet', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); // Mock the network configurations jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -882,11 +876,10 @@ describe('NetworkEnablementController', () => { }); it('enables all popular networks from constants', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); // Mock all popular networks to be available in configurations - // eslint-disable-next-line dot-notation - jest.spyOn(controller['messenger'], 'call').mockImplementation( + jest.spyOn(messenger, 'call').mockImplementation( // eslint-disable-next-line @typescript-eslint/no-explicit-any (actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -965,12 +958,11 @@ describe('NetworkEnablementController', () => { }); it('disables existing networks and enables only popular networks (exclusive behavior)', async () => { - const { controller, rootMessenger } = setupController(); + const { controller, rootMessenger, messenger } = setupController(); // Mock the network configurations to include popular networks jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: any[]): any => { // eslint-disable-next-line jest/no-conditional-in-test @@ -1049,12 +1041,11 @@ describe('NetworkEnablementController', () => { }); it('enables Bitcoin mainnet when configured in MultichainNetworkController', () => { - const { controller } = setupController(); + const { controller, messenger } = setupController(); // Mock the network configurations to include Bitcoin jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messenger'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: unknown[]): any => { const responses = { From 41a89823e3863b8eac24a75b911a847ecef42d1c Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 8 Oct 2025 14:15:10 +0200 Subject: [PATCH 170/247] fix: Incorrect Messenger Passed to Providers --- .../src/MultichainAccountService.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index 875c8255721..424b5f49805 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.test.ts @@ -244,17 +244,17 @@ describe('MultichainAccountService', () => { // No `EVM_ACCOUNT_PROVIDER_NAME`, cause it's optional in this test. }; - const { mocks, rootMessenger } = setup({ + const { mocks, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_SOL_ACCOUNT_1], providerConfigs, }); expect(mocks.EvmAccountProvider.constructor).toHaveBeenCalledWith( - rootMessenger, + messenger, undefined, ); expect(mocks.SolAccountProvider.constructor).toHaveBeenCalledWith( - rootMessenger, + messenger, providerConfigs[SolAccountProvider.NAME], ); }); From ab370309515017d3d9c6abd1ced8f7eaa80fa416 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 13 Oct 2025 14:09:47 +0200 Subject: [PATCH 171/247] apply @mcmire suggestions --- .../src/ApprovalController.test.ts | 13 ++++++++----- .../base-controller/src/next/BaseController.test.ts | 10 +++++++--- .../src/TransactionControllerIntegration.test.ts | 8 ++++++-- .../src/utils/eip7702.test.ts | 6 ++++-- .../src/utils/feature-flags.test.ts | 6 ++++-- .../transaction-controller/src/utils/swaps.test.ts | 6 ++++-- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/approval-controller/src/ApprovalController.test.ts b/packages/approval-controller/src/ApprovalController.test.ts index 5afdfdf1898..652a87ba4f3 100644 --- a/packages/approval-controller/src/ApprovalController.test.ts +++ b/packages/approval-controller/src/ApprovalController.test.ts @@ -1327,7 +1327,7 @@ describe('approval controller', () => { }); it('updateRequestState', () => { - const { rootMessenger, approvalControllerMessenger } = getMessengers(); + const { approvalControllerMessenger } = getMessengers(); approvalController = new ApprovalController({ messenger: approvalControllerMessenger, @@ -1343,10 +1343,13 @@ describe('approval controller', () => { requestState: { foo: 'bar' }, }); - rootMessenger.call('ApprovalController:updateRequestState', { - id: 'foo', - requestState: { foo: 'foobar' }, - }); + approvalControllerMessenger.call( + 'ApprovalController:updateRequestState', + { + id: 'foo', + requestState: { foo: 'foobar' }, + }, + ); expect(approvalController.get('foo')?.requestState).toStrictEqual({ foo: 'foobar', diff --git a/packages/base-controller/src/next/BaseController.test.ts b/packages/base-controller/src/next/BaseController.test.ts index 7bd2339a50a..954d3277030 100644 --- a/packages/base-controller/src/next/BaseController.test.ts +++ b/packages/base-controller/src/next/BaseController.test.ts @@ -1,5 +1,9 @@ /* eslint-disable jest/no-export */ -import { Messenger } from '@metamask/messenger'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Json } from '@metamask/utils'; import type { Draft, Patch } from 'immer'; import * as sinon from 'sinon'; @@ -728,10 +732,10 @@ describe('BaseController', () => { it('should allow messaging between controllers', () => { // Construct root messenger const rootMessenger = new Messenger< - 'Root', + MockAnyNamespace, VisitorControllerActions | VisitorOverflowControllerActions, VisitorControllerEvents | VisitorOverflowControllerEvents - >({ namespace: 'Root' }); + >({ namespace: MOCK_ANY_NAMESPACE }); // Construct controller messengers, delegating to parent const visitorControllerMessenger = new Messenger< typeof visitorName, diff --git a/packages/transaction-controller/src/TransactionControllerIntegration.test.ts b/packages/transaction-controller/src/TransactionControllerIntegration.test.ts index d49269aff3f..4a4cafbfbdc 100644 --- a/packages/transaction-controller/src/TransactionControllerIntegration.test.ts +++ b/packages/transaction-controller/src/TransactionControllerIntegration.test.ts @@ -13,7 +13,9 @@ import { NetworkType, } from '@metamask/controller-utils'; import { + MOCK_ANY_NAMESPACE, Messenger, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -86,7 +88,7 @@ type AllEvents = | NetworkControllerEvents | ApprovalControllerEvents; -type RootMessenger = Messenger<'Root', AllActions, AllEvents>; +type RootMessenger = Messenger; const uuidV4Mock = jest.mocked(uuidV4); @@ -170,7 +172,9 @@ const setupController = async ( ], }); - const rootMessenger: RootMessenger = new Messenger({ namespace: 'Root' }); + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const networkControllerMessenger = new Messenger< 'NetworkController', diff --git a/packages/transaction-controller/src/utils/eip7702.test.ts b/packages/transaction-controller/src/utils/eip7702.test.ts index 82be470ca94..323ff64a120 100644 --- a/packages/transaction-controller/src/utils/eip7702.test.ts +++ b/packages/transaction-controller/src/utils/eip7702.test.ts @@ -2,8 +2,10 @@ import { query } from '@metamask/controller-utils'; import type EthQuery from '@metamask/eth-query'; import { Messenger, + MockAnyNamespace, type MessengerActions, type MessengerEvents, + MOCK_ANY_NAMESPACE, } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import { remove0x } from '@metamask/utils'; @@ -77,7 +79,7 @@ const AUTHORIZATION_LIST_MOCK: AuthorizationList = [ describe('EIP-7702 Utils', () => { let rootMessenger: Messenger< - 'Root', + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -98,7 +100,7 @@ describe('EIP-7702 Utils', () => { beforeEach(() => { jest.resetAllMocks(); - rootMessenger = new Messenger({ namespace: 'Root' }); + rootMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); signAuthorizationMock = jest .fn() diff --git a/packages/transaction-controller/src/utils/feature-flags.test.ts b/packages/transaction-controller/src/utils/feature-flags.test.ts index 652d3ec168e..1d1e8c91b93 100644 --- a/packages/transaction-controller/src/utils/feature-flags.test.ts +++ b/packages/transaction-controller/src/utils/feature-flags.test.ts @@ -1,5 +1,7 @@ import { + MOCK_ANY_NAMESPACE, Messenger, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -41,7 +43,7 @@ const GAS_BUFFER_5_MOCK = 1.5; describe('Feature Flags Utils', () => { let rootMessenger: Messenger< - 'Root', + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -78,7 +80,7 @@ describe('Feature Flags Utils', () => { getFeatureFlagsMock = jest.fn(); - rootMessenger = new Messenger({ namespace: 'Root' }); + rootMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); remoteFeatureFlagControllerMessenger = new Messenger({ namespace: 'RemoteFeatureFlagController', diff --git a/packages/transaction-controller/src/utils/swaps.test.ts b/packages/transaction-controller/src/utils/swaps.test.ts index 9f74710da54..147d9e9174a 100644 --- a/packages/transaction-controller/src/utils/swaps.test.ts +++ b/packages/transaction-controller/src/utils/swaps.test.ts @@ -1,6 +1,8 @@ import { query } from '@metamask/controller-utils'; import { + MOCK_ANY_NAMESPACE, Messenger, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -46,11 +48,11 @@ describe('updateSwapsTransaction', () => { }, }; const rootMessenger = new Messenger< - 'Root', + MockAnyNamespace, MessengerActions, MessengerEvents >({ - namespace: 'Root', + namespace: MOCK_ANY_NAMESPACE, }); messenger = new Messenger< 'TransactionController', From ca266fd47b191cf3ae9f63f1a6337adeffbde6d4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 13 Oct 2025 14:42:09 +0200 Subject: [PATCH 172/247] fix: migrate leftover `messagingSystem` to `messenger` --- packages/assets-controllers/src/TokenBalancesController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index 2a0c5af49be..a26bc1b64c3 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -359,13 +359,13 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ ); // Subscribe to AccountActivityService balance updates for real-time updates - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountActivityService:balanceUpdated', this.#onAccountActivityBalanceUpdate.bind(this), ); // Subscribe to AccountActivityService status changes for dynamic polling management - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountActivityService:statusChanged', this.#onAccountActivityStatusChanged.bind(this), ); From e9a7bbd6e932a1cd760d9747d8e0471ffa69b3d9 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 13 Oct 2025 15:05:26 +0200 Subject: [PATCH 173/247] add `BridgeControllerStateChangeEvent` to `index.ts` --- packages/bridge-controller/CHANGELOG.md | 2 +- packages/bridge-controller/src/index.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index 4ac772396f7..340317907af 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `BridgeControllerGetStateAction` type ([#6444](https://github.com/MetaMask/core/pull/6444)) +- Add `BridgeControllerGetStateAction` and `BridgeControllerStateChangeEvent` types ([#6444](https://github.com/MetaMask/core/pull/6444)) ### Changed diff --git a/packages/bridge-controller/src/index.ts b/packages/bridge-controller/src/index.ts index 3897bf9a6de..ac9fe235029 100644 --- a/packages/bridge-controller/src/index.ts +++ b/packages/bridge-controller/src/index.ts @@ -58,6 +58,7 @@ export { BridgeUserAction, BridgeBackgroundAction, type BridgeControllerGetStateAction, + type BridgeControllerStateChangeEvent, } from './types'; export { From d6a143394f748dfe7a18ae039aab0ca43b50131f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 13 Oct 2025 15:07:58 +0200 Subject: [PATCH 174/247] fix `bridge-status-controller/CHANGELOG` --- packages/bridge-status-controller/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index dde68b08b72..0380b005e08 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) - Previously, `BridgeStatusController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. From 1bc9e1fec5393c0254ccea56663153b0a939f1de Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 13 Oct 2025 15:51:33 +0200 Subject: [PATCH 175/247] remove polling-controller changelog entry --- packages/polling-controller/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/polling-controller/CHANGELOG.md b/packages/polling-controller/CHANGELOG.md index 4b1f45f2ff5..bc9a31b9efd 100644 --- a/packages/polling-controller/CHANGELOG.md +++ b/packages/polling-controller/CHANGELOG.md @@ -12,10 +12,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Use new `Messenger` from `@metamask/messenger` for `StaticIntervalPollingController` and `BlockTrackerPollingController` ([#6444](https://github.com/MetaMask/core/pull/6444)) - Previously, `StaticIntervalPollingController` and `BlockTrackerPollingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. -### Added - -- Add `StaticIntervalPollingControllerNext` to extend from `@metamask/base-controller/next` ([#6386](https://github.com/MetaMask/core/pull/6386)) - ## [14.0.1] ### Changed From daec53ef304601fa5fe21d5aa5dbdaa1e6a46113 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Mon, 13 Oct 2025 19:55:46 +0200 Subject: [PATCH 176/247] refactor: migrate {AccountActivity,BackendWebSocket}Service to @metamask/messenger --- packages/core-backend/CHANGELOG.md | 12 ++ packages/core-backend/package.json | 2 +- .../src/AccountActivityService.test.ts | 110 +++++++++++++----- .../src/AccountActivityService.ts | 34 +----- .../src/BackendWebSocketService.test.ts | 67 +++++++---- .../src/BackendWebSocketService.ts | 20 ++-- packages/core-backend/src/index.ts | 8 -- packages/core-backend/tsconfig.build.json | 2 +- packages/core-backend/tsconfig.json | 4 +- yarn.lock | 2 +- 10 files changed, 160 insertions(+), 101 deletions(-) diff --git a/packages/core-backend/CHANGELOG.md b/packages/core-backend/CHANGELOG.md index e2cf6ea43be..82d02555cd6 100644 --- a/packages/core-backend/CHANGELOG.md +++ b/packages/core-backend/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6823](https://github.com/MetaMask/core/pull/6823)) + - Previously, `AccountActivityService` and `BackendWebSocketService` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + +### Removed + +- **BREAKING:** Remove exported type aliases and constants that were specific to controller messenger integration ([#6823](https://github.com/MetaMask/core/pull/6823)) + - Removed type exports: `BackendWebSocketServiceAllowedActions`, `BackendWebSocketServiceAllowedEvents`, `AccountActivityServiceAllowedActions`, `AccountActivityServiceAllowedEvents` + - Removed constant exports: `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS`, `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS` + - These types and constants were internal implementation details that should not have been exposed. Consumers should use the service-specific messenger types directly. + ## [1.0.1] ### Changed diff --git a/packages/core-backend/package.json b/packages/core-backend/package.json index 6ce574279fa..a3cf11b9cd4 100644 --- a/packages/core-backend/package.json +++ b/packages/core-backend/package.json @@ -47,8 +47,8 @@ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { - "@metamask/base-controller": "^8.4.1", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/profile-sync-controller": "^25.1.1", "@metamask/utils": "^11.8.1", "uuid": "^8.3.2" diff --git a/packages/core-backend/src/AccountActivityService.test.ts b/packages/core-backend/src/AccountActivityService.test.ts index c24a1a831a2..67aff284a4a 100644 --- a/packages/core-backend/src/AccountActivityService.test.ts +++ b/packages/core-backend/src/AccountActivityService.test.ts @@ -1,18 +1,18 @@ -import { Messenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import nock, { isDone } from 'nock'; -import type { - AccountActivityServiceAllowedEvents, - AccountActivityServiceAllowedActions, -} from './AccountActivityService'; import { AccountActivityService, type AccountActivityServiceMessenger, type SubscriptionOptions, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS, } from './AccountActivityService'; import type { ServerNotificationMessage } from './BackendWebSocketService'; import { WebSocketState } from './BackendWebSocketService'; @@ -20,6 +20,18 @@ import type { Transaction, BalanceUpdate } from './types'; import type { AccountActivityMessage } from './types'; import { flushPromises } from '../../../tests/helpers'; +type AllAccountActivityServiceActions = + MessengerActions; + +type AllAccountActivityServiceEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllAccountActivityServiceActions, + AllAccountActivityServiceEvents +>; + // Helper function for completing async operations const completeAsyncOperations = async (timeoutMs = 0) => { await flushPromises(); @@ -49,25 +61,70 @@ const createMockInternalAccount = (options: { scopes: ['eip155:1'], // Required scopes property }); +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Creates a real messenger with registered mock actions for testing * Each call creates a completely independent messenger to ensure test isolation * * @returns Object containing the messenger and mock action functions */ -const getMessenger = () => { +const getMessenger = (): { + rootMessenger: RootMessenger; + messenger: AccountActivityServiceMessenger; + mocks: { + getSelectedAccount: jest.Mock; + connect: jest.Mock; + disconnect: jest.Mock; + subscribe: jest.Mock; + channelHasSubscription: jest.Mock; + getSubscriptionsByChannel: jest.Mock; + findSubscriptionsByChannelPrefix: jest.Mock; + addChannelCallback: jest.Mock; + removeChannelCallback: jest.Mock; + }; +} => { // Use any types for the root messenger to avoid complex type constraints in tests // Create a unique root messenger for each test - const rootMessenger = new Messenger< - AccountActivityServiceAllowedActions, - AccountActivityServiceAllowedEvents - >(); - const messenger: AccountActivityServiceMessenger = - rootMessenger.getRestricted({ - name: 'AccountActivityService', - allowedActions: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS], - allowedEvents: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS], - }); + const rootMessenger = getRootMessenger(); + const messenger: AccountActivityServiceMessenger = new Messenger< + 'AccountActivityService', + AllAccountActivityServiceActions, + AllAccountActivityServiceEvents, + RootMessenger + >({ + namespace: 'AccountActivityService', + parent: rootMessenger, + }); + + rootMessenger.delegate({ + actions: [ + 'AccountsController:getSelectedAccount', + 'BackendWebSocketService:connect', + 'BackendWebSocketService:disconnect', + 'BackendWebSocketService:subscribe', + 'BackendWebSocketService:getConnectionInfo', + 'BackendWebSocketService:channelHasSubscription', + 'BackendWebSocketService:getSubscriptionsByChannel', + 'BackendWebSocketService:findSubscriptionsByChannelPrefix', + 'BackendWebSocketService:addChannelCallback', + 'BackendWebSocketService:removeChannelCallback', + ], + events: [ + 'AccountsController:selectedAccountChange', + 'BackendWebSocketService:connectionStateChanged', + ], + messenger, + }); // Create mock action handlers const mockGetSelectedAccount = jest.fn(); @@ -216,10 +273,7 @@ type WithServiceOptions = { type WithServiceCallback = (payload: { service: AccountActivityService; messenger: AccountActivityServiceMessenger; - rootMessenger: Messenger< - AccountActivityServiceAllowedActions, - AccountActivityServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getSelectedAccount: jest.Mock; connect: jest.Mock; @@ -633,7 +687,7 @@ describe('AccountActivityService', () => { }); // Publish WebSocket ERROR state event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.ERROR, @@ -686,7 +740,7 @@ describe('AccountActivityService', () => { }); // Publish account change event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', solanaAccount, ); @@ -716,7 +770,7 @@ describe('AccountActivityService', () => { }); // Publish account change event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', unknownAccount, ); @@ -738,7 +792,7 @@ describe('AccountActivityService', () => { mocks.getSelectedAccount.mockReturnValue(null); // Publish WebSocket connection event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.CONNECTED, @@ -805,7 +859,7 @@ describe('AccountActivityService', () => { }); // Publish account change event on root messenger - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', newAccount, ); @@ -843,7 +897,7 @@ describe('AccountActivityService', () => { }); // Publish account change event on root messenger - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', newAccount, ); diff --git a/packages/core-backend/src/AccountActivityService.ts b/packages/core-backend/src/AccountActivityService.ts index 8b460bf48e6..bac0c19a490 100644 --- a/packages/core-backend/src/AccountActivityService.ts +++ b/packages/core-backend/src/AccountActivityService.ts @@ -9,8 +9,8 @@ import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent, } from '@metamask/accounts-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types'; import type { @@ -119,27 +119,7 @@ export type AccountActivityServiceOptions = { // Action types for the messaging system - using generated method actions export type AccountActivityServiceActions = AccountActivityServiceMethodActions; -// Allowed actions that AccountActivityService can call on other controllers -export const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = [ - 'AccountsController:getSelectedAccount', - 'BackendWebSocketService:connect', - 'BackendWebSocketService:disconnect', - 'BackendWebSocketService:subscribe', - 'BackendWebSocketService:getConnectionInfo', - 'BackendWebSocketService:channelHasSubscription', - 'BackendWebSocketService:getSubscriptionsByChannel', - 'BackendWebSocketService:findSubscriptionsByChannelPrefix', - 'BackendWebSocketService:addChannelCallback', - 'BackendWebSocketService:removeChannelCallback', -] as const; - -// Allowed events that AccountActivityService can listen to -export const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [ - 'AccountsController:selectedAccountChange', - 'BackendWebSocketService:connectionStateChanged', -] as const; - -export type AccountActivityServiceAllowedActions = +type AllowedActions = | AccountsControllerGetSelectedAccountAction | BackendWebSocketServiceMethodActions; @@ -176,16 +156,14 @@ export type AccountActivityServiceEvents = | AccountActivityServiceSubscriptionErrorEvent | AccountActivityServiceStatusChangedEvent; -export type AccountActivityServiceAllowedEvents = +export type AllowedEvents = | AccountsControllerSelectedAccountChangeEvent | BackendWebSocketServiceConnectionStateChangedEvent; -export type AccountActivityServiceMessenger = RestrictedMessenger< +export type AccountActivityServiceMessenger = Messenger< typeof SERVICE_NAME, - AccountActivityServiceActions | AccountActivityServiceAllowedActions, - AccountActivityServiceEvents | AccountActivityServiceAllowedEvents, - AccountActivityServiceAllowedActions['type'], - AccountActivityServiceAllowedEvents['type'] + AccountActivityServiceActions | AllowedActions, + AccountActivityServiceEvents | AllowedEvents >; // ============================================================================= diff --git a/packages/core-backend/src/BackendWebSocketService.test.ts b/packages/core-backend/src/BackendWebSocketService.test.ts index 1c848c4034a..9e96be09c00 100644 --- a/packages/core-backend/src/BackendWebSocketService.test.ts +++ b/packages/core-backend/src/BackendWebSocketService.test.ts @@ -1,4 +1,10 @@ -import { Messenger } from '@metamask/base-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { BackendWebSocketService, @@ -6,8 +12,6 @@ import { WebSocketState, type BackendWebSocketServiceOptions, type BackendWebSocketServiceMessenger, - type BackendWebSocketServiceAllowedActions, - type BackendWebSocketServiceAllowedEvents, } from './BackendWebSocketService'; import { flushPromises } from '../../../tests/helpers'; @@ -18,6 +22,18 @@ import { flushPromises } from '../../../tests/helpers'; // Type for global object with WebSocket mock type GlobalWithWebSocket = typeof global & { lastWebSocket: MockWebSocket }; +type AllBackendWebSocketServiceActions = + MessengerActions; + +type AllBackendWebSocketServiceEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllBackendWebSocketServiceActions, + AllBackendWebSocketServiceEvents +>; + // ===================================================== // MOCK WEBSOCKET CLASS // ===================================================== @@ -149,6 +165,17 @@ class MockWebSocket extends EventTarget { // TEST UTILITIES & MOCKS // ===================================================== +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** * Creates a real messenger with registered mock actions for testing * Each call creates a completely independent messenger to ensure test isolation @@ -157,15 +184,21 @@ class MockWebSocket extends EventTarget { */ const getMessenger = () => { // Create a unique root messenger for each test - const rootMessenger = new Messenger< - BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceAllowedEvents - >(); - const messenger = rootMessenger.getRestricted({ - name: 'BackendWebSocketService', - allowedActions: ['AuthenticationController:getBearerToken'], - allowedEvents: ['AuthenticationController:stateChange'], - }) as unknown as BackendWebSocketServiceMessenger; + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< + 'BackendWebSocketService', + AllBackendWebSocketServiceActions, + AllBackendWebSocketServiceEvents, + RootMessenger + >({ + namespace: 'BackendWebSocketService', + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: ['AuthenticationController:getBearerToken'], + events: ['AuthenticationController:stateChange'], + messenger, + }); // Create mock action handlers const mockGetBearerToken = jest.fn().mockResolvedValue('valid-default-token'); @@ -234,10 +267,7 @@ type TestSetupOptions = { type TestSetup = { service: BackendWebSocketService; messenger: BackendWebSocketServiceMessenger; - rootMessenger: Messenger< - BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getBearerToken: jest.Mock; }; @@ -256,10 +286,7 @@ type TestSetup = { type WithServiceCallback = (payload: { service: BackendWebSocketService; messenger: BackendWebSocketServiceMessenger; - rootMessenger: Messenger< - BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getBearerToken: jest.Mock; }; diff --git a/packages/core-backend/src/BackendWebSocketService.ts b/packages/core-backend/src/BackendWebSocketService.ts index 16664af3a3a..527fd5e927d 100644 --- a/packages/core-backend/src/BackendWebSocketService.ts +++ b/packages/core-backend/src/BackendWebSocketService.ts @@ -1,4 +1,4 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; import { getErrorMessage } from '@metamask/utils'; import { v4 as uuidV4 } from 'uuid'; @@ -211,13 +211,11 @@ export type WebSocketConnectionInfo = { export type BackendWebSocketServiceActions = BackendWebSocketServiceMethodActions; -export type BackendWebSocketServiceAllowedActions = - | AuthenticationController.AuthenticationControllerGetBearerToken - | BackendWebSocketServiceMethodActions; +type AllowedActions = + AuthenticationController.AuthenticationControllerGetBearerToken; -export type BackendWebSocketServiceAllowedEvents = - | AuthenticationController.AuthenticationControllerStateChangeEvent - | BackendWebSocketServiceConnectionStateChangedEvent; +type AllowedEvents = + AuthenticationController.AuthenticationControllerStateChangeEvent; // Event types for WebSocket connection state changes export type BackendWebSocketServiceConnectionStateChangedEvent = { @@ -228,12 +226,10 @@ export type BackendWebSocketServiceConnectionStateChangedEvent = { export type BackendWebSocketServiceEvents = BackendWebSocketServiceConnectionStateChangedEvent; -export type BackendWebSocketServiceMessenger = RestrictedMessenger< +export type BackendWebSocketServiceMessenger = Messenger< typeof SERVICE_NAME, - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents, - BackendWebSocketServiceAllowedActions['type'], - BackendWebSocketServiceAllowedEvents['type'] + BackendWebSocketServiceActions | AllowedActions, + BackendWebSocketServiceEvents | AllowedEvents >; /** diff --git a/packages/core-backend/src/index.ts b/packages/core-backend/src/index.ts index 4831e4569f2..e77bc517a75 100644 --- a/packages/core-backend/src/index.ts +++ b/packages/core-backend/src/index.ts @@ -19,8 +19,6 @@ export type { WebSocketConnectionInfo, WebSocketSubscription, BackendWebSocketServiceActions, - BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceAllowedEvents, BackendWebSocketServiceMessenger, BackendWebSocketServiceEvents, BackendWebSocketServiceConnectionStateChangedEvent, @@ -34,8 +32,6 @@ export type { SubscriptionOptions, AccountActivityServiceOptions, AccountActivityServiceActions, - AccountActivityServiceAllowedActions, - AccountActivityServiceAllowedEvents, AccountActivityServiceTransactionUpdatedEvent, AccountActivityServiceBalanceUpdatedEvent, AccountActivityServiceSubscriptionErrorEvent, @@ -43,8 +39,4 @@ export type { AccountActivityServiceEvents, AccountActivityServiceMessenger, } from './AccountActivityService'; -export { - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS, -} from './AccountActivityService'; export { AccountActivityService } from './AccountActivityService'; diff --git a/packages/core-backend/tsconfig.build.json b/packages/core-backend/tsconfig.build.json index f4d2ea7f933..83b256b2baf 100644 --- a/packages/core-backend/tsconfig.build.json +++ b/packages/core-backend/tsconfig.build.json @@ -7,8 +7,8 @@ }, "references": [ { "path": "../accounts-controller/tsconfig.build.json" }, - { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, { "path": "../profile-sync-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/core-backend/tsconfig.json b/packages/core-backend/tsconfig.json index 66c601646f5..d12734f44ad 100644 --- a/packages/core-backend/tsconfig.json +++ b/packages/core-backend/tsconfig.json @@ -10,10 +10,10 @@ "path": "../accounts-controller" }, { - "path": "../base-controller" + "path": "../controller-utils" }, { - "path": "../controller-utils" + "path": "../messenger" }, { "path": "../profile-sync-controller" diff --git a/yarn.lock b/yarn.lock index cbe0fb8b6e1..c20a31d34ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2926,8 +2926,8 @@ __metadata: dependencies: "@metamask/accounts-controller": "npm:^33.1.1" "@metamask/auto-changelog": "npm:^3.4.4" - "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/profile-sync-controller": "npm:^25.1.1" "@metamask/utils": "npm:^11.8.1" "@ts-bridge/cli": "npm:^0.6.1" From 7fe630b3824db71d71d87a35c13592f0907dbff1 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 15 Oct 2025 11:13:25 +0200 Subject: [PATCH 177/247] run dedupe --- yarn.lock | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2fda527a91c..7a56bd63403 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5056,26 +5056,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0": - version: 11.8.0 - resolution: "@metamask/utils@npm:11.8.0" - dependencies: - "@ethereumjs/tx": "npm:^4.2.0" - "@metamask/superstruct": "npm:^3.1.0" - "@noble/hashes": "npm:^1.3.1" - "@scure/base": "npm:^1.1.3" - "@types/debug": "npm:^4.1.7" - "@types/lodash": "npm:^4.17.20" - debug: "npm:^4.3.4" - lodash: "npm:^4.17.21" - pony-cause: "npm:^2.1.10" - semver: "npm:^7.5.4" - uuid: "npm:^9.0.1" - checksum: 10/d5a9d8c04223fc62b0d4a078b505e062f5d1d47e752df36802189bec19a9e68aee7a9b0df9b15e7e6fa15fd9d65f61c7e4909604209dddc21f0943cd9a2fd5d1 - languageName: node - linkType: hard - -"@metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": +"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0, @metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": version: 11.8.1 resolution: "@metamask/utils@npm:11.8.1" dependencies: From 80e5d0100b76007d141c1f814b1a6d7df8642c06 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 15 Oct 2025 11:17:55 +0200 Subject: [PATCH 178/247] dedupe packages --- yarn.lock | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1baf94b0fc9..78482b192b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5056,26 +5056,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0": - version: 11.8.0 - resolution: "@metamask/utils@npm:11.8.0" - dependencies: - "@ethereumjs/tx": "npm:^4.2.0" - "@metamask/superstruct": "npm:^3.1.0" - "@noble/hashes": "npm:^1.3.1" - "@scure/base": "npm:^1.1.3" - "@types/debug": "npm:^4.1.7" - "@types/lodash": "npm:^4.17.20" - debug: "npm:^4.3.4" - lodash: "npm:^4.17.21" - pony-cause: "npm:^2.1.10" - semver: "npm:^7.5.4" - uuid: "npm:^9.0.1" - checksum: 10/d5a9d8c04223fc62b0d4a078b505e062f5d1d47e752df36802189bec19a9e68aee7a9b0df9b15e7e6fa15fd9d65f61c7e4909604209dddc21f0943cd9a2fd5d1 - languageName: node - linkType: hard - -"@metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": +"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0, @metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": version: 11.8.1 resolution: "@metamask/utils@npm:11.8.1" dependencies: From 3c427e7fc1640c71d91dcba9ab169c9926502d22 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 15 Oct 2025 11:18:42 +0200 Subject: [PATCH 179/247] dedupe packages --- yarn.lock | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/yarn.lock b/yarn.lock index 74aa0cd5141..affa6350cbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5056,26 +5056,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0": - version: 11.8.0 - resolution: "@metamask/utils@npm:11.8.0" - dependencies: - "@ethereumjs/tx": "npm:^4.2.0" - "@metamask/superstruct": "npm:^3.1.0" - "@noble/hashes": "npm:^1.3.1" - "@scure/base": "npm:^1.1.3" - "@types/debug": "npm:^4.1.7" - "@types/lodash": "npm:^4.17.20" - debug: "npm:^4.3.4" - lodash: "npm:^4.17.21" - pony-cause: "npm:^2.1.10" - semver: "npm:^7.5.4" - uuid: "npm:^9.0.1" - checksum: 10/d5a9d8c04223fc62b0d4a078b505e062f5d1d47e752df36802189bec19a9e68aee7a9b0df9b15e7e6fa15fd9d65f61c7e4909604209dddc21f0943cd9a2fd5d1 - languageName: node - linkType: hard - -"@metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": +"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0, @metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": version: 11.8.1 resolution: "@metamask/utils@npm:11.8.1" dependencies: From 393e93506296fb2008decf9d00505101f7a669bc Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 15 Oct 2025 12:22:19 +0200 Subject: [PATCH 180/247] dedupe packages --- yarn.lock | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/yarn.lock b/yarn.lock index 14dd8678a26..bd4b3f54b10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5056,26 +5056,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0": - version: 11.8.0 - resolution: "@metamask/utils@npm:11.8.0" - dependencies: - "@ethereumjs/tx": "npm:^4.2.0" - "@metamask/superstruct": "npm:^3.1.0" - "@noble/hashes": "npm:^1.3.1" - "@scure/base": "npm:^1.1.3" - "@types/debug": "npm:^4.1.7" - "@types/lodash": "npm:^4.17.20" - debug: "npm:^4.3.4" - lodash: "npm:^4.17.21" - pony-cause: "npm:^2.1.10" - semver: "npm:^7.5.4" - uuid: "npm:^9.0.1" - checksum: 10/d5a9d8c04223fc62b0d4a078b505e062f5d1d47e752df36802189bec19a9e68aee7a9b0df9b15e7e6fa15fd9d65f61c7e4909604209dddc21f0943cd9a2fd5d1 - languageName: node - linkType: hard - -"@metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": +"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.4.0, @metamask/utils@npm:^11.7.0, @metamask/utils@npm:^11.8.1": version: 11.8.1 resolution: "@metamask/utils@npm:11.8.1" dependencies: From 890f59d77bc6dcdddbd0a9d9572b512c25f9e187 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 15:27:54 +0200 Subject: [PATCH 181/247] fix: changelog duplicate entries --- packages/shield-controller/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/shield-controller/CHANGELOG.md b/packages/shield-controller/CHANGELOG.md index aead03c8b2a..31b4c903b3b 100644 --- a/packages/shield-controller/CHANGELOG.md +++ b/packages/shield-controller/CHANGELOG.md @@ -41,8 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6497](https://github.com/MetaMask/core/pull/6497)) - - Previously, `ShieldController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.8.0` to `^11.8.1` ([#6708](https://github.com/MetaMask/core/pull/6708)) - **Breaking:** Change `checkCoverage` API to accept `coverageId` and skip `/init` if `coverageId` is provided ([#6792](https://github.com/MetaMask/core/pull/6792)) From a115dc8d09d05f92af0b2b1c089f82b3988e0389 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 15:31:24 +0200 Subject: [PATCH 182/247] fix: setupCoverageResultReceived --- .../src/ShieldController.test.ts | 15 +++++++-------- packages/shield-controller/tests/utils.ts | 11 ++++------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/shield-controller/src/ShieldController.test.ts b/packages/shield-controller/src/ShieldController.test.ts index cc9cdd2a8b1..1646794df0d 100644 --- a/packages/shield-controller/src/ShieldController.test.ts +++ b/packages/shield-controller/src/ShieldController.test.ts @@ -55,9 +55,9 @@ function setup({ describe('ShieldController', () => { describe('checkCoverage', () => { it('should trigger checkCoverage when a new transaction is added', async () => { - const { rootMessenger, backend } = setup(); + const { rootMessenger, messenger, backend } = setup(); const txMeta = generateMockTxMeta(); - const coverageResultReceived = setupCoverageResultReceived(rootMessenger); + const coverageResultReceived = setupCoverageResultReceived(messenger); rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, @@ -68,13 +68,13 @@ describe('ShieldController', () => { }); it('should tolerate calling start and stop multiple times', async () => { - const { backend, rootMessenger, controller } = setup(); + const { backend, rootMessenger, messenger, controller } = setup(); controller.stop(); controller.stop(); controller.start(); controller.start(); const txMeta = generateMockTxMeta(); - const coverageResultReceived = setupCoverageResultReceived(rootMessenger); + const coverageResultReceived = setupCoverageResultReceived(messenger); rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, @@ -137,9 +137,9 @@ describe('ShieldController', () => { }); it('should check coverage when a transaction is simulated', async () => { - const { rootMessenger, backend } = setup(); + const { rootMessenger, messenger, backend } = setup(); const txMeta = generateMockTxMeta(); - const coverageResultReceived = setupCoverageResultReceived(rootMessenger); + const coverageResultReceived = setupCoverageResultReceived(messenger); // Add transaction. rootMessenger.publish( @@ -155,8 +155,7 @@ describe('ShieldController', () => { txMeta2.simulationData = { tokenBalanceChanges: [], }; - const coverageResultReceived2 = - setupCoverageResultReceived(rootMessenger); + const coverageResultReceived2 = setupCoverageResultReceived(messenger); rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta2] } as TransactionControllerState, diff --git a/packages/shield-controller/tests/utils.ts b/packages/shield-controller/tests/utils.ts index 39eab1b797b..737caf03a57 100644 --- a/packages/shield-controller/tests/utils.ts +++ b/packages/shield-controller/tests/utils.ts @@ -82,20 +82,17 @@ export function getRandomCoverageResult() { /** * Setup a coverage result received handler. * - * @param rootMessenger - The root messenger. + * @param messenger - The controller messenger. * @returns A promise that resolves when the coverage result is received. */ export function setupCoverageResultReceived( - rootMessenger: ReturnType['rootMessenger'], + messenger: ReturnType['messenger'], ): Promise { return new Promise((resolve) => { const handler = (_coverageResult: unknown) => { - rootMessenger.unsubscribe( - 'ShieldController:coverageResultReceived', - handler, - ); + messenger.unsubscribe('ShieldController:coverageResultReceived', handler); resolve(); }; - rootMessenger.subscribe('ShieldController:coverageResultReceived', handler); + messenger.subscribe('ShieldController:coverageResultReceived', handler); }); } From 07361124f31279f23efea7ccdb0024be37ab2d04 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 15:49:16 +0200 Subject: [PATCH 183/247] fix: unit tests move RemoteFeatureFlagControllerState --- .../src/remote-feature-flag-controller.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts index ae390caed34..e9a2439955b 100644 --- a/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts +++ b/packages/remote-feature-flag-controller/src/remote-feature-flag-controller.test.ts @@ -13,11 +13,11 @@ import { DEFAULT_CACHE_DURATION, getDefaultRemoteFeatureFlagControllerState, } from './remote-feature-flag-controller'; -import type { RemoteFeatureFlagControllerMessenger } from './remote-feature-flag-controller'; import type { - FeatureFlags, + RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerState, -} from './remote-feature-flag-controller-types'; +} from './remote-feature-flag-controller'; +import type { FeatureFlags } from './remote-feature-flag-controller-types'; const controllerName = 'RemoteFeatureFlagController'; From 6b692a0f9e707a987b58dd1fa90c7788cc67c9d0 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 16:07:41 +0200 Subject: [PATCH 184/247] fix: UserStorageController tests --- .../controllers/user-storage/UserStorageController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts index 54d70c58800..5c79d3a97e8 100644 --- a/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts +++ b/packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type nock from 'nock'; import { mockUserStorageMessenger } from './__fixtures__/mockMessenger'; @@ -766,7 +766,7 @@ describe('metadata', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { From 141ad75fb20b890565f870182cc63360bed698df Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 16:21:16 +0200 Subject: [PATCH 185/247] fix: PhishingController tests --- packages/phishing-controller/src/PhishingController.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/phishing-controller/src/PhishingController.test.ts b/packages/phishing-controller/src/PhishingController.test.ts index b22e2e6c017..52dce0de2b8 100644 --- a/packages/phishing-controller/src/PhishingController.test.ts +++ b/packages/phishing-controller/src/PhishingController.test.ts @@ -45,7 +45,8 @@ type AllPhishingControllerEvents = MessengerEvents; type RootMessenger = Messenger< MockAnyNamespace, AllPhishingControllerActions, - AllPhishingControllerEvents + AllPhishingControllerEvents, + RootMessenger >; /** From 02ccdb550dfa39cc8b2df4687273c9111505584f Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 16:30:51 +0200 Subject: [PATCH 186/247] fix: changelog --- packages/network-enablement-controller/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/network-enablement-controller/CHANGELOG.md b/packages/network-enablement-controller/CHANGELOG.md index 11bdd5f5cd6..121501f1ccf 100644 --- a/packages/network-enablement-controller/CHANGELOG.md +++ b/packages/network-enablement-controller/CHANGELOG.md @@ -12,6 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6540](https://github.com/MetaMask/core/pull/6540)) - Previously, `NetworkEnablementController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +## [2.1.1] + +### Changed + +- Bump `@metamask/base-controller` from `^8.4.0` to `^8.4.1` ([#6807](https://github.com/MetaMask/core/pull/6807)) +- Bump `@metamask/controller-utils` from `^11.14.0` to `^11.14.1` ([#6807](https://github.com/MetaMask/core/pull/6807)) + ## [2.1.0] ### Added From 3e1240ae3bc57fa1d68bac75e24e6897289ec714 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 16:36:24 +0200 Subject: [PATCH 187/247] fix: Bulk Token Scanning tests --- .../src/BulkTokenScan.test.ts | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/packages/phishing-controller/src/BulkTokenScan.test.ts b/packages/phishing-controller/src/BulkTokenScan.test.ts index 9f84752578a..1773fd75de4 100644 --- a/packages/phishing-controller/src/BulkTokenScan.test.ts +++ b/packages/phishing-controller/src/BulkTokenScan.test.ts @@ -1,13 +1,17 @@ -import { Messenger } from '@metamask/base-controller'; import { safelyExecuteWithTimeout } from '@metamask/controller-utils'; -import type { TransactionControllerStateChangeEvent } from '@metamask/transaction-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import nock, { cleanAll } from 'nock'; import sinon from 'sinon'; -import type { PhishingControllerEvents } from './PhishingController'; +import type { PhishingControllerMessenger } from './PhishingController'; import { PhishingController, - type PhishingControllerActions, type PhishingControllerOptions, SECURITY_ALERTS_BASE_URL, TOKEN_BULK_SCANNING_ENDPOINT, @@ -30,24 +34,55 @@ const mockSafelyExecuteWithTimeout = const controllerName = 'PhishingController'; +type AllPhishingControllerActions = + MessengerActions; + +type AllPhishingControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllPhishingControllerActions, + AllPhishingControllerEvents, + RootMessenger +>; + +/** + * Creates and returns a root messenger for testing + * + * @returns A messenger instance + */ +function getRootMessenger(): RootMessenger { + return new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); +} + /** - * Constructs a restricted messenger with transaction events enabled. + * Constructs a messenger with transaction events enabled. * * @returns A restricted messenger that can listen to TransactionController events. */ -function getRestrictedMessengerWithTransactionEvents() { +function getMessengerWithTransactionEvents() { + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< - PhishingControllerActions, - PhishingControllerEvents | TransactionControllerStateChangeEvent - >(); + typeof controllerName, + AllPhishingControllerActions, + AllPhishingControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + + rootMessenger.delegate({ + actions: [], + events: ['TransactionController:stateChange'], + messenger, + }); return { - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: ['TransactionController:stateChange'], - }), - globalMessenger: messenger, + messenger, }; } @@ -59,7 +94,7 @@ function getRestrictedMessengerWithTransactionEvents() { */ function getPhishingController(options?: Partial) { return new PhishingController({ - messenger: getRestrictedMessengerWithTransactionEvents().messenger, + messenger: getMessengerWithTransactionEvents().messenger, ...options, }); } From 3619e829ea82e638ea0ce246d492bd52a51f40e6 Mon Sep 17 00:00:00 2001 From: Salah-Eddine Saakoun Date: Wed, 15 Oct 2025 17:57:44 +0200 Subject: [PATCH 188/247] fix: rollback index export refactoring --- packages/message-manager/src/index.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/message-manager/src/index.ts b/packages/message-manager/src/index.ts index fae5514ea32..27723685451 100644 --- a/packages/message-manager/src/index.ts +++ b/packages/message-manager/src/index.ts @@ -1,8 +1,4 @@ export * from './AbstractMessageManager'; export * from './EncryptionPublicKeyManager'; export * from './DecryptMessageManager'; -export type { - SignTypedDataMessageV3V4, - PersonalMessageParams, - TypedMessageParams, -} from './types'; +export * from './types'; From 534d5bb3439ce32af831bac33653d034d4393b44 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 12:42:22 +0200 Subject: [PATCH 189/247] rename `anonymous` to `includeInDebugSnapshot` --- packages/base-controller/src/next/BaseController.ts | 2 +- packages/composable-controller/src/ComposableController.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/base-controller/src/next/BaseController.ts b/packages/base-controller/src/next/BaseController.ts index cf4dafe793c..7d2c0059ae2 100644 --- a/packages/base-controller/src/next/BaseController.ts +++ b/packages/base-controller/src/next/BaseController.ts @@ -112,7 +112,7 @@ export type StateDeriverConstraint = (value: never) => Json; * This type can be assigned to any `StatePropertyMetadata` type. */ export type StatePropertyMetadataConstraint = { - anonymous: boolean | StateDeriverConstraint; + includeInDebugSnapshot: boolean | StateDeriverConstraint; includeInStateLogs?: boolean | StateDeriverConstraint; persist: boolean | StateDeriverConstraint; usedInUi?: boolean; diff --git a/packages/composable-controller/src/ComposableController.ts b/packages/composable-controller/src/ComposableController.ts index 04be89d88fb..06421a64110 100644 --- a/packages/composable-controller/src/ComposableController.ts +++ b/packages/composable-controller/src/ComposableController.ts @@ -145,7 +145,7 @@ export class ComposableController< (metadata as StateMetadataConstraint)[name] = { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }; return metadata; From ec56365f0a2add4e3c16e8e4e185f1a05f25c678 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 12:52:29 +0200 Subject: [PATCH 190/247] update metadata tests --- .../src/ComposableController.test.ts | 158 ++++++++++++------ 1 file changed, 103 insertions(+), 55 deletions(-) diff --git a/packages/composable-controller/src/ComposableController.test.ts b/packages/composable-controller/src/ComposableController.test.ts index 6058e7287c7..7b67a1e7d12 100644 --- a/packages/composable-controller/src/ComposableController.test.ts +++ b/packages/composable-controller/src/ComposableController.test.ts @@ -56,7 +56,9 @@ type FooMessenger = Messenger< const fooControllerStateMetadata = { foo: { persist: true, - anonymous: true, + includeInDebugSnapshot: true, + usedInUi: false, + includeInStateLogs: false, }, }; @@ -103,7 +105,9 @@ type QuzMessenger = Messenger< const quzControllerStateMetadata = { quz: { persist: true, - anonymous: true, + includeInDebugSnapshot: true, + usedInUi: false, + includeInStateLogs: false, }, }; @@ -428,24 +432,35 @@ describe('ComposableController', () => { FooController: FooControllerState; }; const messenger = new Messenger< - never, + MockAnyNamespace, + | ComposableControllerActions + | FooControllerAction, | ComposableControllerEvents | FooControllerEvent - >(); - const fooMessenger = messenger.getRestricted< + >({ namespace: MOCK_ANY_NAMESPACE }); + const fooControllerMessenger = new Messenger< 'FooController', - never, - never + FooControllerAction, + FooControllerEvent, + typeof messenger >({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + namespace: 'FooController', + parent: messenger, }); - const fooController = new FooController(fooMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], + const fooController = new FooController(fooControllerMessenger); + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], }); const controller = new ComposableController< ComposableControllerState, @@ -461,7 +476,7 @@ describe('ComposableController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -477,24 +492,35 @@ describe('ComposableController', () => { FooController: FooControllerState; }; const messenger = new Messenger< - never, + MockAnyNamespace, + | ComposableControllerActions + | FooControllerAction, | ComposableControllerEvents | FooControllerEvent - >(); - const fooMessenger = messenger.getRestricted< + >({ namespace: MOCK_ANY_NAMESPACE }); + const fooControllerMessenger = new Messenger< 'FooController', - never, - never + FooControllerAction, + FooControllerEvent, + typeof messenger >({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + namespace: 'FooController', + parent: messenger, }); - const fooController = new FooController(fooMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], + const fooController = new FooController(fooControllerMessenger); + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], }); const controller = new ComposableController< ComposableControllerState, @@ -520,24 +546,35 @@ describe('ComposableController', () => { FooController: FooControllerState; }; const messenger = new Messenger< - never, + MockAnyNamespace, + | ComposableControllerActions + | FooControllerAction, | ComposableControllerEvents | FooControllerEvent - >(); - const fooMessenger = messenger.getRestricted< + >({ namespace: MOCK_ANY_NAMESPACE }); + const fooControllerMessenger = new Messenger< 'FooController', - never, - never + FooControllerAction, + FooControllerEvent, + typeof messenger >({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + namespace: 'FooController', + parent: messenger, }); - const fooController = new FooController(fooMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], + const fooController = new FooController(fooControllerMessenger); + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], }); const controller = new ComposableController< ComposableControllerState, @@ -569,24 +606,35 @@ describe('ComposableController', () => { FooController: FooControllerState; }; const messenger = new Messenger< - never, + MockAnyNamespace, + | ComposableControllerActions + | FooControllerAction, | ComposableControllerEvents | FooControllerEvent - >(); - const fooMessenger = messenger.getRestricted< + >({ namespace: MOCK_ANY_NAMESPACE }); + const fooControllerMessenger = new Messenger< 'FooController', - never, - never + FooControllerAction, + FooControllerEvent, + typeof messenger >({ - name: 'FooController', - allowedActions: [], - allowedEvents: [], + namespace: 'FooController', + parent: messenger, }); - const fooController = new FooController(fooMessenger); - const composableControllerMessenger = messenger.getRestricted({ - name: 'ComposableController', - allowedActions: [], - allowedEvents: ['FooController:stateChange'], + const fooController = new FooController(fooControllerMessenger); + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], }); const controller = new ComposableController< ComposableControllerState, From 3f4446d10e6527ce03e039a1e6cc3014b2c4379a Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 15:31:26 +0200 Subject: [PATCH 191/247] update tests with `RootMessenger` type --- .../src/SeedlessOnboardingController.test.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts index 3fd2cb53c18..d48f1bacace 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts @@ -54,8 +54,8 @@ import type { import type { AllSeedlessOnboardingControllerActions, AllSeedlessOnboardingControllerEvents, - baseMessengerName, MockKeyringControllerMessenger, + RootMessenger, } from '../tests/__fixtures__/mockMessenger'; import { mockSeedlessOnboardingMessenger } from '../tests/__fixtures__/mockMessenger'; import { @@ -122,11 +122,7 @@ type WithControllerCallback = ({ encryptor: VaultEncryptor; initialState: SeedlessOnboardingControllerState; messenger: SeedlessOnboardingControllerMessenger; - baseMessenger: Messenger< - typeof baseMessengerName, - AllSeedlessOnboardingControllerActions, - AllSeedlessOnboardingControllerEvents - >; + baseMessenger: RootMessenger; keyringControllerMessenger: MockKeyringControllerMessenger; toprfClient: ToprfSecureBackup; mockRefreshJWTToken: jest.Mock; From 03bacfbcacaa7902cab3e07d6453342f501da022 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 15:31:50 +0200 Subject: [PATCH 192/247] remove unused imports --- .../src/SeedlessOnboardingController.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts index d48f1bacace..b56267d9132 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts @@ -9,7 +9,6 @@ import { decryptWithKey as decryptWithKeyBrowserPassworder, importKey as importKeyBrowserPassworder, } from '@metamask/browser-passworder'; -import type { Messenger } from '@metamask/messenger'; import { TOPRFError, type FetchAuthPubKeyResult, @@ -52,8 +51,6 @@ import type { VaultEncryptor, } from './types'; import type { - AllSeedlessOnboardingControllerActions, - AllSeedlessOnboardingControllerEvents, MockKeyringControllerMessenger, RootMessenger, } from '../tests/__fixtures__/mockMessenger'; From 119f99956d782d687594742d31dff7c93e14281d Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 20:29:32 +0200 Subject: [PATCH 193/247] fix changelog --- packages/seedless-onboarding-controller/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/seedless-onboarding-controller/CHANGELOG.md b/packages/seedless-onboarding-controller/CHANGELOG.md index 2eab6266b69..a6b7603e099 100644 --- a/packages/seedless-onboarding-controller/CHANGELOG.md +++ b/packages/seedless-onboarding-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6364](https://github.com/MetaMask/core/pull/6364)) + - Previously, `SeedlessOnboardingController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [4.1.0] ### Added @@ -15,7 +20,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **Breaking:** Migrate to new messenger ([#6364](https://github.com/MetaMask/core/pull/6364)) - Bump `@metamask/utils` from `^11.4.2` to `^11.8.1` ([#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.3.0` to `^8.4.1` ([#6632](https://github.com/MetaMask/core/pull/6632), [#6807](https://github.com/MetaMask/core/pull/6807)) From 5cb7667c39855a86bb37ae7dd6a911ba624b997f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 21:02:39 +0200 Subject: [PATCH 194/247] migrate `SubscriptionController` messenger --- README.md | 1 + packages/subscription-controller/CHANGELOG.md | 5 ++ packages/subscription-controller/package.json | 1 + .../src/SubscriptionController.test.ts | 59 ++++++++++++------- .../src/SubscriptionController.ts | 38 ++++++------ .../tsconfig.build.json | 3 + .../subscription-controller/tsconfig.json | 3 + yarn.lock | 1 + 8 files changed, 70 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 583963d2711..7aae80c22f7 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,7 @@ linkStyle default opacity:0.5 signature_controller --> network_controller; subscription_controller --> base_controller; subscription_controller --> controller_utils; + subscription_controller --> messenger; subscription_controller --> polling_controller; subscription_controller --> profile_sync_controller; token_search_discovery_controller --> base_controller; diff --git a/packages/subscription-controller/CHANGELOG.md b/packages/subscription-controller/CHANGELOG.md index 99efef2c472..b59064206d3 100644 --- a/packages/subscription-controller/CHANGELOG.md +++ b/packages/subscription-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `SubscriptionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [2.0.0] ### Added diff --git a/packages/subscription-controller/package.json b/packages/subscription-controller/package.json index 451e825c92f..61bb64c57be 100644 --- a/packages/subscription-controller/package.json +++ b/packages/subscription-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.1", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/polling-controller": "^14.0.1", "@metamask/utils": "^11.8.1" }, diff --git a/packages/subscription-controller/src/SubscriptionController.test.ts b/packages/subscription-controller/src/SubscriptionController.test.ts index 17dfd1e2b20..7083b9a40ab 100644 --- a/packages/subscription-controller/src/SubscriptionController.test.ts +++ b/packages/subscription-controller/src/SubscriptionController.test.ts @@ -1,4 +1,11 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import * as sinon from 'sinon'; import { @@ -9,7 +16,6 @@ import { SubscriptionServiceError } from './errors'; import { getDefaultSubscriptionControllerState, SubscriptionController, - type AllowedActions, type AllowedEvents, type SubscriptionControllerMessenger, type SubscriptionControllerOptions, @@ -35,6 +41,12 @@ import { } from './types'; import { advanceTime } from '../../../tests/helpers'; +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; + // Mock data const MOCK_SUBSCRIPTION: Subscription = { id: 'sub_123456789', @@ -113,25 +125,30 @@ const MOCK_GET_SUBSCRIPTIONS_RESPONSE = { function createCustomSubscriptionMessenger(props?: { overrideEvents?: AllowedEvents['type'][]; }) { - const baseMessenger = new Messenger(); + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); - const messenger = baseMessenger.getRestricted< + const messenger = new Messenger< typeof controllerName, - AllowedActions['type'], - AllowedEvents['type'] + AllActions, + AllEvents, + RootMessenger >({ - name: controllerName, - allowedActions: [ + namespace: controllerName, + parent: rootMessenger, + }); + rootMessenger.delegate({ + messenger, + actions: [ 'AuthenticationController:getBearerToken', 'AuthenticationController:performSignOut', ], - allowedEvents: props?.overrideEvents ?? [ - 'AuthenticationController:stateChange', - ], + events: props?.overrideEvents ?? ['AuthenticationController:stateChange'], }); return { - baseMessenger, + rootMessenger, messenger, }; } @@ -140,25 +157,25 @@ function createCustomSubscriptionMessenger(props?: { * Jest Mock Utility to generate a mock Subscription Messenger * * @param overrideMessengers - override messengers if need to modify the underlying permissions - * @param overrideMessengers.baseMessenger - base messenger to override + * @param overrideMessengers.rootMessenger - base messenger to override * @param overrideMessengers.messenger - messenger to override * @returns series of mocks to actions that can be called */ function createMockSubscriptionMessenger(overrideMessengers?: { - baseMessenger: Messenger; + rootMessenger: RootMessenger; messenger: SubscriptionControllerMessenger; }) { - const { baseMessenger, messenger } = + const { rootMessenger, messenger } = overrideMessengers ?? createCustomSubscriptionMessenger(); const mockPerformSignOut = jest.fn(); - baseMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AuthenticationController:performSignOut', mockPerformSignOut, ); return { - baseMessenger, + rootMessenger, messenger, mockPerformSignOut, }; @@ -216,7 +233,7 @@ type WithControllerCallback = (params: { controller: SubscriptionController; initialState: SubscriptionControllerState; messenger: SubscriptionControllerMessenger; - baseMessenger: Messenger; + rootMessenger: RootMessenger; mockService: ReturnType['mockService']; mockPerformSignOut: jest.Mock; }) => Promise | ReturnValue; @@ -237,7 +254,7 @@ async function withController( ...args: WithControllerArgs ) { const [{ ...rest }, fn] = args.length === 2 ? args : [{}, args[0]]; - const { messenger, mockPerformSignOut, baseMessenger } = + const { messenger, mockPerformSignOut, rootMessenger } = createMockSubscriptionMessenger(); const { mockService } = createMockSubscriptionService(); @@ -251,7 +268,7 @@ async function withController( controller, initialState: controller.state, messenger, - baseMessenger, + rootMessenger, mockService, mockPerformSignOut, }); @@ -1192,7 +1209,7 @@ describe('SubscriptionController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/subscription-controller/src/SubscriptionController.ts b/packages/subscription-controller/src/SubscriptionController.ts index 36ba7e16035..4b2146cadb0 100644 --- a/packages/subscription-controller/src/SubscriptionController.ts +++ b/packages/subscription-controller/src/SubscriptionController.ts @@ -2,8 +2,8 @@ import { type StateMetadata, type ControllerStateChangeEvent, type ControllerGetStateAction, - type RestrictedMessenger, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import { StaticIntervalPollingController } from '@metamask/polling-controller'; import type { AuthenticationController } from '@metamask/profile-sync-controller'; @@ -110,12 +110,10 @@ export type AllowedEvents = AuthenticationController.AuthenticationControllerStateChangeEvent; // Messenger -export type SubscriptionControllerMessenger = RestrictedMessenger< +export type SubscriptionControllerMessenger = Messenger< typeof controllerName, SubscriptionControllerActions | AllowedActions, - SubscriptionControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SubscriptionControllerEvents | AllowedEvents >; /** @@ -166,25 +164,25 @@ const subscriptionControllerMetadata: StateMetadata subscriptions: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, customerId: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, trialedProducts: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, pricing: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; @@ -233,47 +231,47 @@ export class SubscriptionController extends StaticIntervalPollingController()< * actions. */ #registerMessageHandlers(): void { - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:getSubscriptions', this.getSubscriptions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:getSubscriptionByProduct', this.getSubscriptionByProduct.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:cancelSubscription', this.cancelSubscription.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:startShieldSubscriptionWithCard', this.startShieldSubscriptionWithCard.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:getPricing', this.getPricing.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:getCryptoApproveTransactionParams', this.getCryptoApproveTransactionParams.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:startSubscriptionWithCrypto', this.startSubscriptionWithCrypto.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:updatePaymentMethod', this.updatePaymentMethod.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( 'SubscriptionController:getBillingPortalUrl', this.getBillingPortalUrl.bind(this), ); @@ -576,7 +574,7 @@ export class SubscriptionController extends StaticIntervalPollingController()< // We perform a sign out to clear the access token from the authentication // controller. Next time the access token is requested, a new access token // will be fetched. - this.messagingSystem.call('AuthenticationController:performSignOut'); + this.messenger.call('AuthenticationController:performSignOut'); } #assertIsUserSubscribed(request: { subscriptionId: string }) { diff --git a/packages/subscription-controller/tsconfig.build.json b/packages/subscription-controller/tsconfig.build.json index affca7cb2c1..9c4e429236a 100644 --- a/packages/subscription-controller/tsconfig.build.json +++ b/packages/subscription-controller/tsconfig.build.json @@ -9,6 +9,9 @@ { "path": "../base-controller/tsconfig.build.json" }, + { + "path": "../messenger/tsconfig.build.json" + }, { "path": "../profile-sync-controller/tsconfig.build.json" }, diff --git a/packages/subscription-controller/tsconfig.json b/packages/subscription-controller/tsconfig.json index 04ea472196b..e8c3b75ae82 100644 --- a/packages/subscription-controller/tsconfig.json +++ b/packages/subscription-controller/tsconfig.json @@ -7,6 +7,9 @@ { "path": "../base-controller" }, + { + "path": "../messenger" + }, { "path": "../profile-sync-controller" }, diff --git a/yarn.lock b/yarn.lock index 07446cc02e6..1610420cc56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4898,6 +4898,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/polling-controller": "npm:^14.0.1" "@metamask/profile-sync-controller": "npm:^25.1.1" "@metamask/utils": "npm:^11.8.1" From bd260755aa75636e0ef001942865e06cb1418000 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 21:10:52 +0200 Subject: [PATCH 195/247] fix changelog --- packages/preferences-controller/CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/preferences-controller/CHANGELOG.md b/packages/preferences-controller/CHANGELOG.md index 33a53f8cca4..fe284711453 100644 --- a/packages/preferences-controller/CHANGELOG.md +++ b/packages/preferences-controller/CHANGELOG.md @@ -7,17 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Changed - -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6534](https://github.com/MetaMask/core/pull/6534)) - - Previously, `PreferencesController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - ### Added - Add support for MONAD (chain ID `0x8f`) ([#6827](https://github.com/MetaMask/core/pull/6827)) - Add `MONAD` into constant `ETHERSCAN_SUPPORTED_CHAIN_IDS` - Update default controller state so MONAD (Chain ID `0x8f`) is automatically enabled in `showIncomingTransactions` +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6534](https://github.com/MetaMask/core/pull/6534)) + - Previously, `PreferencesController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [20.0.2] ### Changed From b1f0c534e01b6223247dc25085eee13547a57072 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 21:12:41 +0200 Subject: [PATCH 196/247] migrate new `messagingSystem` call --- .../bridge-status-controller/src/bridge-status-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 31c313b057a..f4872cf0805 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -650,7 +650,7 @@ export class BridgeStatusController extends StaticIntervalPollingController Date: Mon, 20 Oct 2025 21:18:57 +0200 Subject: [PATCH 197/247] migrate new `messagingSystem` call --- packages/assets-controllers/src/TokenBalancesController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.ts b/packages/assets-controllers/src/TokenBalancesController.ts index fe775c5f453..ca0a2e1efd0 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -1145,7 +1145,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ // Update native balances in AccountTrackerController if (nativeBalanceUpdates.length > 0) { - this.messagingSystem.call( + this.messenger.call( 'AccountTrackerController:updateNativeBalances', nativeBalanceUpdates, ); @@ -1153,7 +1153,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ // Import any new tokens that were discovered (balance already updated from websocket) if (newTokens.length > 0) { - await this.messagingSystem.call( + await this.messenger.call( 'TokenDetectionController:addDetectedTokensViaWs', { tokensSlice: newTokens, From a3d5fc939b729db257d5a657ffbc2d3cbf2b85a9 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 21:28:13 +0200 Subject: [PATCH 198/247] fix `RatesController` tests --- .../RatesController/RatesController.test.ts | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/packages/assets-controllers/src/RatesController/RatesController.test.ts b/packages/assets-controllers/src/RatesController/RatesController.test.ts index 8ba6ccbe2b5..2fadaeda774 100644 --- a/packages/assets-controllers/src/RatesController/RatesController.test.ts +++ b/packages/assets-controllers/src/RatesController/RatesController.test.ts @@ -81,13 +81,14 @@ function setupRatesController({ fetchMultiExchangeRate?: typeof defaultFetchExchangeRate; }) { const ratesControllerMessenger = buildRatesControllerMessenger(messenger); - return new RatesController({ + const ratesController = new RatesController({ interval, messenger: ratesControllerMessenger, state: initialState, includeUsdRate, fetchMultiExchangeRate, }); + return { ratesController, ratesControllerMessenger }; } describe('RatesController', () => { @@ -95,7 +96,7 @@ describe('RatesController', () => { describe('construct', () => { it('constructs the RatesController with default values', () => { - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ initialState: {}, messenger: buildRootMessenger(), includeUsdRate: false, @@ -125,7 +126,6 @@ describe('RatesController', () => { it('starts the polling process with default values', async () => { const messenger = buildRootMessenger(); - const publishActionSpy = jest.spyOn(messenger, 'publish'); jest.spyOn(global.Date, 'now').mockImplementation(() => getStubbedDate()); const mockBtcRateValue = 57715.42; @@ -141,15 +141,17 @@ describe('RatesController', () => { }, }); }); - const ratesController = setupRatesController({ - interval: 150, - initialState: { - fiatCurrency: 'eur', - }, - messenger, - fetchMultiExchangeRate: fetchExchangeRateStub, - includeUsdRate: false, - }); + const { ratesController, ratesControllerMessenger } = + setupRatesController({ + interval: 150, + initialState: { + fiatCurrency: 'eur', + }, + messenger, + fetchMultiExchangeRate: fetchExchangeRateStub, + includeUsdRate: false, + }); + const publishActionSpy = jest.spyOn(ratesControllerMessenger, 'publish'); const ratesPreUpdate = ratesController.state.rates; @@ -222,7 +224,7 @@ describe('RatesController', () => { }); }); - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ interval: 150, initialState: { cryptocurrencies: [Cryptocurrency.Btc], @@ -270,15 +272,16 @@ describe('RatesController', () => { it('stops the polling process', async () => { const messenger = buildRootMessenger(); - const publishActionSpy = jest.spyOn(messenger, 'publish'); const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const ratesController = setupRatesController({ - interval: 150, - initialState: {}, - messenger, - fetchMultiExchangeRate: fetchExchangeRateStub, - includeUsdRate: false, - }); + const { ratesController, ratesControllerMessenger } = + setupRatesController({ + interval: 150, + initialState: {}, + messenger, + fetchMultiExchangeRate: fetchExchangeRateStub, + includeUsdRate: false, + }); + const publishActionSpy = jest.spyOn(ratesControllerMessenger, 'publish'); await ratesController.start(); @@ -319,7 +322,7 @@ describe('RatesController', () => { it('returns the current cryptocurrency list', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const mockCryptocurrencyList = [Cryptocurrency.Btc]; - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ interval: 150, initialState: { cryptocurrencies: mockCryptocurrencyList, @@ -338,7 +341,7 @@ describe('RatesController', () => { it('updates the cryptocurrency list', async () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); const mockCryptocurrencyList: Cryptocurrency[] = []; // Different from default list - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ interval: 150, initialState: {}, messenger: buildRootMessenger(), @@ -369,7 +372,7 @@ describe('RatesController', () => { describe('setCurrentCurrency', () => { it('sets the currency to a new value', async () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ interval: 150, initialState: {}, messenger: buildRootMessenger(), @@ -388,7 +391,7 @@ describe('RatesController', () => { it('throws if input is an empty string', async () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ interval: 150, initialState: {}, messenger: buildRootMessenger(), @@ -405,7 +408,7 @@ describe('RatesController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ + const { ratesController } = setupRatesController({ messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, @@ -413,8 +416,8 @@ describe('RatesController', () => { expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` @@ -440,7 +443,7 @@ describe('RatesController', () => { it('includes expected state in state logs', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ + const { ratesController } = setupRatesController({ messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, @@ -448,8 +451,8 @@ describe('RatesController', () => { expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'includeInStateLogs', ), ).toMatchInlineSnapshot(` @@ -465,7 +468,7 @@ describe('RatesController', () => { it('persists expected state', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ + const { ratesController } = setupRatesController({ messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, @@ -473,8 +476,8 @@ describe('RatesController', () => { expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'persist', ), ).toMatchInlineSnapshot(` @@ -500,7 +503,7 @@ describe('RatesController', () => { it('exposes expected state to UI', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ + const { ratesController } = setupRatesController({ messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, @@ -508,8 +511,8 @@ describe('RatesController', () => { expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'usedInUi', ), ).toMatchInlineSnapshot(` From bd8994754eab551d9de7ab8f3916487c1f165de1 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Mon, 20 Oct 2025 21:30:19 +0200 Subject: [PATCH 199/247] fix `TokenBalancesController` tests --- .../src/TokenBalancesController.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 6b6707eff23..a7388c9cf99 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -4383,13 +4383,14 @@ describe('TokenBalancesController', () => { const chainId = '0x1'; const account = createMockInternalAccount({ address: accountAddress }); - const { controller, messenger } = setupController({ - listAccounts: [account], - }); + const { controller, messenger, tokenBalancesControllerMessenger } = + setupController({ + listAccounts: [account], + }); // Spy on AccountTrackerController calls const updateNativeBalancesSpy = jest.fn(); - jest.spyOn(messenger, 'call').mockImplementation((( + jest.spyOn(tokenBalancesControllerMessenger, 'call').mockImplementation((( action: string, ...args: unknown[] ) => { From ccc573a027a7cc3fe5751dddaeef06f1555b6b17 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 13:27:31 +0200 Subject: [PATCH 200/247] add `GetStateAction` and `StateChangeEvent` --- .../src/AbstractMessageManager.ts | 49 +++++++++++++++---- .../src/DecryptMessageManager.ts | 33 ++++++++----- .../src/EncryptionPublicKeyManager.ts | 35 ++++++++----- 3 files changed, 81 insertions(+), 36 deletions(-) diff --git a/packages/message-manager/src/AbstractMessageManager.ts b/packages/message-manager/src/AbstractMessageManager.ts index 5202f2ec71b..1af6186122e 100644 --- a/packages/message-manager/src/AbstractMessageManager.ts +++ b/packages/message-manager/src/AbstractMessageManager.ts @@ -1,9 +1,14 @@ -import { BaseController } from '@metamask/base-controller/next'; +import { + BaseController, + type ControllerStateChangeEvent, + type ControllerGetStateAction, +} from '@metamask/base-controller/next'; import type { ApprovalType } from '@metamask/controller-utils'; import type { - ActionConstraint, - EventConstraint, Messenger, + EventConstraint, + ActionConstraint, + NamespacedName, } from '@metamask/messenger'; import type { Json } from '@metamask/utils'; // This package purposefully relies on Node's EventEmitter module. @@ -121,6 +126,29 @@ export type SecurityProviderRequest = ( messageType: string, ) => Promise; +type MessageManagerActionConstraint = { + type: NamespacedName; + handler: ActionConstraint['handler']; +}; + +type MessageManagerEventConstraint = { + type: NamespacedName; + payload: EventConstraint['payload']; +}; + +export type MessageManagerMessenger< + Name extends string, + Message extends AbstractMessage, + Action extends MessageManagerActionConstraint, + Event extends MessageManagerEventConstraint, +> = Messenger< + Name, + ControllerGetStateAction> | Action, + | ControllerStateChangeEvent> + | UpdateBadgeEvent + | Event +>; + /** * AbstractMessageManager constructor options. * @@ -133,11 +161,11 @@ export type SecurityProviderRequest = ( export type AbstractMessageManagerOptions< Name extends string, Message extends AbstractMessage, - Action extends ActionConstraint, - Event extends EventConstraint, + Action extends MessageManagerActionConstraint, + Event extends MessageManagerEventConstraint, > = { additionalFinishStatuses?: string[]; - messenger: Messenger>; + messenger: MessageManagerMessenger; name: Name; securityProviderRequest?: SecurityProviderRequest; state?: MessageManagerState; @@ -151,12 +179,12 @@ export abstract class AbstractMessageManager< Message extends AbstractMessage, Params extends AbstractMessageParams, ParamsMetamask extends AbstractMessageParamsMetamask, - Action extends ActionConstraint, - Event extends EventConstraint, + Action extends MessageManagerActionConstraint, + Event extends MessageManagerEventConstraint, > extends BaseController< Name, MessageManagerState, - Messenger> + MessageManagerMessenger > { protected messages: Message[]; @@ -245,7 +273,8 @@ export abstract class AbstractMessageManager< state.unapprovedMessagesCount = this.getUnapprovedMessagesCount(); }); if (emitUpdateBadge) { - this.messenger.publish(`${this.name}:updateBadge` as const); + // @ts-expect-error Messenger typing seems to have trouble inferring correctly here + this.messenger.publish(`${this.name}:updateBadge`); } } diff --git a/packages/message-manager/src/DecryptMessageManager.ts b/packages/message-manager/src/DecryptMessageManager.ts index c1a96581b42..df28681aa75 100644 --- a/packages/message-manager/src/DecryptMessageManager.ts +++ b/packages/message-manager/src/DecryptMessageManager.ts @@ -1,9 +1,10 @@ -import { ApprovalType } from '@metamask/controller-utils'; import type { - ActionConstraint, - EventConstraint, - Messenger, -} from '@metamask/messenger'; + ControllerGetStateAction, + ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; +import { ApprovalType } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; +import {} from '@metamask/messenger'; import type { AbstractMessage, @@ -30,12 +31,20 @@ export type DecryptMessageManagerUpdateBadgeEvent = { payload: []; }; -export type DecryptMessageManagerMessenger = Messenger< +type DecryptMessageManagerActions = ControllerGetStateAction< typeof managerName, - ActionConstraint, - | EventConstraint + DecryptMessageManagerState +>; + +type DecryptMessageManagerEvents = + | ControllerStateChangeEvent | DecryptMessageManagerUnapprovedMessageAddedEvent - | DecryptMessageManagerUpdateBadgeEvent + | DecryptMessageManagerUpdateBadgeEvent; + +export type DecryptMessageManagerMessenger = Messenger< + typeof managerName, + DecryptMessageManagerActions, + DecryptMessageManagerEvents >; type DecryptMessageManagerOptions = { @@ -95,10 +104,8 @@ export class DecryptMessageManager extends AbstractMessageManager< DecryptMessage, DecryptMessageParams, DecryptMessageParamsMetamask, - ActionConstraint, - | EventConstraint - | DecryptMessageManagerUnapprovedMessageAddedEvent - | DecryptMessageManagerUpdateBadgeEvent + DecryptMessageManagerActions, + DecryptMessageManagerEvents > { constructor({ additionalFinishStatuses, diff --git a/packages/message-manager/src/EncryptionPublicKeyManager.ts b/packages/message-manager/src/EncryptionPublicKeyManager.ts index cfff6f3b7b8..3a13882d103 100644 --- a/packages/message-manager/src/EncryptionPublicKeyManager.ts +++ b/packages/message-manager/src/EncryptionPublicKeyManager.ts @@ -1,9 +1,9 @@ -import { ApprovalType } from '@metamask/controller-utils'; import type { - ActionConstraint, - EventConstraint, - Messenger, -} from '@metamask/messenger'; + ControllerGetStateAction, + ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; +import { ApprovalType } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { AbstractMessage, @@ -31,12 +31,23 @@ export type EncryptionPublicKeyManagerUpdateBadgeEvent = { payload: []; }; -export type EncryptionPublicKeyManagerMessenger = Messenger< +type EncryptionPublicKeyManagerActions = ControllerGetStateAction< typeof managerName, - ActionConstraint, - | EventConstraint + EncryptionPublicKeyManagerState +>; + +type EncryptionPublicKeyManagerEvents = + | ControllerStateChangeEvent< + typeof managerName, + EncryptionPublicKeyManagerState + > | EncryptionPublicKeyManagerUnapprovedMessageAddedEvent - | EncryptionPublicKeyManagerUpdateBadgeEvent + | EncryptionPublicKeyManagerUpdateBadgeEvent; + +export type EncryptionPublicKeyManagerMessenger = Messenger< + typeof managerName, + EncryptionPublicKeyManagerActions, + EncryptionPublicKeyManagerEvents >; type EncryptionPublicKeyManagerOptions = { @@ -93,10 +104,8 @@ export class EncryptionPublicKeyManager extends AbstractMessageManager< EncryptionPublicKey, EncryptionPublicKeyParams, EncryptionPublicKeyParamsMetamask, - ActionConstraint, - | EventConstraint - | EncryptionPublicKeyManagerUnapprovedMessageAddedEvent - | EncryptionPublicKeyManagerUpdateBadgeEvent + EncryptionPublicKeyManagerActions, + EncryptionPublicKeyManagerEvents > { constructor({ additionalFinishStatuses, From 9564b942e2724143351ef8a2e30aa632c381a091 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 13:31:37 +0200 Subject: [PATCH 201/247] use `ActionConstraint` and `EventConstraint` as messenger generic params --- .../src/AbstractMessageManager.ts | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/packages/message-manager/src/AbstractMessageManager.ts b/packages/message-manager/src/AbstractMessageManager.ts index 1af6186122e..75e4aa48ada 100644 --- a/packages/message-manager/src/AbstractMessageManager.ts +++ b/packages/message-manager/src/AbstractMessageManager.ts @@ -8,7 +8,6 @@ import type { Messenger, EventConstraint, ActionConstraint, - NamespacedName, } from '@metamask/messenger'; import type { Json } from '@metamask/utils'; // This package purposefully relies on Node's EventEmitter module. @@ -126,21 +125,11 @@ export type SecurityProviderRequest = ( messageType: string, ) => Promise; -type MessageManagerActionConstraint = { - type: NamespacedName; - handler: ActionConstraint['handler']; -}; - -type MessageManagerEventConstraint = { - type: NamespacedName; - payload: EventConstraint['payload']; -}; - export type MessageManagerMessenger< Name extends string, Message extends AbstractMessage, - Action extends MessageManagerActionConstraint, - Event extends MessageManagerEventConstraint, + Action extends ActionConstraint, + Event extends EventConstraint, > = Messenger< Name, ControllerGetStateAction> | Action, @@ -161,8 +150,8 @@ export type MessageManagerMessenger< export type AbstractMessageManagerOptions< Name extends string, Message extends AbstractMessage, - Action extends MessageManagerActionConstraint, - Event extends MessageManagerEventConstraint, + Action extends ActionConstraint, + Event extends EventConstraint, > = { additionalFinishStatuses?: string[]; messenger: MessageManagerMessenger; @@ -179,8 +168,8 @@ export abstract class AbstractMessageManager< Message extends AbstractMessage, Params extends AbstractMessageParams, ParamsMetamask extends AbstractMessageParamsMetamask, - Action extends MessageManagerActionConstraint, - Event extends MessageManagerEventConstraint, + Action extends ActionConstraint, + Event extends EventConstraint, > extends BaseController< Name, MessageManagerState, From 34e3e0074ceefb50012f6650e93b115cd71f180f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 13:43:37 +0200 Subject: [PATCH 202/247] use `MessageManagerMessenger` generic type param instead of Action, Event --- .../src/AbstractMessageManager.ts | 42 +++++++++---------- .../src/DecryptMessageManager.ts | 3 +- .../src/EncryptionPublicKeyManager.ts | 3 +- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/message-manager/src/AbstractMessageManager.ts b/packages/message-manager/src/AbstractMessageManager.ts index 75e4aa48ada..4496834556c 100644 --- a/packages/message-manager/src/AbstractMessageManager.ts +++ b/packages/message-manager/src/AbstractMessageManager.ts @@ -125,19 +125,6 @@ export type SecurityProviderRequest = ( messageType: string, ) => Promise; -export type MessageManagerMessenger< - Name extends string, - Message extends AbstractMessage, - Action extends ActionConstraint, - Event extends EventConstraint, -> = Messenger< - Name, - ControllerGetStateAction> | Action, - | ControllerStateChangeEvent> - | UpdateBadgeEvent - | Event ->; - /** * AbstractMessageManager constructor options. * @@ -150,11 +137,17 @@ export type MessageManagerMessenger< export type AbstractMessageManagerOptions< Name extends string, Message extends AbstractMessage, - Action extends ActionConstraint, - Event extends EventConstraint, + MessageManagerMessenger extends Messenger< + Name, + | ControllerGetStateAction> + | ActionConstraint, + | ControllerStateChangeEvent> + | UpdateBadgeEvent + | EventConstraint + >, > = { additionalFinishStatuses?: string[]; - messenger: MessageManagerMessenger; + messenger: MessageManagerMessenger; name: Name; securityProviderRequest?: SecurityProviderRequest; state?: MessageManagerState; @@ -168,12 +161,18 @@ export abstract class AbstractMessageManager< Message extends AbstractMessage, Params extends AbstractMessageParams, ParamsMetamask extends AbstractMessageParamsMetamask, - Action extends ActionConstraint, - Event extends EventConstraint, + MessageManagerMessenger extends Messenger< + Name, + | ControllerGetStateAction> + | ActionConstraint, + | ControllerStateChangeEvent> + | UpdateBadgeEvent + | EventConstraint + >, > extends BaseController< Name, MessageManagerState, - MessageManagerMessenger + MessageManagerMessenger > { protected messages: Message[]; @@ -189,7 +188,7 @@ export abstract class AbstractMessageManager< name, securityProviderRequest, state = {} as MessageManagerState, - }: AbstractMessageManagerOptions) { + }: AbstractMessageManagerOptions) { super({ messenger, metadata: stateMetadata, @@ -262,8 +261,7 @@ export abstract class AbstractMessageManager< state.unapprovedMessagesCount = this.getUnapprovedMessagesCount(); }); if (emitUpdateBadge) { - // @ts-expect-error Messenger typing seems to have trouble inferring correctly here - this.messenger.publish(`${this.name}:updateBadge`); + this.messenger.publish(`${this.name}:updateBadge` as const); } } diff --git a/packages/message-manager/src/DecryptMessageManager.ts b/packages/message-manager/src/DecryptMessageManager.ts index df28681aa75..87176a021c8 100644 --- a/packages/message-manager/src/DecryptMessageManager.ts +++ b/packages/message-manager/src/DecryptMessageManager.ts @@ -104,8 +104,7 @@ export class DecryptMessageManager extends AbstractMessageManager< DecryptMessage, DecryptMessageParams, DecryptMessageParamsMetamask, - DecryptMessageManagerActions, - DecryptMessageManagerEvents + DecryptMessageManagerMessenger > { constructor({ additionalFinishStatuses, diff --git a/packages/message-manager/src/EncryptionPublicKeyManager.ts b/packages/message-manager/src/EncryptionPublicKeyManager.ts index 3a13882d103..56e7b9455fd 100644 --- a/packages/message-manager/src/EncryptionPublicKeyManager.ts +++ b/packages/message-manager/src/EncryptionPublicKeyManager.ts @@ -104,8 +104,7 @@ export class EncryptionPublicKeyManager extends AbstractMessageManager< EncryptionPublicKey, EncryptionPublicKeyParams, EncryptionPublicKeyParamsMetamask, - EncryptionPublicKeyManagerActions, - EncryptionPublicKeyManagerEvents + EncryptionPublicKeyManagerMessenger > { constructor({ additionalFinishStatuses, From 63f7037bd2fa48dbfa1bd45e062ac89c30be88eb Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 13:51:52 +0200 Subject: [PATCH 203/247] fix `AbstractMessageManager` tests --- .../src/AbstractMessageManager.test.ts | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/message-manager/src/AbstractMessageManager.test.ts b/packages/message-manager/src/AbstractMessageManager.test.ts index 5f1899b0605..0b0560e9073 100644 --- a/packages/message-manager/src/AbstractMessageManager.test.ts +++ b/packages/message-manager/src/AbstractMessageManager.test.ts @@ -1,14 +1,19 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + deriveStateFromMetadata, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import { ApprovalType } from '@metamask/controller-utils'; import type { Messenger } from '@metamask/messenger'; -import type { - AbstractMessage, - AbstractMessageParams, - OriginalRequest, - SecurityProviderRequest, +import { + AbstractMessageManager, + type AbstractMessage, + type AbstractMessageParams, + type MessageManagerState, + type OriginalRequest, + type SecurityProviderRequest, } from './AbstractMessageManager'; -import { AbstractMessageManager } from './AbstractMessageManager'; type ConcreteMessage = AbstractMessage & { messageParams: ConcreteMessageParams; @@ -22,16 +27,26 @@ type ConcreteMessageParamsMetamask = ConcreteMessageParams & { metamaskId?: string; }; -type ConcreteMessageManagerActions = never; -type ConcreteMessageManagerEvents = never; +type ConcreteMessageManagerActions = ControllerGetStateAction< + 'TestManager', + MessageManagerState +>; +type ConcreteMessageManagerEvents = ControllerStateChangeEvent< + 'TestManager', + MessageManagerState +>; +type ConcreteMessageManagerMessenger = Messenger< + 'TestManager', + ConcreteMessageManagerActions, + ConcreteMessageManagerEvents +>; class AbstractTestManager extends AbstractMessageManager< 'TestManager', ConcreteMessage, ConcreteMessageParams, ConcreteMessageParamsMetamask, - ConcreteMessageManagerActions, - ConcreteMessageManagerEvents + ConcreteMessageManagerMessenger > { addRequestToMessageParams( messageParams: MessageParams, From 5cfd25698d7ddf1bb0965dff427544841c2f44a7 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 14:02:22 +0200 Subject: [PATCH 204/247] lint: import `MockAnyNamespace` as type --- packages/transaction-controller/src/utils/eip7702.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/transaction-controller/src/utils/eip7702.test.ts b/packages/transaction-controller/src/utils/eip7702.test.ts index 323ff64a120..069b31f3ebc 100644 --- a/packages/transaction-controller/src/utils/eip7702.test.ts +++ b/packages/transaction-controller/src/utils/eip7702.test.ts @@ -2,7 +2,7 @@ import { query } from '@metamask/controller-utils'; import type EthQuery from '@metamask/eth-query'; import { Messenger, - MockAnyNamespace, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, MOCK_ANY_NAMESPACE, From 64e065ecafbe48f5ab101a0cd6d3e578ddae1f27 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 14:07:35 +0200 Subject: [PATCH 205/247] run prettier --- packages/announcement-controller/tsconfig.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/announcement-controller/tsconfig.json b/packages/announcement-controller/tsconfig.json index 8af2380112d..68c3ddfc2cd 100644 --- a/packages/announcement-controller/tsconfig.json +++ b/packages/announcement-controller/tsconfig.json @@ -3,9 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "references": [ - { "path": "../base-controller" }, - { "path": "../messenger" } - ], + "references": [{ "path": "../base-controller" }, { "path": "../messenger" }], "include": ["../../types", "./src"] } From b3f16e624d4ad9315d0bcad089c2f458e112e85d Mon Sep 17 00:00:00 2001 From: Jongsun Suh Date: Wed, 22 Oct 2025 02:23:24 +0900 Subject: [PATCH 206/247] fix: `Messenger` type errors in `ComposableController` (#6904) ## Explanation - Fix incompatibility of `ChildControllerStateChangeEvents` type with `BaseController` (when used in the `Events` type argument of `ComposableControllerMessenger`) by removing unnecessary nested logic from definition. - Update generic parameter names `ControllerName` and `ControllerState` to `ChildControllerName`, `ChildControllerState` for reduced ambiguity. ## References - Branches from https://github.com/MetaMask/core/pull/6710 ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs), highlighting breaking changes as necessary - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes --- > [!NOTE] > Simplifies `ChildControllerStateChangeEvents` and types the messenger `subscribe` call to fix `ComposableController` Messenger type errors; updates changelog. > > - **Composable Controller**: > - **Types**: Simplify `ChildControllerStateChangeEvents` by removing nested conditional logic and renaming generics to `ChildControllerName`/`ChildControllerState`. > - **Messenger**: Type the `subscribe` call for child `stateChange` events, updating composed state with the received child state (with an inline ts-expect-error to bypass an unnecessary overload constraint). > - **Docs**: > - Update `packages/composable-controller/CHANGELOG.md` with a Fixed entry describing the type compatibility change. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b3444fb970ecbb8b37742b4450464cf2e5a5730c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .vscode/settings.json | 3 ++ eslint-warning-thresholds.json | 3 -- packages/composable-controller/CHANGELOG.md | 5 ++++ .../src/ComposableController.ts | 30 +++++++++---------- 4 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..25fa6215fdd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index a16cd0d14f3..fcfb3d02a3f 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -107,9 +107,6 @@ "packages/composable-controller/src/ComposableController.test.ts": { "import-x/namespace": 3 }, - "packages/composable-controller/src/ComposableController.ts": { - "@typescript-eslint/no-unused-vars": 1 - }, "packages/controller-utils/jest.environment.js": { "n/prefer-global/text-encoder": 1, "n/prefer-global/text-decoder": 1, diff --git a/packages/composable-controller/CHANGELOG.md b/packages/composable-controller/CHANGELOG.md index e68d9c2e7b3..8bfbf05ac4c 100644 --- a/packages/composable-controller/CHANGELOG.md +++ b/packages/composable-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Resolve incompatibility of `ChildControllerStateChangeEvents` type with `BaseController` (when used in the `Events` type argument of `ComposableControllerMessenger`) by removing unnecessary nested logic from definition ([#6904](https://github.com/MetaMask/core/pull/6904)) + - Also update generic parameter names `ControllerName` and `ControllerState` to `ChildControllerName`, `ChildControllerState` for reduced ambiguity. + ## [11.1.0] ### Added diff --git a/packages/composable-controller/src/ComposableController.ts b/packages/composable-controller/src/ComposableController.ts index 06421a64110..03bdb317341 100644 --- a/packages/composable-controller/src/ComposableController.ts +++ b/packages/composable-controller/src/ComposableController.ts @@ -70,12 +70,10 @@ export type ChildControllerStateChangeEvents< ComposableControllerState extends ComposableControllerStateConstraint, > = ComposableControllerState extends Record< - infer ControllerName extends string, - infer ControllerState + infer ChildControllerName extends string, + infer ChildControllerState extends StateConstraint > - ? ControllerState extends StateConstraint - ? ControllerStateChangeEvent - : never + ? ControllerStateChangeEvent : never; /** @@ -184,16 +182,18 @@ export class ComposableController< throw new Error(`${name} - ${INVALID_CONTROLLER_ERROR}`); } try { - this.messenger.subscribe( - `${name}:stateChange`, - (childState: StateConstraint) => { - this.update((state) => { - // Type assertion is necessary for property assignment to a generic type. This does not pollute or widen the type of the asserted variable. - // @ts-expect-error "Type instantiation is excessively deep" - (state as ComposableControllerStateConstraint)[name] = childState; - }); - }, - ); + this.messenger.subscribe< + // The type intersection with "ComposableController:stateChange" is added by one of the `Messenger.subscribe` overloads, but that constraint is unnecessary here, + // since this method only subscribes the messenger to child controller `stateChange` events. + // @ts-expect-error "Type '`${string}:stateChange`' is not assignable to parameter of type '"ComposableController:stateChange" & ChildControllerStateChangeEvents["type"]'." + ChildControllerStateChangeEvents['type'] + >(`${name}:stateChange`, (childState: StateConstraint) => { + this.update((state) => { + // Type assertion is necessary for property assignment to a generic type. This does not pollute or widen the type of the asserted variable. + // @ts-expect-error "Type instantiation is excessively deep" + (state as ComposableControllerStateConstraint)[name] = childState; + }); + }); } catch (error: unknown) { // False negative. `name` is a string type. // eslint-disable-next-line @typescript-eslint/restrict-template-expressions From 57bec0b8c3be51942581bf6f373aa3e7784b49c4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 19:40:48 +0200 Subject: [PATCH 207/247] add test case for child state change subscription failure --- .../src/ComposableController.test.ts | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/packages/composable-controller/src/ComposableController.test.ts b/packages/composable-controller/src/ComposableController.test.ts index 7b67a1e7d12..d9e81c01e2a 100644 --- a/packages/composable-controller/src/ComposableController.test.ts +++ b/packages/composable-controller/src/ComposableController.test.ts @@ -231,8 +231,8 @@ describe('ComposableController', () => { namespace: 'ComposableController', parent: messenger, }); - composableControllerMessenger.delegate({ - messenger: fooControllerMessenger, + messenger.delegate({ + messenger: composableControllerMessenger, events: ['FooController:stateChange'], }); new ComposableController< @@ -246,7 +246,10 @@ describe('ComposableController', () => { }); const listener = sinon.stub(); - messenger.subscribe('ComposableController:stateChange', listener); + composableControllerMessenger.subscribe( + 'ComposableController:stateChange', + listener, + ); fooController.updateFoo('qux'); expect(listener.calledOnce).toBe(true); @@ -332,6 +335,56 @@ describe('ComposableController', () => { }); }); + it('should not throw if child state change event subscription fails', () => { + type ComposableControllerState = { + FooController: FooControllerState; + }; + const messenger = new Messenger< + MockAnyNamespace, + | ComposableControllerActions + | FooControllerAction, + ComposableControllerEvents | FooControllerEvent + >({ namespace: MOCK_ANY_NAMESPACE }); + const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerAction, + FooControllerEvent, + typeof messenger + >({ + namespace: 'FooController', + parent: messenger, + }); + const fooController = new FooController(fooControllerMessenger); + const composableControllerMessenger = new Messenger< + 'ComposableController', + ComposableControllerActions, + | ComposableControllerEvents + | FooControllerEvent, + typeof messenger + >({ + namespace: 'ComposableController', + parent: messenger, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], + }); + jest + .spyOn(composableControllerMessenger, 'subscribe') + .mockImplementation(() => { + throw new Error(); + }); + expect( + () => + new ComposableController({ + controllers: { + FooController: fooController, + }, + messenger: composableControllerMessenger, + }), + ).not.toThrow(); + }); + it('should throw if controller messenger not provided', () => { const messenger = new Messenger< MockAnyNamespace, From 030a9170a00a1e592816b9451fc8cb833bf7d049 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 19:44:16 +0200 Subject: [PATCH 208/247] remove .vscode folder --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 25fa6215fdd..00000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib" -} From 6287e52d6cec42110053c4301fd926a16c6c9c54 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 19:46:10 +0200 Subject: [PATCH 209/247] update tsconfig and README files --- README.md | 1 + packages/composable-controller/tsconfig.build.json | 3 +++ packages/composable-controller/tsconfig.json | 3 +++ 3 files changed, 7 insertions(+) diff --git a/README.md b/README.md index ba3b7ae26f8..f51bcc2f582 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,7 @@ linkStyle default opacity:0.5 chain_agnostic_permission --> network_controller; chain_agnostic_permission --> permission_controller; composable_controller --> base_controller; + composable_controller --> messenger; composable_controller --> json_rpc_engine; core_backend --> base_controller; core_backend --> controller_utils; diff --git a/packages/composable-controller/tsconfig.build.json b/packages/composable-controller/tsconfig.build.json index 779d385a6ab..249f327913d 100644 --- a/packages/composable-controller/tsconfig.build.json +++ b/packages/composable-controller/tsconfig.build.json @@ -8,6 +8,9 @@ "references": [ { "path": "../base-controller/tsconfig.build.json" + }, + { + "path": "../messenger/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/composable-controller/tsconfig.json b/packages/composable-controller/tsconfig.json index cc814f313b7..0d608a82545 100644 --- a/packages/composable-controller/tsconfig.json +++ b/packages/composable-controller/tsconfig.json @@ -7,6 +7,9 @@ { "path": "../base-controller" }, + { + "path": "../messenger" + }, { "path": "../json-rpc-engine" } From 2595e29d92809c00c60f5fd7313b004ea4914fb9 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Tue, 21 Oct 2025 19:48:25 +0200 Subject: [PATCH 210/247] update changelog --- packages/composable-controller/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/composable-controller/CHANGELOG.md b/packages/composable-controller/CHANGELOG.md index 8bfbf05ac4c..32a4bf9441b 100644 --- a/packages/composable-controller/CHANGELOG.md +++ b/packages/composable-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Migrate `ComposableController` to new `Messenger` from `@metamask/messenger` ([#6710](https://github.com/MetaMask/core/pull/6710)) + - Previously, the controller accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ### Fixed - Resolve incompatibility of `ChildControllerStateChangeEvents` type with `BaseController` (when used in the `Events` type argument of `ComposableControllerMessenger`) by removing unnecessary nested logic from definition ([#6904](https://github.com/MetaMask/core/pull/6904)) From 2358bbe5fa354429a32cde519f2f6f7d5773dac9 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 13 Aug 2025 15:35:25 -0230 Subject: [PATCH 211/247] feat: Migrate controller guidelines and examples to new Messenger Migrate the controller guidelines and the `sample-controllers` package to the `next` export of the `@metamask/base-controller` package and the new `Messenger` class from `@metamask/messenger`. --- docs/controller-guidelines.md | 306 ++++++++---------- docs/data-services.md | 2 +- packages/sample-controllers/CHANGELOG.md | 1 + packages/sample-controllers/package.json | 1 + .../src/sample-gas-prices-controller.test.ts | 32 +- .../src/sample-gas-prices-controller.ts | 57 ++-- .../sample-gas-prices-service.test.ts | 26 +- .../sample-gas-prices-service.ts | 31 +- .../src/sample-petnames-controller.test.ts | 29 +- .../src/sample-petnames-controller.ts | 39 +-- .../sample-controllers/tsconfig.build.json | 1 + packages/sample-controllers/tsconfig.json | 1 + yarn.lock | 25 +- 13 files changed, 284 insertions(+), 267 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index fdf1a32cac6..60e99f89ab6 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -231,12 +231,10 @@ If the recipient controller supports the messaging system, however, the callback const name = 'FooController'; -type FooControllerMessenger = RestrictedMessenger< +type FooControllerMessenger = Messenger< typeof name, - never, - never, - never, - never + FooControllerActions, + FooControllerEvents >; class FooController extends BaseController< @@ -255,15 +253,33 @@ class FooController extends BaseController< // === Client repo === -const rootMessenger = new Messenger<'BarController:stateChange', never>(); -const barControllerMessenger = rootMessenger.getRestricted({ - name: 'BarController', +const rootMessenger = new Messenger<'Root', RootActions, RootEvents>({ + namespace: 'Root', +}); +const barControllerMessenger = new Messenger< + 'BarController', + BarControllerActions, + BarControllerEvents, + typeof rootMessenger +>({ + namespace: 'BarController', + parent: rootMessenger, }); const barController = new BarController({ messenger: barControllerMessenger, }); -const fooControllerMessenger = rootMessenger.getRestricted({ - name: 'FooController', +const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerActions, + FooControllerEvents | BarControllerStateChange, + typeof rootMessenger +>({ + namespace: 'FooController', + parent: rootMessenger, +}); +rootMessenger.delegate({ + events: ['BarController:stateChange' as const], + messenger: fooControllerMessenger, }); const fooController = new FooController({ messenger: fooControllerMessenger, @@ -312,12 +328,10 @@ However, this pattern can be replaced with the use of the messenger: const name = 'FooController'; -type FooControllerMessenger = RestrictedMessenger< +type FooControllerMessenger = Messenger< typeof name, - never, - never, - never, - never + FooControllerActions, + FooControllerEvents >; class FooController extends BaseController< @@ -336,9 +350,17 @@ class FooController extends BaseController< // === Client repo === -const rootMessenger = new Messenger<'FooController:someEvent', never>(); -const fooControllerMessenger = rootMessenger.getRestricted({ - name: 'FooController', +const rootMessenger = new Messenger<'Root', RootActions, RootEvents>({ + namespace: 'Root', +}); +const fooControllerMessenger = new Messenger< + 'FooController', + FooControllerActions, + FooControllerEvents, + typeof rootMessenger +>({ + namespace: 'FooController', + parent: rootMessenger, }); const fooController = new FooController({ messenger: fooControllerMessenger, @@ -507,37 +529,19 @@ A controller should define and export a type union that holds all of its actions The name of this type should be `${ControllerName}Actions`. -This type should be only passed to `RestrictedMessenger` as the 2nd type parameter. It should _not_ be included in its 4th type parameter, as that is is used for external actions. - -🚫 **`FooController['type']` is passed as the 4th type parameter** - -```typescript -export type FooControllerActions = - | FooControllerUpdateCurrencyAction - | FooControllerUpdateRatesAction; - -export type FooControllerMessenger = RestrictedMessenger< - 'FooController', - FooControllerActions, - never, - FooControllerActions['type'], - never ->; -``` +This type should be passed to `Messenger` as the 2nd type parameter. It should _not_ include external actions. -✅ **`never` is passed as the 4th type parameter (assuming no external actions)** +✅ **`FooControllerActions` is passed as the 2nd type parameter (assuming no external actions)** ```typescript export type FooControllerActions = | FooControllerUpdateCurrencyAction | FooControllerUpdateRatesAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', FooControllerActions, - never, - never, - never + FooControllerEvents >; ``` @@ -547,189 +551,178 @@ A controller should define and export a type union that holds all of its events. The name of this type should be `${ControllerName}Events`. -This type should be only passed to `RestrictedMessenger` as the 3rd type parameter. It should _not_ be included in its 5th type parameter, as that is is used for external events. +This type should be passed to `Messenger` as the 3rd type parameter. It should _not_ include external events. -🚫 **`FooControllerEvents['type']` is passed as the 5th type parameter** +✅ **`FooControllerEvents` is passed as the 2nd type parameter (assuming no external events)** ```typescript export type FooControllerEvents = | FooControllerMessageReceivedEvent | FooControllerNotificationAddedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - FooControllerEvents, - never, - FooControllerEvents['type'] ->; -``` - -✅ **`never` is passed as the 5th type parameter (assuming no external events)** - -```typescript -export type FooControllerEvents = - | FooControllerMessageReceivedEvent - | FooControllerNotificationAddedEvent; - -export type FooControllerMessenger = RestrictedMessenger< - 'FooController', - never, - FooControllerEvents, - never, - never + FooControllerActions, + FooControllerEvents >; ``` ## Define, but do not export, a type union for external action types -A controller may wish to call actions defined by other controllers, and therefore will need to define them in the messenger's allowlist. +A controller may wish to call actions defined by other controllers, and therefore will need to include them in the controller messenger's type definition. -In this case, the controller should group these types into a type union so that they can be easily passed to the `RestrictedMessenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. +In this case, the controller should group these types into a type union so that they can be easily passed to the `Messenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. The name of this type should be `AllowedActions`. -This type should not only be passed to `RestrictedMessenger` as the 2nd type parameter, but should also be included in its 4th type parameter. +This type should be passed to `Messenger` as part of the 2nd type parameter, in a type union with internal actions. -🚫 **`never` is passed as the 4th type parameter** +🚫 **`AllowedActions` is exported** ```typescript +/* === packages/foo-controller/src/FooController.ts === */ + +export type FooControllerActions = + | FooControllerUpdateCurrencyAction + | FooControllerUpdateRatesAction; + export type AllowedActions = | BarControllerDoSomethingAction | BarControllerDoSomethingElseAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - AllowedActions, - never, - never, - never + FooControllerActions | AllowedActions, + FooControllerEvents >; + +/* === packages/foo-controller/src/index.ts === */ + +export type { AllowedActions } from '@metamask/foo-controller'; ``` -🚫 **`AllowedActions['type']` is passed as the 4th type parameter, but `AllowedActions` is exported** +🚫 **External actions are included in controller action type** ```typescript -/* === packages/foo-controller/src/FooController.ts === */ - -export type AllowedActions = +export type FooControllerActions = + | FooControllerUpdateCurrencyAction + | FooControllerUpdateRatesAction | BarControllerDoSomethingAction | BarControllerDoSomethingElseAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - AllowedActions, - never, - AllowedActions['type'], - never + FooControllerActions, + FooControllerEvents >; - -/* === packages/foo-controller/src/index.ts === */ - -export type { AllowedActions } from '@metamask/foo-controller'; ``` -✅ **`AllowedActions['type']` is passed as the 4th type parameter, and `AllowedActions` is _not_ exported** +✅ **`AllowedActions` is included in the 2nd type parameter but is _not_ exported** ```typescript +export type FooControllerActions = + | FooControllerUpdateCurrencyAction + | FooControllerUpdateRatesAction; + type AllowedActions = | BarControllerDoSomethingAction | BarControllerDoSomethingElseAction; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - AllowedActions, - never, - AllowedActions['type'], - never + FooControllerActions | AllowedActions, + FooControllerEvents >; ``` -If, in a test, you need to access all of the actions included in a controller's messenger allowlist, use the [`ExtractAvailableAction` utility type](../packages/base-controller/tests/helpers.ts): +If, in a test, you need to access all of the actions supported by a messenger, use the [`MessengerActions` utility type](../packages/messenger/src/Messenger.ts): ```typescript -// NOTE: You may need to adjust the path depending on where you are -import { ExtractAvailableAction } from '../../base-controller/tests/helpers'; +import type { MessengerActions, MessengerEvents } from '@metamask/messenger'; const messenger = new Messenger< - ExtractAvailableAction, - never + controllerName, + MessengerActions, + MessengerEvents >(); ``` ## Define, but do not export, a type union for external event types -A controller may wish to subscribe to events defined by other controllers, and therefore will need to define them in the messenger's allowlist. +A controller may wish to subscribe to events defined by other controllers, and therefore will need to include them in the controller messenger's type definition. -In this case, the controller should group these types into a type union so that they can be easily passed to the `RestrictedMessenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. +In this case, the controller should group these types into a type union so that they can be easily passed to the `Messenger` type. However, it should not export this type, as it would then be re-exporting types that another package has already exported. The name of this type should be `AllowedEvents`. -This type should not only be passed to `RestrictedMessenger` as the 3rd type parameter, but should also be included in its 5th type parameter. +This type should be passed to `Messenger` as part of the 3rd type parameter, in a type union with internal events. -🚫 **`never` is passed as the 5th type parameter** +🚫 **`AllowedEvents` is exported** ```typescript +/* === packages/foo-controller/src/FooController.ts === */ +export type FooControllerEvents = + | FooControllerMessageReceivedEvent + | FooControllerNotificationAddedEvent; + export type AllowedEvents = | BarControllerSomethingHappenedEvent | BarControllerSomethingElseHappenedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - AllowedEvents, - never, - never + FooControllerActions, + FooControllerEvents | AllowedEvents >; + +/* === packages/foo-controller/src/index.ts === */ + +export type { AllowedEvents } from '@metamask/foo-controller'; ``` -🚫 **`AllowedEvents['type']` is passed as the 5th type parameter, but `AllowedEvents` is exported** +🚫 **External events are included in controller actieventn type** ```typescript -/* === packages/foo-controller/src/FooController.ts === */ - -export type AllowedEvents = +export type FooControllerEvents = + | FooControllerMessageReceivedEvent + | FooControllerNotificationAddedEvent | BarControllerSomethingHappenedEvent | BarControllerSomethingElseHappenedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - AllowedEvents, - never, - AllowedEvents['type'] + FooControllerActions, + FooControllerEvents >; - -/* === packages/foo-controller/src/index.ts === */ - -export type { AllowedEvents } from '@metamask/foo-controller'; ``` -✅ **`AllowedEvents['type']` is passed as the 5th type parameter, and `AllowedEvents` is _not_ exported** +✅ **`AllowedEvents` is included in the 3nd type parameter but is _not_ exported** ```typescript +export type FooControllerEvents = + | FooControllerMessageReceivedEvent + | FooControllerNotificationAddedEvent; + type AllowedEvents = | BarControllerSomethingHappenedEvent | BarControllerSomethingElseHappenedEvent; -export type FooControllerMessenger = RestrictedMessenger< +export type FooControllerMessenger = Messenger< 'FooController', - never, - AllowedEvents, - never, - AllowedEvents['type'] + FooControllerActions, + FooControllerEvents | AllowedEvents >; ``` -If, in a test, you need to access all of the events included in a controller's messenger allowlist, use the [`ExtractAvailableEvent` utility type](../packages/base-controller/tests/helpers.ts): +If, in a test, you need to access all of the events supported by a messenger, use the [`MessengerEvents` utility type](../packages/messenger/src/Messenger.ts): ```typescript -// NOTE: You may need to adjust the path depending on where you are -import { ExtractAvailableEvent } from '../../base-controller/tests/helpers'; +import type { MessengerActions, MessengerEvents } from '@metamask/messenger'; const messenger = new Messenger< - never, - ExtractAvailableEvent + controllerName, + MessengerActions, + MessengerEvents >(); ``` @@ -792,25 +785,17 @@ export type AllowedEvents = | ApprovalControllerApprovalRequestApprovedEvent | ApprovalControllerApprovalRequestRejectedEvent; -export type SwapsControllerMessenger = RestrictedMessenger< +export type SwapsControllerMessenger = Messenger< 'SwapsController', SwapsControllerActions | AllowedActions, - SwapsControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SwapsControllerEvents | AllowedEvents >; ``` A messenger that allows no actions or events (whether internal or external) looks like this: ```typescript -export type SwapsControllerMessenger = RestrictedMessenger< - 'SwapsController', - never, - never, - never, - never ->; +export type FooServiceMessenger = Messenger<'FooService', never, never>; ``` ## Define and export a type for the controller's state @@ -1175,14 +1160,14 @@ class AccountsController extends BaseController< import { AccountsControllerGetStateAction } from '@metamask/accounts-controller'; +// Other type definitions + type AllowedActions = AccountsControllerGetStateAction; -type PreferencesControllerMessenger = RestrictedMessenger< +type PreferencesControllerMessenger = Messenger< 'PreferencesController', - AllowedActions, - never, - AllowedActions['type'], - never + PreferencesControllerActions | AllowedActions, + PreferencesControllerEvents >; class PreferencesController extends BaseController< @@ -1271,16 +1256,15 @@ export type AccountsControllerGetInactiveAccountsAction = { handler: AccountsController['getInactiveAccounts']; }; -type AccountsControllerActions = +export type AccountsControllerActions = + /// Other actions | AccountsControllerGetActiveAccountAction | AccountsControllerGetInactiveAccountsAction; -export type AccountsControllerMessenger = RestrictedMessenger< +export type AccountsControllerMessenger = Messenger< 'AccountsController', AccountsControllerActions, - never, - never, - never + AccountsControllerEvents >; class AccountsController extends BaseController { @@ -1304,12 +1288,10 @@ type AllowedActions = | AccountsControllerGetActiveAccountsAction | AccountsControllerGetInactiveAccountsAction; -export type TokensControllerMessenger = RestrictedMessenger< +export type TokensControllerMessenger = Messenger< 'TokensController', - AllowedActions, - never, - AllowedActions['type'], - never + TokensControllerActions | AllowedActions, + TokensControllerEvents >; class TokensController extends BaseController { @@ -1385,12 +1367,10 @@ export type AccountsControllerGetStateAction = ControllerGetStateAction< type AccountsControllerActions = AccountsControllerGetStateAccountAction; -export type AccountsControllerMessenger = RestrictedMessenger< +export type AccountsControllerMessenger = Messenger< 'AccountsController', AccountsControllerActions, - never, - never, - never + AccountsControllerEvents, >; /* === This repo: packages/tokens-controller/src/TokensController.ts === */ @@ -1402,12 +1382,10 @@ import { accountsControllerSelectors } from '@metamask/accounts-controller'; type AllowedActions = AccountsControllerGetStateAction; -export type TokensControllerMessenger = RestrictedMessenger< +export type TokensControllerMessenger = Messenger< 'TokensController', - AllowedActions, - never, - AllowedActions['type'], - never + TokensControllerActions | AllowedActions, + TokensControllerEvents >; class TokensController extends BaseController { diff --git a/docs/data-services.md b/docs/data-services.md index 57d79671c0b..b00920b77a0 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -78,7 +78,7 @@ Next we'll define the messenger. We give the messenger a namespace, and we expos ```typescript // (top of file) -import type { RestrictedMessenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/base-controller'; const SERVICE_NAME = 'GasPricesService'; diff --git a/packages/sample-controllers/CHANGELOG.md b/packages/sample-controllers/CHANGELOG.md index 221507b9201..a486e0e44e8 100644 --- a/packages/sample-controllers/CHANGELOG.md +++ b/packages/sample-controllers/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Migrate to new `Messenger` class ([#6335](https://github.com/MetaMask/core/pull/6335)) - Bump `@metamask/network-controller` from `^24.2.2` to `^24.3.0` ([#6883](https://github.com/MetaMask/core/pull/6883)) ## [2.0.1] diff --git a/packages/sample-controllers/package.json b/packages/sample-controllers/package.json index 59203040d7a..2e6cf450715 100644 --- a/packages/sample-controllers/package.json +++ b/packages/sample-controllers/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.1", + "@metamask/messenger": "^0.1.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts index 6159414c133..fd7744a49b5 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts @@ -1,12 +1,13 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { SampleGasPricesController } from '@metamask/sample-controllers'; import type { SampleGasPricesControllerMessenger } from '@metamask/sample-controllers'; import { flushPromises } from '../../../tests/helpers'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { buildMockGetNetworkClientById } from '../../network-controller/tests/helpers'; describe('SampleGasPricesController', () => { @@ -301,7 +302,7 @@ describe('SampleGasPricesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -362,8 +363,11 @@ describe('SampleGasPricesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + // Use `string` rather than 'Root' here to allow registering actions and publishing events from + // any namespace in tests. + string, + MessengerActions, + MessengerEvents >; /** @@ -389,7 +393,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** @@ -402,13 +406,9 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesControllerMessenger { - return rootMessenger.getRestricted({ - name: 'SampleGasPricesController', - allowedActions: [ - 'SampleGasPricesService:fetchGasPrices', - 'NetworkController:getNetworkClientById', - ], - allowedEvents: ['NetworkController:stateChange'], + return new Messenger({ + namespace: 'SampleGasPricesController', + parent: rootMessenger, }); } diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.ts b/packages/sample-controllers/src/sample-gas-prices-controller.ts index c6db87b3f32..8bcec969f01 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.ts @@ -1,10 +1,10 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction, @@ -65,9 +65,9 @@ export type SampleGasPricesControllerState = { */ const gasPricesControllerMetadata = { gasPricesByChainId: { + includeInDebugSnapshot: false, includeInStateLogs: true, persist: true, - anonymous: false, usedInUi: true, }, } satisfies StateMetadata; @@ -137,12 +137,10 @@ type AllowedEvents = NetworkControllerStateChangeEvent; * The messenger restricted to actions and events accessed by * {@link SampleGasPricesController}. */ -export type SampleGasPricesControllerMessenger = RestrictedMessenger< +export type SampleGasPricesControllerMessenger = Messenger< typeof controllerName, SampleGasPricesControllerActions | AllowedActions, - SampleGasPricesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SampleGasPricesControllerEvents | AllowedEvents >; // === CONTROLLER DEFINITION === @@ -153,7 +151,7 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * @example * * ``` ts - * import { Messenger } from '@metamask/base-controller'; + * import { Messenger } from '@metamask/messenger'; * import type { * NetworkControllerActions, * NetworkControllerEvents, @@ -170,18 +168,23 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * selectGasPrices, * } from '@metamask/sample-controllers'; * - * const globalMessenger = new Messenger< + * const rootMessenger = new Messenger< + * 'Root', * SampleGasPricesServiceActions * | SampleGasPricesControllerActions * | NetworkControllerActions, * SampleGasPricesServiceEvents * | SampleGasPricesControllerEvents * | NetworkControllerEvents - * >(); - * const gasPricesServiceMessenger = globalMessenger.getRestricted({ - * name: 'SampleGasPricesService', - * allowedActions: [], - * allowedEvents: [], + * >({ namespace: 'Root' }); + * const gasPricesServiceMessenger = new Messenger< + * 'SampleGasPricesService', + * SampleGasPricesServiceActions, + * SampleGasPricesServiceEvents, + * typeof rootMessenger, + * >({ + * namespace: 'SampleGasPricesService', + * parent: rootMessenger, * }); * // Instantiate the service to register its actions on the messenger * new SampleGasPricesService({ @@ -189,10 +192,14 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * // We assume you're using this in the browser. * fetch, * }); - * const gasPricesControllerMessenger = globalMessenger.getRestricted({ - * name: 'SampleGasPricesController', - * allowedActions: ['NetworkController:getNetworkClientById'], - * allowedEvents: ['NetworkController:stateChange'], + * const gasPricesControllerMessenger = new Messenger< + * 'SampleGasPricesController', + * SampleGasPricesControllerActions | NetworkControllerGetNetworkClientByIdAction, + * SampleGasPricesControllerEvents | NetworkControllerStateChangeEvent, + * typeof rootMessenger, + * >({ + * namespace: 'SampleGasPricesController', + * parent: rootMessenger, * }); * // Instantiate the controller to register its actions on the messenger * new SampleGasPricesController({ @@ -200,11 +207,11 @@ export type SampleGasPricesControllerMessenger = RestrictedMessenger< * }); * * // Later... - * await globalMessenger.call( + * await rootMessenger.call( * 'SampleGasPricesController:updateGasPrices', * { chainId: '0x42' }, * ); - * const gasPricesControllerState = await globalMessenger.call( + * const gasPricesControllerState = await rootMessenger.call( * 'SampleGasPricesController:getState', * ); * gasPricesControllerState.gasPricesByChainId @@ -246,12 +253,12 @@ export class SampleGasPricesController extends BaseController< }, }); - this.messagingSystem.registerMethodActionHandlers( + this.messenger.registerMethodActionHandlers( this, MESSENGER_EXPOSED_METHODS, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:stateChange', this.#onSelectedNetworkClientIdChange.bind(this), (networkControllerState) => @@ -267,7 +274,7 @@ export class SampleGasPricesController extends BaseController< * @param args.chainId - The chain ID for which to fetch gas prices. */ async updateGasPrices({ chainId }: { chainId: Hex }) { - const gasPricesResponse = await this.messagingSystem.call( + const gasPricesResponse = await this.messenger.call( 'SampleGasPricesService:fetchGasPrices', chainId, ); @@ -291,7 +298,7 @@ export class SampleGasPricesController extends BaseController< ) { const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', selectedNetworkClientId, ); diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts index d7628dc190c..159ba031095 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts @@ -1,15 +1,15 @@ -import { Messenger } from '@metamask/base-controller'; import { HttpError } from '@metamask/controller-utils'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import nock from 'nock'; import { useFakeTimers } from 'sinon'; import type { SinonFakeTimers } from 'sinon'; import type { SampleGasPricesServiceMessenger } from './sample-gas-prices-service'; import { SampleGasPricesService } from './sample-gas-prices-service'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; describe('SampleGasPricesService', () => { let clock: SinonFakeTimers; @@ -293,8 +293,11 @@ describe('SampleGasPricesService', () => { * required by the service under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + // Use `string` rather than 'Root' here to allow registering actions and publishing events from + // any namespace in tests. + string, + MessengerActions, + MessengerEvents >; /** @@ -304,7 +307,7 @@ type RootMessenger = Messenger< * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** @@ -317,10 +320,9 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesServiceMessenger { - return rootMessenger.getRestricted({ - name: 'SampleGasPricesService', - allowedActions: [], - allowedEvents: [], + return new Messenger({ + namespace: 'SampleGasPricesService', + parent: rootMessenger, }); } diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts index 5117114bc7c..1639cce3cd3 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts @@ -1,4 +1,3 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { CreateServicePolicyOptions, ServicePolicy, @@ -8,6 +7,7 @@ import { fromHex, HttpError, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import { hasProperty, isPlainObject, type Hex } from '@metamask/utils'; import type { SampleGasPricesServiceMethodActions } from './sample-gas-prices-service-method-action-types'; @@ -49,12 +49,10 @@ type AllowedEvents = never; * The messenger which is restricted to actions and events accessed by * {@link SampleGasPricesService}. */ -export type SampleGasPricesServiceMessenger = RestrictedMessenger< +export type SampleGasPricesServiceMessenger = Messenger< typeof serviceName, SampleGasPricesServiceActions | AllowedActions, - SampleGasPricesServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SampleGasPricesServiceEvents | AllowedEvents >; // === SERVICE DEFINITION === @@ -76,20 +74,25 @@ type GasPricesResponse = { * @example * * ``` ts - * import { Messenger } from '@metamask/base-controller'; + * import { Messenger } from '@metamask/messenger'; * import type { * SampleGasPricesServiceActions, * SampleGasPricesServiceEvents, * } from '@metamask/sample-controllers'; * - * const globalMessenger = new Messenger< - * SampleGasPricesServiceActions, + * const rootMessenger = new Messenger< + * 'Root', + * SampleGasPricesServiceActions * SampleGasPricesServiceEvents - * >(); - * const gasPricesServiceMessenger = globalMessenger.getRestricted({ - * name: 'SampleGasPricesService', - * allowedActions: [], - * allowedEvents: [], + * >({ namespace: 'Root' }); + * const gasPricesServiceMessenger = new Messenger< + * 'SampleGasPricesService', + * SampleGasPricesServiceActions, + * SampleGasPricesServiceEvents, + * typeof rootMessenger, + * >({ + * namespace: 'SampleGasPricesService', + * parent: rootMessenger, * }); * // Instantiate the service to register its actions on the messenger * new SampleGasPricesService({ @@ -99,7 +102,7 @@ type GasPricesResponse = { * * // Later... * // Fetch gas prices for Mainnet - * const gasPrices = await globalMessenger.call( + * const gasPrices = await rootMessenger.call( * 'SampleGasPricesService:fetchGasPrices', * '0x1', * ); diff --git a/packages/sample-controllers/src/sample-petnames-controller.test.ts b/packages/sample-controllers/src/sample-petnames-controller.test.ts index 40163b21703..9cc34615060 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.test.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.test.ts @@ -1,11 +1,12 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { SamplePetnamesControllerMessenger } from './sample-petnames-controller'; import { SamplePetnamesController } from './sample-petnames-controller'; -import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; import { PROTOTYPE_POLLUTION_BLOCKLIST } from '../../controller-utils/src/util'; describe('SamplePetnamesController', () => { @@ -197,7 +198,7 @@ describe('SamplePetnamesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -258,8 +259,11 @@ describe('SamplePetnamesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + // Use `string` rather than 'Root' here to allow registering actions and publishing events from + // any namespace in tests. + string, + MessengerActions, + MessengerEvents >; /** @@ -285,7 +289,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: 'Root' }); } /** @@ -298,10 +302,9 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SamplePetnamesControllerMessenger { - return rootMessenger.getRestricted({ - name: 'SamplePetnamesController', - allowedActions: [], - allowedEvents: [], + return new Messenger({ + namespace: 'SamplePetnamesController', + parent: rootMessenger, }); } diff --git a/packages/sample-controllers/src/sample-petnames-controller.ts b/packages/sample-controllers/src/sample-petnames-controller.ts index cf7a4a70784..bf0e7499441 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.ts @@ -1,11 +1,11 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, StateMetadata, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { isSafeDynamicKey } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import type { SamplePetnamesControllerMethodActions } from './sample-petnames-controller-method-action-types'; @@ -41,9 +41,9 @@ export type SamplePetnamesControllerState = { */ const samplePetnamesControllerMetadata = { namesByChainIdAndAddress: { + includeInDebugSnapshot: false, includeInStateLogs: true, persist: true, - anonymous: false, usedInUi: true, }, } satisfies StateMetadata; @@ -111,12 +111,10 @@ type AllowedEvents = never; * The messenger restricted to actions and events accessed by * {@link SamplePetnamesController}. */ -export type SamplePetnamesControllerMessenger = RestrictedMessenger< +export type SamplePetnamesControllerMessenger = Messenger< typeof controllerName, SamplePetnamesControllerActions | AllowedActions, - SamplePetnamesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + SamplePetnamesControllerEvents | AllowedEvents >; // === CONTROLLER DEFINITION === @@ -128,27 +126,32 @@ export type SamplePetnamesControllerMessenger = RestrictedMessenger< * @example * * ``` ts - * import { Messenger } from '@metamask/base-controller'; + * import { Messenger } from '@metamask/messenger'; * import type { * SamplePetnamesControllerActions, * SamplePetnamesControllerEvents, * } from '@metamask/sample-controllers'; * - * const globalMessenger = new Messenger< + * const rootMessenger = new Messenger< + * 'Root', * SamplePetnamesControllerActions, * SamplePetnamesControllerEvents - * >(); - * const samplePetnamesMessenger = globalMessenger.getRestricted({ - * name: 'SamplePetnamesController', - * allowedActions: [], - * allowedEvents: [], + * >({ namespace: 'Root' }); + * const samplePetnamesMessenger = new Messenger< + * 'SamplePetnamesController', + * SamplePetnamesControllerActions, + * SamplePetnamesControllerEvents, + * typeof rootMessenger, + * >({ + * namespace: 'SamplePetnamesController', + * parent: rootMessenger, * }); * // Instantiate the controller to register its actions on the messenger * new SamplePetnamesController({ * messenger: samplePetnamesMessenger, * }); * - * globalMessenger.call( + * rootMessenger.call( * 'SamplePetnamesController:assignPetname', * [ * '0x1', @@ -156,7 +159,7 @@ export type SamplePetnamesControllerMessenger = RestrictedMessenger< * 'Primary Account', * ], * ); - * const samplePetnamesControllerState = await globalMessenger.call( + * const samplePetnamesControllerState = await rootMessenger.call( * 'SamplePetnamesController:getState', * ); * samplePetnamesControllerState.namesByChainIdAndAddress @@ -193,7 +196,7 @@ export class SamplePetnamesController extends BaseController< }, }); - this.messagingSystem.registerMethodActionHandlers( + this.messenger.registerMethodActionHandlers( this, MESSENGER_EXPOSED_METHODS, ); diff --git a/packages/sample-controllers/tsconfig.build.json b/packages/sample-controllers/tsconfig.build.json index 37e83ff4f7f..d71ced03932 100644 --- a/packages/sample-controllers/tsconfig.build.json +++ b/packages/sample-controllers/tsconfig.build.json @@ -7,6 +7,7 @@ }, "references": [ { "path": "../../packages/base-controller/tsconfig.build.json" }, + { "path": "../../packages/messenger/tsconfig.build.json" }, { "path": "../../packages/network-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/sample-controllers/tsconfig.json b/packages/sample-controllers/tsconfig.json index 42ff3e1c18a..65b458897f2 100644 --- a/packages/sample-controllers/tsconfig.json +++ b/packages/sample-controllers/tsconfig.json @@ -6,6 +6,7 @@ "references": [ { "path": "../../packages/base-controller" }, { "path": "../../packages/controller-utils" }, + { "path": "../../packages/messenger" }, { "path": "../../packages/network-controller" } ], "include": ["../../types", "./src"], diff --git a/yarn.lock b/yarn.lock index 5d09b848de7..58d7bf44ea5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4015,6 +4015,13 @@ __metadata: languageName: unknown linkType: soft +"@metamask/messenger@npm:^0.1.0": + version: 0.1.0 + resolution: "@metamask/messenger@npm:0.1.0" + checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 + languageName: node + linkType: hard + "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4610,6 +4617,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.1.0" "@metamask/network-controller": "npm:^24.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -13946,12 +13954,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": - version: 7.7.3 - resolution: "semver@npm:7.7.3" +"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.6.3 + resolution: "semver@npm:7.6.3" bin: semver: bin/semver.js - checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 + checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 languageName: node linkType: hard @@ -13964,6 +13972,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.4": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 + languageName: node + linkType: hard + "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0" From bcd4e747cef42d1cf812ceda88913dea6228a7a5 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 20 Aug 2025 15:26:24 -0230 Subject: [PATCH 212/247] Update docs and sample-controllers to handle messagingSystem rename --- docs/controller-guidelines.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 60e99f89ab6..43109ed5ab0 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -344,7 +344,7 @@ class FooController extends BaseController< } doSomething() { - this.messagingSystem.publish('FooController:someEvent'); + this.messenger.publish('FooController:someEvent'); } } @@ -1309,7 +1309,7 @@ class TokensController extends BaseController { // Now TokensController no longer needs an instance of AccountsController to // access the list of active accounts, which is good... const tokens = getTokens( - this.messagingSystem.call('AccountsController:getActiveAccounts'), + this.messenger.call('AccountsController:getActiveAccounts'), ); // ... do something with tokens ... } @@ -1401,7 +1401,7 @@ class TokensController extends BaseController { fetchTokens() { // Now TokensController can use the selector in combination with the state - const tokensControllerState = this.messagingSystem.call( + const tokensControllerState = this.messenger.call( 'AccountsController:getState', ); const accounts = accountsControllerSelectors.selectActiveAccounts( From 2723744802ae25a31ac18512dc384dc670234fdb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 20 Aug 2025 15:35:35 -0230 Subject: [PATCH 213/247] Update data services docs --- docs/data-services.md | 75 ++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/docs/data-services.md b/docs/data-services.md index b00920b77a0..1dba0ff8725 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -78,7 +78,7 @@ Next we'll define the messenger. We give the messenger a namespace, and we expos ```typescript // (top of file) -import type { Messenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; const SERVICE_NAME = 'GasPricesService'; @@ -95,21 +95,19 @@ export type GasPricesServiceEvents = never; type AllowedEvents = never; -export type GasPricesServiceMessenger = RestrictedMessenger< +export type GasPricesServiceMessenger = Messenger< typeof SERVICE_NAME, GasPricesServiceActions | AllowedActions, - GasPricesServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + GasPricesServiceEvents | AllowedEvents >; // ... ``` -Note that we need to add `@metamask/base-controller` as a direct dependency of the package to bring in the `RestrictedMessenger` type (here we assume that our package is called `@metamask/gas-prices-controller`): +Note that we need to add `@metamask/messenger` as a direct dependency of the package to bring in the `Messenger` type (here we assume that our package is called `@metamask/gas-prices-controller`): ```shell -yarn workspace @metamask/gas-prices-controller add @metamask/base-controller +yarn workspace @metamask/gas-prices-controller add @metamask/messenger ``` Finally we will register the method as an action handler on the messenger: @@ -145,7 +143,7 @@ export class GasPricesService {
View whole file
```typescript -import type { RestrictedMessenger } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; const SERVICE_NAME = 'GasPricesService'; @@ -162,12 +160,10 @@ export type GasPricesServiceEvents = never; type AllowedEvents = never; -export type GasPricesServiceMessenger = RestrictedMessenger< +export type GasPricesServiceMessenger = Messenger< typeof SERVICE_NAME, GasPricesServiceActions | AllowedActions, - GasPricesServiceEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + GasPricesServiceEvents | AllowedEvents >; type GasPricesResponse = { @@ -272,10 +268,12 @@ import { Messenger } from '@metamask/base-controller'; // ... function buildMessenger(): GasPricesServiceMessenger { - return new Messenger().getRestricted({ - name: 'GasPricesService', - allowedActions: [], - allowedEvents: [], + return new Messenger< + 'GasPricesService', + GasPricesServiceActions, + GasPricesServiceEvents + >({ + namespace: 'GasPricesService', }); } ``` @@ -321,7 +319,11 @@ describe('GasPricesService', () => { ```typescript import nock from 'nock'; -import type { GasPricesServiceMessenger } from './gas-prices-service'; +import type { + GasPricesServiceMessenger, + GasPricesServiceActions, + GasPricesServiceEvents, +} from './gas-prices-service'; import { GasPricesService } from './gas-prices-service'; describe('GasPricesService', () => { @@ -375,10 +377,12 @@ describe('GasPricesService', () => { }); function buildMessenger(): GasPricesServiceMessenger { - return new Messenger().getRestricted({ - name: 'GasPricesService', - allowedActions: [], - allowedEvents: [], + return new Messenger< + 'GasPricesService', + GasPricesServiceActions, + GasPricesServiceEvents + >({ + namespace: 'GasPricesService', }); } ``` @@ -387,7 +391,7 @@ function buildMessenger(): GasPricesServiceMessenger { ## How to use a data service -Let's say that we wanted to use our data service that we built above. To do this, we will instantiate the messenger for the data service — which itself relies on a global messenger — and then the data service itself. +Let's say that we wanted to use our data service that we built above. To do this, we will instantiate the messenger for the data service — which itself relies on a root messenger — and then the data service itself. First we need to import the data service: @@ -395,22 +399,29 @@ First we need to import the data service: import { GasPricesService } from '@metamask/gas-prices-service'; ``` -Then we create a global messenger: +Then we create a root messenger: ```typescript -const globalMessenger = new Messenger(); +const rootMessenger = new Messenger<'Root', AllActions, AllEvents>({ + namespace: 'Root', +}); ``` Then we create a messenger for the GasPricesService: ```typescript -const gasPricesServiceMessenger = globalMessenger.getRestricted({ - allowedActions: [], - allowedEvents: [], +const gasPricesServiceMessenger = new Messenger< + 'GasPricesService', + GasPricesServiceActions, + GasPricesServiceEvents, + typeof rootMessenger +>({ + namespace: 'GasPricesService', + parent: rootMessenger, }); ``` -Now we instantiate the data service to register the action handler on the global messenger. We assume we have a global `fetch` function available: +Now we instantiate the data service to register the action handler on the root messenger. We assume we have a global `fetch` function available: ```typescript const gasPricesService = new GasPricesService({ @@ -421,7 +432,7 @@ const gasPricesService = new GasPricesService({ Great! Now that we've set up the data service and its messenger action, we can use it somewhere else. -Let's say we wanted to use it in a controller. We'd just need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by passing it via the `allowedActions` option. +Let's say we wanted to use it in a controller. We'd just need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by delegating it from the root messenger. This code would probably be in the controller package itself. For instance, if we had a file `packages/send-controller/send-controller.ts`, we might have: @@ -436,12 +447,10 @@ type SendControllerEvents = ...; type AllowedEvents = ...; -type SendControllerMessenger = RestrictedMessenger< +type SendControllerMessenger = Messenger< 'SendController', SendControllerActions | AllowedActions, SendControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] >; ``` @@ -452,7 +461,7 @@ class SendController extends BaseController { // ... await someMethodThatUsesGasPrices() { - const gasPrices = await this.#messagingSystem.call( + const gasPrices = await this.messenger.call( 'GasPricesService:fetchGasPrices', ); // ... use gasPrices somehow ... From 1692a732e48db9b306a36634dbd7683d7d02a19a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 21 Aug 2025 15:09:01 -0230 Subject: [PATCH 214/247] Avoid using the term messaging system --- docs/controller-guidelines.md | 14 +++++++------- docs/data-services.md | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 43109ed5ab0..2b766cc2130 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -14,7 +14,7 @@ Controllers are foundational pieces within MetaMask's architecture: All controllers should inherit from `BaseController` from the `@metamask/base-controller` package. This provides a few benefits: - It defines a standard interface for all controllers. -- It introduces the messenger system, which is useful for interacting with other controllers without requiring direct access to them. +- It introduces the messenger, which is useful for interacting with other parts of the application without requiring a direct reference. - It enforces that `update` is the only way to modify the state of the controller and provides a way to listen for state updates via the messenger. - It simplifies initialization by consolidating constructor arguments into one options object. @@ -22,7 +22,7 @@ All controllers should inherit from `BaseController` from the `@metamask/base-co One of the uniquely identifying features of a controller is the ability to manage state. -If you have a class that does not capture any data in state, then your class does not need to inherit from `BaseController` (even if it uses the messaging system). +If you have a class that does not capture any data in state, then your class does not need to inherit from `BaseController` (even if it uses a messenger). ## Maintain a clear and concise API @@ -193,7 +193,7 @@ class FooController extends BaseController { } ``` -## Use the messaging system instead of callbacks +## Use the messenger instead of callbacks Prior to BaseController v2, it was common for a controller to respond to an event occurring within another controller (such a state change) by receiving an event listener callback which the client would bind ahead of time: @@ -222,7 +222,7 @@ const fooController = new FooController({ }); ``` -If the recipient controller supports the messaging system, however, the callback pattern is unnecessary. Using the messenger not only aligns the controller with `BaseController`, but also reduces the number of options that consumers need to remember in order to use the controller: +If the recipient controller uses a messenger, however, the callback pattern is unnecessary. Using the messenger not only aligns the controller with `BaseController`, but also reduces the number of options that consumers need to remember in order to use the controller: ✅ **The constructor subscribes to the `BarController:stateChange` event** @@ -286,7 +286,7 @@ const fooController = new FooController({ }); ``` -## Use the messaging system instead of event emitters +## Use the messenger instead of event emitters Some controllers expose an EventEmitter object so that other parts of the system can listen to them: @@ -1201,7 +1201,7 @@ class PreferencesController extends BaseController< ## Expose derived state using selectors instead of getters -Sometimes, for convenience, consumers want access to a higher-level representation of a controller's state. It is tempting to add a method to the controller which provides this representation, but this means that a consumer would need an entire instance of the controller on hand to use this method. Using the messaging system mitigates this problem, but then the consumer would need access to a messenger as well, which may be impossible in places like Redux selector functions. +Sometimes, for convenience, consumers want access to a higher-level representation of a controller's state. It is tempting to add a method to the controller which provides this representation, but this means that a consumer would need an entire instance of the controller on hand to use this method. Using the messenger mitigates this problem, but then the consumer would need access to a messenger as well, which may be impossible in places like Redux selector functions. To make it easier to share such representations across disparate parts of the codebase in a flexible fashion, you can define and export selector functions from your controller file instead. They should be placed under a `${controllerName}Selectors` object and then exported. @@ -1241,7 +1241,7 @@ class TokensController extends BaseController { } ``` -🚫 **Methods exposed via the messaging system** +🚫 **Methods exposed via the messenger** ```typescript /* === This repo: packages/accounts-controller/src/AccountsController.ts === */ diff --git a/docs/data-services.md b/docs/data-services.md index 1dba0ff8725..951f8e79021 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -2,14 +2,14 @@ ## What is a data service? -A **data service** is a pattern for making interactions with an external API (fetching token prices, storing accounts, etc.). It is implemented as a plain TypeScript class with methods that are exposed through the messaging system. +A **data service** is a pattern for making interactions with an external API (fetching token prices, storing accounts, etc.). It is implemented as a plain TypeScript class with methods that are exposed through a messenger. ## Why use this pattern? If you want to talk to an API, it might be tempting to define a method in the controller or a function in a separate file. However, implementing the data service pattern is advantageous for the following reasons: 1. The pattern provides an abstraction that allows for implementing and reusing strategies that are common when working with external APIs, such as batching, automatic retries with exponential backoff, etc. -2. By integrating with the messaging system, other parts of the application can make use of the data service without needing to go through the controller, or in fact, without needing a reference to the data service at all. +2. By integrating with a messenger, other parts of the application can make use of the data service without needing to go through the controller, or in fact, without needing a reference to the data service at all. ## How to create a data service From 7215dd07edce1f31ab5e2dc5eac1605879cd0a77 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 21 Aug 2025 15:13:46 -0230 Subject: [PATCH 215/247] Expand on data service delegation example --- docs/data-services.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/data-services.md b/docs/data-services.md index 951f8e79021..cfd3967d3dc 100644 --- a/docs/data-services.md +++ b/docs/data-services.md @@ -432,7 +432,7 @@ const gasPricesService = new GasPricesService({ Great! Now that we've set up the data service and its messenger action, we can use it somewhere else. -Let's say we wanted to use it in a controller. We'd just need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by delegating it from the root messenger. +Let's say we wanted to use `GasPricesService:fetchGasPrices` in a controller. First, that controller's messenger would need to include `GasPricesService:fetchGasPrices` in its type defintion. This code would probably be in the controller package itself. For instance, if we had a file `packages/send-controller/send-controller.ts`, we might have: @@ -454,6 +454,15 @@ type SendControllerMessenger = Messenger< >; ``` +Then we'll need to allow that controller's messenger access to `GasPricesService:fetchGasPrices` by delegating it from the root messenger: + +```typescript +rootMessenger.delegate({ + actions: ['GasPricesService:fetchGasPrices'], + messenger: sendControllerMessenger, +}); +``` + Then, later on in our controller, we could say: ```typescript From f784d4849d8933a1dd8f58876e6302b0bdbd44bb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 14:28:15 -0230 Subject: [PATCH 216/247] Fix typo Co-authored-by: Elliot Winkler --- docs/controller-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 2b766cc2130..e6cd3d8ac7f 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -680,7 +680,7 @@ export type FooControllerMessenger = Messenger< export type { AllowedEvents } from '@metamask/foo-controller'; ``` -🚫 **External events are included in controller actieventn type** +🚫 **External events are included in controller event type** ```typescript export type FooControllerEvents = From 53f2c02ad684b0a71efa3d649d3c52ace4981998 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 14:29:28 -0230 Subject: [PATCH 217/247] Remove 'as const' that doesn't seem necessary --- docs/controller-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index e6cd3d8ac7f..a8413a325f6 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -278,7 +278,7 @@ const fooControllerMessenger = new Messenger< parent: rootMessenger, }); rootMessenger.delegate({ - events: ['BarController:stateChange' as const], + events: ['BarController:stateChange'], messenger: fooControllerMessenger, }); const fooController = new FooController({ From 0e497c2a2ee420c38b611b78855856298499bfd9 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 17:26:37 -0230 Subject: [PATCH 218/247] Improve example title by dropping reference to type parameter order --- docs/controller-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index a8413a325f6..e143b729001 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -617,7 +617,7 @@ export type FooControllerMessenger = Messenger< >; ``` -✅ **`AllowedActions` is included in the 2nd type parameter but is _not_ exported** +✅ **`AllowedActions` is included in the actions type union but is _not_ exported** ```typescript export type FooControllerActions = @@ -696,7 +696,7 @@ export type FooControllerMessenger = Messenger< >; ``` -✅ **`AllowedEvents` is included in the 3nd type parameter but is _not_ exported** +✅ **`AllowedEvents` is included in the events type union but is _not_ exported** ```typescript export type FooControllerEvents = From 22a761eb9ecd757c8a5b69258367a2ff2abe354c Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 17:26:59 -0230 Subject: [PATCH 219/247] Fix mistake in type parameter number --- docs/controller-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index e143b729001..bd954e9f5df 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -553,7 +553,7 @@ The name of this type should be `${ControllerName}Events`. This type should be passed to `Messenger` as the 3rd type parameter. It should _not_ include external events. -✅ **`FooControllerEvents` is passed as the 2nd type parameter (assuming no external events)** +✅ **`FooControllerEvents` is passed as the 3rd type parameter (assuming no external events)** ```typescript export type FooControllerEvents = From 2ddbd1ad1b8385bfdda168fbdda55255c84580e5 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Thu, 28 Aug 2025 17:29:44 -0230 Subject: [PATCH 220/247] Improve example title consistency --- docs/controller-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index bd954e9f5df..25ac0375b7f 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -577,7 +577,7 @@ The name of this type should be `AllowedActions`. This type should be passed to `Messenger` as part of the 2nd type parameter, in a type union with internal actions. -🚫 **`AllowedActions` is exported** +🚫 **`AllowedActions` is included in the actions type union and _is_ exported** ```typescript /* === packages/foo-controller/src/FooController.ts === */ @@ -657,7 +657,7 @@ The name of this type should be `AllowedEvents`. This type should be passed to `Messenger` as part of the 3rd type parameter, in a type union with internal events. -🚫 **`AllowedEvents` is exported** +🚫 **`AllowedEvents` is included in the actions type union and _is_ exported** ```typescript /* === packages/foo-controller/src/FooController.ts === */ From 7ebddfd102b8a8301a138c83a7a2ce87bdaf708a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 2 Sep 2025 13:15:49 -0230 Subject: [PATCH 221/247] Use new mock namespace bypass --- .../src/sample-gas-prices-controller.test.ts | 8 ++++---- .../sample-gas-prices-service.test.ts | 8 ++++---- .../src/sample-petnames-controller.test.ts | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts index fd7744a49b5..29569e825f1 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts @@ -1,6 +1,8 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -363,9 +365,7 @@ describe('SampleGasPricesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - // Use `string` rather than 'Root' here to allow registering actions and publishing events from - // any namespace in tests. - string, + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -393,7 +393,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts index 159ba031095..1796ea2bd6e 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts @@ -1,6 +1,8 @@ import { HttpError } from '@metamask/controller-utils'; import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -293,9 +295,7 @@ describe('SampleGasPricesService', () => { * required by the service under test. */ type RootMessenger = Messenger< - // Use `string` rather than 'Root' here to allow registering actions and publishing events from - // any namespace in tests. - string, + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -307,7 +307,7 @@ type RootMessenger = Messenger< * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** diff --git a/packages/sample-controllers/src/sample-petnames-controller.test.ts b/packages/sample-controllers/src/sample-petnames-controller.test.ts index 9cc34615060..0d7d67159f7 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.test.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.test.ts @@ -1,6 +1,8 @@ import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, type MessengerActions, type MessengerEvents, } from '@metamask/messenger'; @@ -259,9 +261,7 @@ describe('SamplePetnamesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - // Use `string` rather than 'Root' here to allow registering actions and publishing events from - // any namespace in tests. - string, + MockAnyNamespace, MessengerActions, MessengerEvents >; @@ -289,7 +289,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger({ namespace: 'Root' }); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** From 692060ddd8828547c0ecb475547f35a9a3681389 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 2 Sep 2025 13:25:15 -0230 Subject: [PATCH 222/247] Delegate actions/events to test gas prices controller messenger --- .../src/sample-gas-prices-controller.test.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts index 29569e825f1..b61edf5c6c4 100644 --- a/packages/sample-controllers/src/sample-gas-prices-controller.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-controller.test.ts @@ -406,10 +406,19 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesControllerMessenger { - return new Messenger({ + const messenger: SampleGasPricesControllerMessenger = new Messenger({ namespace: 'SampleGasPricesController', parent: rootMessenger, }); + rootMessenger.delegate({ + actions: [ + 'NetworkController:getNetworkClientById', + 'SampleGasPricesService:fetchGasPrices', + ], + events: ['NetworkController:stateChange'], + messenger, + }); + return messenger; } /** From 2e2705b87a1e2f28be13b6cf01aae3686343b196 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 3 Oct 2025 16:16:36 -0230 Subject: [PATCH 223/247] Fix lint errors --- packages/sample-controllers/package.json | 2 +- .../sample-gas-prices-service.test.ts | 2 ++ .../sample-gas-prices-service.ts | 2 ++ yarn.lock | 9 +-------- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/sample-controllers/package.json b/packages/sample-controllers/package.json index 2e6cf450715..955ff91a2c3 100644 --- a/packages/sample-controllers/package.json +++ b/packages/sample-controllers/package.json @@ -48,7 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.1", - "@metamask/messenger": "^0.1.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts index 1796ea2bd6e..2c82b73f7c6 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.test.ts @@ -297,6 +297,8 @@ describe('SampleGasPricesService', () => { type RootMessenger = Messenger< MockAnyNamespace, MessengerActions, + // TODO: Disable this lint rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments MessengerEvents >; diff --git a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts index 1639cce3cd3..5eacf5d7a79 100644 --- a/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts +++ b/packages/sample-controllers/src/sample-gas-prices-service/sample-gas-prices-service.ts @@ -52,6 +52,8 @@ type AllowedEvents = never; export type SampleGasPricesServiceMessenger = Messenger< typeof serviceName, SampleGasPricesServiceActions | AllowedActions, + // TODO: Disable this lint rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments SampleGasPricesServiceEvents | AllowedEvents >; diff --git a/yarn.lock b/yarn.lock index 58d7bf44ea5..034d2b30cc1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4015,13 +4015,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" @@ -4617,7 +4610,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" From 80d77308319b6675b3f0e3cb1a814f510e9f3c30 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Mon, 20 Oct 2025 10:48:58 -0230 Subject: [PATCH 224/247] Update controller metadata docs with new properties` --- docs/controller-guidelines.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 25ac0375b7f..7a4c613304f 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -108,22 +108,24 @@ export { FooController, getDefaultFooControllerState } from './FooController'; Each property in state has two pieces of metadata that must be specified. This instructs the client how to treat that property: +- `includeInDebugLogs` - Informs the client whether to include the property in debug state logs attached to Sentry events (`true`) or not (`false`). We must exclude any data that could potentially be personally identifying here, and we often also exclude data that is large and/or unhelpful for debugging. +- `includeInStateLogs` - Informs the client whether to include the property in state logs downloaded by users (`true`) or not (`false`). We must exclude any sensitive data that we don't want our support team to have access to (such as private keys). We include personally-identifiable data related to on-chain state here (we never collect this data, and we have a disclaimer about this in the UI when users download state logs), but other types of personally identifiable information must still be excluded. - `persist` — Informs the client whether the property should be placed in persistent storage (`true`) or not (`false`). Opting out is useful if you want to have a property in state for convenience reasons but you know that property is ephemeral and can be easily reconstructed. -- `anonymous` — Informs the client whether the property is free of personally identifiable information (`true`) or not (`false`) and can therefore safely be included and sent to error reporting services such as Sentry. When in doubt, use `false`. +- `usedInUi` - Informs the client whether the property is used in the UI (`true`) or not (`false`). This is used to filter the state we send to the UI to improve performance. A variable named `${controllerName}Metadata` should be defined (there is no need to export it) and passed as the `metadata` argument in the constructor to `BaseController`. ```typescript const keyringControllerMetadata = { vault: { + // This property can be used to identify a user, so we want to make sure we + // do not include it in Sentry. + includeInDebugLogs: false, // We don't want to include this in state logs because it contains sensitive key material. includeInStateLogs: false, // We want to persist this property so it's restored automatically, as we // cannot reconstruct it otherwise. persist: true, - // This property can be used to identify a user, so we want to make sure we - // do not include it in Sentry. - anonymous: false, // This property is only used in the controller, not in the UI. usedInUi: false, }, From d4bea7d76e9ae1ef608f55510b3d1c0c6838a8eb Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Tue, 21 Oct 2025 18:34:15 -0230 Subject: [PATCH 225/247] Dedupe dependencies --- yarn.lock | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 034d2b30cc1..f7f9842e83c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13947,12 +13947,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": - version: 7.6.3 - resolution: "semver@npm:7.6.3" +"semver@npm:7.x, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" bin: semver: bin/semver.js - checksum: 10/36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 + checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 languageName: node linkType: hard @@ -13965,15 +13965,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.4": - version: 7.7.3 - resolution: "semver@npm:7.7.3" - bin: - semver: bin/semver.js - checksum: 10/8dbc3168e057a38fc322af909c7f5617483c50caddba135439ff09a754b20bdd6482a5123ff543dad4affa488ecf46ec5fb56d61312ad20bb140199b88dfaea9 - languageName: node - linkType: hard - "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0" From c2c78a3ab45da0a8d7a95afa28d3e944f0f5dc3f Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Wed, 22 Oct 2025 17:09:18 +0200 Subject: [PATCH 226/247] rename `anonymous` to `includeInDebutSnapshot` --- README.md | 1 + packages/logging-controller/package.json | 2 +- .../src/LoggingController.test.ts | 20 +++++++++---------- .../src/LoggingController.ts | 5 +++-- .../logging-controller/tsconfig.build.json | 1 + packages/logging-controller/tsconfig.json | 1 + yarn.lock | 9 +-------- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ba3b7ae26f8..085c2aa66ca 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,7 @@ linkStyle default opacity:0.5 keyring_controller --> base_controller; logging_controller --> base_controller; logging_controller --> controller_utils; + logging_controller --> messenger; message_manager --> base_controller; message_manager --> controller_utils; multichain_account_service --> base_controller; diff --git a/packages/logging-controller/package.json b/packages/logging-controller/package.json index 8e7cc140265..8c216f63072 100644 --- a/packages/logging-controller/package.json +++ b/packages/logging-controller/package.json @@ -49,7 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.1", "@metamask/controller-utils": "^11.14.1", - "@metamask/messenger": "^0.1.0", + "@metamask/messenger": "^0.3.0", "uuid": "^8.3.2" }, "devDependencies": { diff --git a/packages/logging-controller/src/LoggingController.test.ts b/packages/logging-controller/src/LoggingController.test.ts index 38aa455fc42..7fbd5421fd9 100644 --- a/packages/logging-controller/src/LoggingController.test.ts +++ b/packages/logging-controller/src/LoggingController.test.ts @@ -1,4 +1,4 @@ -import { deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { Messenger, MOCK_ANY_NAMESPACE, @@ -197,8 +197,8 @@ describe('LoggingController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); @@ -207,14 +207,14 @@ describe('LoggingController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); it('includes expected state in state logs', () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); @@ -233,8 +233,8 @@ describe('LoggingController', () => { }); it('persists expected state', () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); @@ -253,8 +253,8 @@ describe('LoggingController', () => { }); it('exposes expected state to UI', () => { - const unrestricted = getUnrestrictedMessenger(); - const messenger = getRestrictedMessenger(unrestricted); + const rootMessenger = getRootMessenger(); + const messenger = getLoggingControllerMessenger(rootMessenger); const controller = new LoggingController({ messenger, }); diff --git a/packages/logging-controller/src/LoggingController.ts b/packages/logging-controller/src/LoggingController.ts index 6d1f74ac77a..61e3fc6478a 100644 --- a/packages/logging-controller/src/LoggingController.ts +++ b/packages/logging-controller/src/LoggingController.ts @@ -1,6 +1,7 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, + StateMetadata, } from '@metamask/base-controller/next'; import { BaseController } from '@metamask/base-controller/next'; import type { Messenger } from '@metamask/messenger'; @@ -60,11 +61,11 @@ export type LoggingControllerMessenger = Messenger< LoggingControllerEvents >; -const metadata = { +const metadata: StateMetadata = { logs: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; diff --git a/packages/logging-controller/tsconfig.build.json b/packages/logging-controller/tsconfig.build.json index bbfe057a207..9df8150d8d0 100644 --- a/packages/logging-controller/tsconfig.build.json +++ b/packages/logging-controller/tsconfig.build.json @@ -7,6 +7,7 @@ }, "references": [ { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/logging-controller/tsconfig.json b/packages/logging-controller/tsconfig.json index 7ee9852347a..8e913ea23d1 100644 --- a/packages/logging-controller/tsconfig.json +++ b/packages/logging-controller/tsconfig.json @@ -5,6 +5,7 @@ }, "references": [ { "path": "../base-controller" }, + { "path": "../messenger" }, { "path": "../controller-utils" } ], "include": ["../../types", "./src"] diff --git a/yarn.lock b/yarn.lock index 0831623c9a5..8a61da7c8d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3982,7 +3982,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" - "@metamask/messenger": "npm:^0.1.0" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" @@ -4016,13 +4016,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^0.1.0": - version: 0.1.0 - resolution: "@metamask/messenger@npm:0.1.0" - checksum: 10/5d6105865255e72571df143c648ebfb42b04ead24cd6bade758f0340eafba3790d9ff0818bd06fbda17799e3253ac05df51d9e13ebfd0c710e68fd1c0d1007a9 - languageName: node - linkType: hard - "@metamask/messenger@npm:^0.3.0, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" From b7ebc186d3c2e867874c24423b26592fb5376c8c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 23 Oct 2025 16:47:08 +0200 Subject: [PATCH 227/247] fix unit tests --- packages/core-backend/src/AccountActivityService.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-backend/src/AccountActivityService.test.ts b/packages/core-backend/src/AccountActivityService.test.ts index e0696b551c1..00e83d7a9d1 100644 --- a/packages/core-backend/src/AccountActivityService.test.ts +++ b/packages/core-backend/src/AccountActivityService.test.ts @@ -83,11 +83,11 @@ const getMessenger = (): { mocks: { getSelectedAccount: jest.Mock; connect: jest.Mock; - disconnect: jest.Mock; subscribe: jest.Mock; channelHasSubscription: jest.Mock; getSubscriptionsByChannel: jest.Mock; findSubscriptionsByChannelPrefix: jest.Mock; + forceReconnection: jest.Mock; addChannelCallback: jest.Mock; removeChannelCallback: jest.Mock; }; @@ -109,7 +109,7 @@ const getMessenger = (): { actions: [ 'AccountsController:getSelectedAccount', 'BackendWebSocketService:connect', - 'BackendWebSocketService:disconnect', + 'BackendWebSocketService:forceReconnection', 'BackendWebSocketService:subscribe', 'BackendWebSocketService:getConnectionInfo', 'BackendWebSocketService:channelHasSubscription', From b9b5db6c1fc3c11bb3d1ced1aef3cd363a46fb31 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 23 Oct 2025 17:01:16 +0200 Subject: [PATCH 228/247] fix lint --- eslint-warning-thresholds.json | 2 +- packages/ens-controller/src/EnsController.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index a16cd0d14f3..ee30b9a332f 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -137,7 +137,7 @@ "@typescript-eslint/no-misused-promises": 1 }, "packages/ens-controller/src/EnsController.test.ts": { - "import-x/order": 2 + "import-x/order": 1 }, "packages/ens-controller/src/EnsController.ts": { "jsdoc/check-tag-names": 6 diff --git a/packages/ens-controller/src/EnsController.ts b/packages/ens-controller/src/EnsController.ts index 0f8f6aa79b5..f7659ba3343 100644 --- a/packages/ens-controller/src/EnsController.ts +++ b/packages/ens-controller/src/EnsController.ts @@ -1,7 +1,7 @@ import { Web3Provider } from '@ethersproject/providers'; import { BaseController, - StateMetadata, + type StateMetadata, type ControllerGetStateAction, type ControllerStateChangeEvent, } from '@metamask/base-controller/next'; From 03d8a95ea0f0da26d2e8856d0663a527924d1118 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Thu, 23 Oct 2025 17:09:42 +0200 Subject: [PATCH 229/247] run prettier --- packages/ens-controller/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ens-controller/tsconfig.json b/packages/ens-controller/tsconfig.json index f62c7c2e078..c6a3a4c830a 100644 --- a/packages/ens-controller/tsconfig.json +++ b/packages/ens-controller/tsconfig.json @@ -7,7 +7,7 @@ { "path": "../base-controller" }, { "path": "../controller-utils" }, { "path": "../network-controller" }, - { "path": "../messenger" }, + { "path": "../messenger" } ], "include": ["../../types", "./src"] } From 4881477947910b49a37ce721c9eb1736a77091c6 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 17:07:44 +0200 Subject: [PATCH 230/247] rename `messagingSystem` to `messenger` --- packages/transaction-controller/src/TransactionController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index 9ad06d12646..13bdd89b9e6 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -4467,12 +4467,12 @@ export class TransactionController extends BaseController< this.updateTransaction.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:emulateNewTransaction`, this.emulateNewTransaction.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:emulateTransactionUpdate`, this.emulateTransactionUpdate.bind(this), ); From e0cbe0ecfa261d52e714da5c824834886b840cc6 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 19:08:35 +0200 Subject: [PATCH 231/247] update readme content --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0b377cff988..443b91c2b94 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ linkStyle default opacity:0.5 transaction_controller(["@metamask/transaction-controller"]); user_operation_controller(["@metamask/user-operation-controller"]); account_tree_controller --> base_controller; + account_tree_controller --> messenger; account_tree_controller --> accounts_controller; account_tree_controller --> keyring_controller; account_tree_controller --> multichain_account_service; @@ -216,6 +217,7 @@ linkStyle default opacity:0.5 earn_controller --> transaction_controller; eip_5792_middleware --> transaction_controller; eip_5792_middleware --> keyring_controller; + eip_7702_internal_rpc_middleware --> controller_utils; eip1193_permission_middleware --> chain_agnostic_permission; eip1193_permission_middleware --> controller_utils; eip1193_permission_middleware --> json_rpc_engine; From 102de9dbb7efb69515fb79a35a90f960a8c0948c Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 19:09:26 +0200 Subject: [PATCH 232/247] update changelog --- packages/account-tree-controller/CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index 3eccf5ebf90..57454190ab2 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) + - Previously, `AccountTreeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [1.6.0] ### Changed @@ -19,8 +24,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6380](https://github.com/MetaMask/core/pull/6380)) - - Previously, `AccountTreeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Use non-EVM account names for group names ([#6831](https://github.com/MetaMask/core/pull/6831)) - EVM accounts still take precedence over non-EVM accounts. - Before accounts get re-aligned, it is possible that a group contains only non-EVM accounts, in which case, the first non-EVM account name will be used for that account group. From c1fce8210adde8a8caf37649978945d9eac323a1 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 19:11:48 +0200 Subject: [PATCH 233/247] update changelog and readme --- README.md | 1 + packages/address-book-controller/CHANGELOG.md | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7245d5dba6f..3d8dbd26ff5 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ linkStyle default opacity:0.5 earn_controller --> transaction_controller; eip_5792_middleware --> transaction_controller; eip_5792_middleware --> keyring_controller; + eip_7702_internal_rpc_middleware --> controller_utils; eip1193_permission_middleware --> chain_agnostic_permission; eip1193_permission_middleware --> controller_utils; eip1193_permission_middleware --> json_rpc_engine; diff --git a/packages/address-book-controller/CHANGELOG.md b/packages/address-book-controller/CHANGELOG.md index 7fa4ef6fe21..3570d2aa57f 100644 --- a/packages/address-book-controller/CHANGELOG.md +++ b/packages/address-book-controller/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6383](https://github.com/MetaMask/core/pull/6383)) + - Previously, `AddressBookController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6383](https://github.com/MetaMask/core/pull/6383)) + ## [6.2.1] ### Changed @@ -21,8 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6383](https://github.com/MetaMask/core/pull/6383)) - - Previously, `AddressBookController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.1` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632), [#6807](https://github.com/MetaMask/core/pull/6807)) - Bump `@metamask/controller-utils` from `^11.11.0` to `^11.14.1` ([#6303](https://github.com/MetaMask/core/pull/6303), [#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629), [#6807](https://github.com/MetaMask/core/pull/6807)) - Bump `@metamask/utils` from `^11.4.2` to `^11.8.1` ([#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) From 63e4718f5cb17c964d7a0195607f525447cfd4c4 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 19:19:37 +0200 Subject: [PATCH 234/247] update changelog and readme --- README.md | 1 + packages/composable-controller/CHANGELOG.md | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c8ef118646..618cbee74b0 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,7 @@ linkStyle default opacity:0.5 earn_controller --> transaction_controller; eip_5792_middleware --> transaction_controller; eip_5792_middleware --> keyring_controller; + eip_7702_internal_rpc_middleware --> controller_utils; eip1193_permission_middleware --> chain_agnostic_permission; eip1193_permission_middleware --> controller_utils; eip1193_permission_middleware --> json_rpc_engine; diff --git a/packages/composable-controller/CHANGELOG.md b/packages/composable-controller/CHANGELOG.md index 0ed2942db74..85c3df4a74e 100644 --- a/packages/composable-controller/CHANGELOG.md +++ b/packages/composable-controller/CHANGELOG.md @@ -7,12 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- **BREAKING:** Migrate `ComposableController` to new `Messenger` from `@metamask/messenger` ([#6710](https://github.com/MetaMask/core/pull/6710)) + - Previously, the controller accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6710](https://github.com/MetaMask/core/pull/6710)) + ## [11.1.1] ### Changed -- **BREAKING:** Migrate `ComposableController` to new `Messenger` from `@metamask/messenger` ([#6710](https://github.com/MetaMask/core/pull/6710)) - - Previously, the controller accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ### Fixed From 7ad33d9a1485c0526221922647af2bd6446cff46 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 19:28:17 +0200 Subject: [PATCH 235/247] update changelog and readme --- README.md | 1 + packages/keyring-controller/CHANGELOG.md | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b1f5926c9a6..588d3d70535 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,7 @@ linkStyle default opacity:0.5 earn_controller --> transaction_controller; eip_5792_middleware --> transaction_controller; eip_5792_middleware --> keyring_controller; + eip_7702_internal_rpc_middleware --> controller_utils; eip1193_permission_middleware --> chain_agnostic_permission; eip1193_permission_middleware --> controller_utils; eip1193_permission_middleware --> json_rpc_engine; diff --git a/packages/keyring-controller/CHANGELOG.md b/packages/keyring-controller/CHANGELOG.md index 6363520aa6d..3aa0213aa12 100644 --- a/packages/keyring-controller/CHANGELOG.md +++ b/packages/keyring-controller/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6370](https://github.com/MetaMask/core/pull/6370)) + - Previously, `KeyringController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6370](https://github.com/MetaMask/core/pull/6370)) + ## [23.2.0] ### Added @@ -16,8 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6370](https://github.com/MetaMask/core/pull/6370)) - - Previously, `KeyringController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [23.1.1] From 83f388baa2ac1cb811baf9171e501e1d513e0980 Mon Sep 17 00:00:00 2001 From: Michele Esposito Date: Fri, 24 Oct 2025 19:32:11 +0200 Subject: [PATCH 236/247] update readme and changelog --- README.md | 1 + packages/multichain-transactions-controller/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index ba3b7ae26f8..9f86c278243 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,7 @@ linkStyle default opacity:0.5 multichain_network_controller --> keyring_controller; multichain_network_controller --> network_controller; multichain_transactions_controller --> base_controller; + multichain_transactions_controller --> messenger; multichain_transactions_controller --> polling_controller; multichain_transactions_controller --> accounts_controller; multichain_transactions_controller --> keyring_controller; diff --git a/packages/multichain-transactions-controller/CHANGELOG.md b/packages/multichain-transactions-controller/CHANGELOG.md index 0bcf93dc0c0..186af6f545d 100644 --- a/packages/multichain-transactions-controller/CHANGELOG.md +++ b/packages/multichain-transactions-controller/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6542](https://github.com/MetaMask/core/pull/6542)) - Previously, `MultichainTransactionsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. +- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6542](https://github.com/MetaMask/core/pull/6542)) ## [5.1.0] From cf3638ea23dce0a2260d17e640e4b00b146585b6 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:01:45 -0230 Subject: [PATCH 237/247] Update messagingSystem references to messenger --- .../src/TransactionController.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index 13bdd89b9e6..f30de01f283 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -2816,11 +2816,11 @@ export class TransactionController extends BaseController< } if (transactionMeta.type === TransactionType.swap) { - this.messagingSystem.publish('TransactionController:transactionNewSwap', { + this.messenger.publish('TransactionController:transactionNewSwap', { transactionMeta, }); } else if (transactionMeta.type === TransactionType.swapApproval) { - this.messagingSystem.publish( + this.messenger.publish( 'TransactionController:transactionNewSwapApproval', { transactionMeta }, ); @@ -2837,7 +2837,7 @@ export class TransactionController extends BaseController< ...transactionMeta, txParams: { ...transactionMeta.txParams, - from: this.messagingSystem.call('AccountsController:getSelectedAccount') + from: this.messenger.call('AccountsController:getSelectedAccount') .address, }, }; @@ -2857,10 +2857,9 @@ export class TransactionController extends BaseController< 'Generated from user operation', ); - this.messagingSystem.publish( - 'TransactionController:transactionStatusUpdated', - { transactionMeta: updatedTransactionMeta }, - ); + this.messenger.publish('TransactionController:transactionStatusUpdated', { + transactionMeta: updatedTransactionMeta, + }); } #addMetadata(transactionMeta: TransactionMeta) { From 998ffb96d2bfe6b386ed983ac05eb0d0a6d35023 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:07:31 -0230 Subject: [PATCH 238/247] Update changelogs following branch update --- packages/approval-controller/CHANGELOG.md | 7 +++++-- packages/assets-controllers/CHANGELOG.md | 4 ++-- packages/gas-fee-controller/CHANGELOG.md | 12 +++++++++--- packages/network-controller/CHANGELOG.md | 7 +++++-- packages/transaction-controller/CHANGELOG.md | 10 +++++----- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/approval-controller/CHANGELOG.md b/packages/approval-controller/CHANGELOG.md index a911aec0ec1..4aa56e42613 100644 --- a/packages/approval-controller/CHANGELOG.md +++ b/packages/approval-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `ApprovalController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [7.2.1] ### Changed @@ -21,8 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - - Previously, `ApprovalController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/utils` from `^11.2.0` to `^11.8.1` ([#6054](https://github.com/MetaMask/core/pull/6054), [#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) - Bump `@metamask/base-controller` from `^8.0.0` to `^8.4.1` ([#5722](https://github.com/MetaMask/core/pull/5722), [#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632), [#6807](https://github.com/MetaMask/core/pull/6807)) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 4cbe20d211b..239e437d631 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Migrate `AssetsContractController`, `NftController`, and `TokensController` to new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, the controllers accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) ## [83.1.0] @@ -44,8 +46,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Migrate `AssetsContractController`, `NftController`, and `TokensController` to new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - - Previously, the controllers accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Batch `OnAssetConversion` and `OnAssetsMarketData` requests to non-EVM account Snaps ([#6886](https://github.com/MetaMask/core/pull/6886)) ## [81.0.1] diff --git a/packages/gas-fee-controller/CHANGELOG.md b/packages/gas-fee-controller/CHANGELOG.md index 0b5b085c2cb..e464e82052c 100644 --- a/packages/gas-fee-controller/CHANGELOG.md +++ b/packages/gas-fee-controller/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Export `GasFeeMessenger` type ([#6386](https://github.com/MetaMask/core/pull/6386)) + +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `GasFeeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [24.1.1] ### Changed @@ -20,12 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add two new controller state metadata properties: `includeInStateLogs` and `usedInUi` ([#6473](https://github.com/MetaMask/core/pull/6473)) -- Export `GasFeeMessenger` type ([#6386](https://github.com/MetaMask/core/pull/6386)) ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - - Previously, `GasFeeController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/base-controller` from `^8.0.1` to `^8.4.1` ([#6284](https://github.com/MetaMask/core/pull/6284), [#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465), [#6632](https://github.com/MetaMask/core/pull/6632), [#6807](https://github.com/MetaMask/core/pull/6807)) - Bump `@metamask/controller-utils` from `^11.10.0` to `^11.14.1` ([#6069](https://github.com/MetaMask/core/pull/6069), [#6303](https://github.com/MetaMask/core/pull/6303), [#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629), [#6807](https://github.com/MetaMask/core/pull/6807)) - Bump `@metamask/utils` from `^11.2.0` to `^11.8.1` ([#6054](https://github.com/MetaMask/core/pull/6054), [#6588](https://github.com/MetaMask/core/pull/6588), [#6708](https://github.com/MetaMask/core/pull/6708)) diff --git a/packages/network-controller/CHANGELOG.md b/packages/network-controller/CHANGELOG.md index 1106f819698..b3dcd31dbfb 100644 --- a/packages/network-controller/CHANGELOG.md +++ b/packages/network-controller/CHANGELOG.md @@ -7,12 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [24.3.1] - ### Changed - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - Previously, `NetworkController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + +## [24.3.1] + +### Changed + - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [24.3.0] diff --git a/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index 2c13c272f42..a16a9bf7218 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) + - Previously, `TransactionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. + ## [60.10.0] ### Added @@ -48,11 +53,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `txMeta` property to `GetSimulationConfig` callback ([#6833](https://github.com/MetaMask/core/pull/6833)) -### Changed - -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386)) - - Previously, `TransactionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - ## [60.6.1] ### Changed From ba9bcecc70f4a5019ad803a60b08bd5bc96f5384 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:10:01 -0230 Subject: [PATCH 239/247] Update changelog --- packages/earn-controller/CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/earn-controller/CHANGELOG.md b/packages/earn-controller/CHANGELOG.md index 1a8f2686b45..58b713bcb5d 100644 --- a/packages/earn-controller/CHANGELOG.md +++ b/packages/earn-controller/CHANGELOG.md @@ -7,13 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [8.0.2] - ### Changed - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6445](https://github.com/MetaMask/core/pull/6445)) - Previously, `EarnController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6445](https://github.com/MetaMask/core/pull/6445)) + +## [8.0.2] + +### Changed + - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) - Bump `@metamask/network-controller` from `^24.2.2` to `^24.3.0` ([#6883](https://github.com/MetaMask/core/pull/6883)) - Bump `@metamask/transaction-controller` from `^60.7.0` to `^60.8.0` ([#6883](https://github.com/MetaMask/core/pull/6883)) From b46b67c90a09ef433599cdb1b9d8482f390d12a0 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:12:16 -0230 Subject: [PATCH 240/247] Fix changelog typo --- packages/ens-controller/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ens-controller/CHANGELOG.md b/packages/ens-controller/CHANGELOG.md index 1ddb8e0630c..8a8be3f6fc3 100644 --- a/packages/ens-controller/CHANGELOG.md +++ b/packages/ens-controller/CHANGELOG.md @@ -9,9 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6459](https://github.com/MetaMask/core/pull/6460)) +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6460](https://github.com/MetaMask/core/pull/6460)) - Previously, `EnsController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. -- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6459](https://github.com/MetaMask/core/pull/6460)) +- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6460](https://github.com/MetaMask/core/pull/6460)) ## [17.1.1] From 06c5ef3e7d2b35357d33cf71f24cf8e8eb4b4705 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:17:23 -0230 Subject: [PATCH 241/247] Simplify example code in comment and use more consistent naming --- .../src/error-reporting-service.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/error-reporting-service/src/error-reporting-service.ts b/packages/error-reporting-service/src/error-reporting-service.ts index a1cbf903f91..675583f3719 100644 --- a/packages/error-reporting-service/src/error-reporting-service.ts +++ b/packages/error-reporting-service/src/error-reporting-service.ts @@ -95,17 +95,17 @@ type ErrorReportingServiceOptions = { * import { ErrorReportingService } from '@metamask/error-reporting-service'; * import { ExampleController } from './example-controller'; * - * type AllActions = MessengerActions; - * - * type AllEvents = MessengerEvents; - * - * type RootMessenger = Messenger<'Root', AllActions, AllEvents>; + * type RootMessenger = Messenger< + * 'Root', + * MessengerActions, + * MessengerEvents + * >; * - * // Create a global messenger. - * const globalMessenger = new Messenger(); + * // Create a root messenger. + * const rootMessenger = new Messenger(); * * // Register handler for the `ErrorReportingService:captureException` - * // action in the global messenger. + * // action in the root messenger. * const errorReportingServiceMessenger = new Messenger< * 'ErrorReportingService', * MessengerActions, @@ -113,7 +113,7 @@ type ErrorReportingServiceOptions = { * RootMessenger * >({ * namespace: 'ErrorReportingService', - * parent: globalMessenger, + * parent: rootMessenger, * }); * const errorReportingService = new ErrorReportingService({ * messenger: errorReportingServiceMessenger, @@ -127,9 +127,9 @@ type ErrorReportingServiceOptions = { * RootMessenger * >({ * namespace: 'ExampleController', - * parent: globalMessenger, + * parent: rootMessenger, * }); - * globalMessenger.delegate({ + * rootMessenger.delegate({ * messenger: exampleControllerMessenger, * actions: ['ErrorReportingService:captureException'], * }); From ac996395bfd3997d37c6ea5ed19f1ce628770494 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:26:11 -0230 Subject: [PATCH 242/247] Fix changelog mistakes from recent merges --- packages/assets-controllers/CHANGELOG.md | 5 ++++- packages/bridge-status-controller/CHANGELOG.md | 4 ++-- packages/subscription-controller/CHANGELOG.md | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 089ee38d9be..532159b939e 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add export for `CurrencyRateMessenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + ### Changed - **BREAKING:** Migrate the following controllers to the new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) @@ -49,7 +53,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - **BREAKING:** Add new event listeners to refresh balances on `TransactionControllerUnapprovedTransactionAddedEvent` and `TransactionControllerTransactionConfirmedEvent` ([#6903](https://github.com/MetaMask/core/pull/6903)) -- Add export for `CurrencyRateMessenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) - Add multicall addresses in `MULTICALL_CONTRACT_BY_CHAINID` ([#6896](https://github.com/MetaMask/core/pull/6896)) - Add multicall address for Chains: `Injective`, `Hemi`, `Plasma`, `Nonmia`, `XRPL`, `Soneium`, `Genesys`, `EDU`, `Abstract`, `Berachain`, `MegaETH Testnet`, `Apechain`, `Matchain`, `Monad Testnet`, `Monad`, `Katana`, `Lens`, `Plume`, `XDC` diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index b1f3875defa..8f3d28b229b 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `BridgeStatusController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) ## [55.0.0] @@ -34,8 +36,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) - - Previously, `BridgeStatusController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Publish `destinationTransactionCompleted` event when a bridge tx completes on the destination chain ([#6900](https://github.com/MetaMask/core/pull/6900)) ## [52.0.0] diff --git a/packages/subscription-controller/CHANGELOG.md b/packages/subscription-controller/CHANGELOG.md index 528f8236b9c..30d5b1f3886 100644 --- a/packages/subscription-controller/CHANGELOG.md +++ b/packages/subscription-controller/CHANGELOG.md @@ -9,14 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) + - Previously, `SubscriptionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) ## [2.1.0] ### Changed -- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444)) - - Previously, `SubscriptionController` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - Make `getCryptoApproveTransactionParams` synchronous ([#6930](https://github.com/MetaMask/core/pull/6930)) - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) From e37bf9a930b837b3ca850e3d5499418d1c3760e4 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:28:34 -0230 Subject: [PATCH 243/247] Add new exports to changelog --- packages/ens-controller/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/ens-controller/CHANGELOG.md b/packages/ens-controller/CHANGELOG.md index 8a8be3f6fc3..e36011718c1 100644 --- a/packages/ens-controller/CHANGELOG.md +++ b/packages/ens-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Export types `EnsControllerActions` and `EnsControllerEvents` ([#6460](https://github.com/MetaMask/core/pull/6460)) + ### Changed - **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6460](https://github.com/MetaMask/core/pull/6460)) From 9660de8e9f0606d9f8d5b1655e73f71c30953159 Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:32:30 -0230 Subject: [PATCH 244/247] Update ESLint warning threshold --- eslint-warning-thresholds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index 85c35fde51b..054fae43f2f 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -56,7 +56,7 @@ }, "packages/assets-controllers/src/TokenListController.test.ts": { "import-x/namespace": 7, - "import-x/order": 3, + "import-x/order": 2, "jest/no-conditional-in-test": 2 }, "packages/assets-controllers/src/TokenRatesController.test.ts": { From 94d3033f3c9fd0a38cf4b6db71ce2ce8f7ba4c2d Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 16:47:51 -0230 Subject: [PATCH 245/247] Fix changelog --- packages/composable-controller/CHANGELOG.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/composable-controller/CHANGELOG.md b/packages/composable-controller/CHANGELOG.md index 85c3df4a74e..0d445b11e8f 100644 --- a/packages/composable-controller/CHANGELOG.md +++ b/packages/composable-controller/CHANGELOG.md @@ -7,21 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + - **BREAKING:** Migrate `ComposableController` to new `Messenger` from `@metamask/messenger` ([#6710](https://github.com/MetaMask/core/pull/6710)) - Previously, the controller accepted a `RestrictedMessenger` instance from `@metamask/base-controller`. - **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6710](https://github.com/MetaMask/core/pull/6710)) +### Fixed + +- Resolve incompatibility of `ChildControllerStateChangeEvents` type with `BaseController` (when used in the `Events` type argument of `ComposableControllerMessenger`) by removing unnecessary nested logic from definition ([#6904](https://github.com/MetaMask/core/pull/6904)) + - Also update generic parameter names `ControllerName` and `ControllerState` to `ChildControllerName`, `ChildControllerState` for reduced ambiguity. + ## [11.1.1] ### Changed - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) -### Fixed - -- Resolve incompatibility of `ChildControllerStateChangeEvents` type with `BaseController` (when used in the `Events` type argument of `ComposableControllerMessenger`) by removing unnecessary nested logic from definition ([#6904](https://github.com/MetaMask/core/pull/6904)) - - Also update generic parameter names `ControllerName` and `ControllerState` to `ChildControllerName`, `ChildControllerState` for reduced ambiguity. - ## [11.1.0] ### Added From 17e90412cd24565bc99091062b5a21589935fe1b Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 17:26:06 -0230 Subject: [PATCH 246/247] Fix broken delegation in unit test The test didn't actually rely upon subscriptions being setup correctly, so this mistake didn't impact the test result. It was only testing the constructor. The mistake has still been updated to avoid confusion. --- .../composable-controller/src/ComposableController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/composable-controller/src/ComposableController.test.ts b/packages/composable-controller/src/ComposableController.test.ts index d9e81c01e2a..188742349f2 100644 --- a/packages/composable-controller/src/ComposableController.test.ts +++ b/packages/composable-controller/src/ComposableController.test.ts @@ -183,8 +183,8 @@ describe('ComposableController', () => { namespace: 'ComposableController', parent: messenger, }); - composableControllerMessenger.delegate({ - messenger: fooMessenger, + messenger.delegate({ + messenger: composableControllerMessenger, events: ['FooController:stateChange', 'QuzController:stateChange'], }); const composableController = new ComposableController< From c1ad7a45e9261bc046a24303711f52c00b3a231a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Fri, 24 Oct 2025 17:32:14 -0230 Subject: [PATCH 247/247] Fix mistake in docs --- docs/controller-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index 7a4c613304f..f4db78d4580 100644 --- a/docs/controller-guidelines.md +++ b/docs/controller-guidelines.md @@ -108,7 +108,7 @@ export { FooController, getDefaultFooControllerState } from './FooController'; Each property in state has two pieces of metadata that must be specified. This instructs the client how to treat that property: -- `includeInDebugLogs` - Informs the client whether to include the property in debug state logs attached to Sentry events (`true`) or not (`false`). We must exclude any data that could potentially be personally identifying here, and we often also exclude data that is large and/or unhelpful for debugging. +- `includeInDebugSnapshot` - Informs the client whether to include the property in debug state logs attached to Sentry events (`true`) or not (`false`). We must exclude any data that could potentially be personally identifying here, and we often also exclude data that is large and/or unhelpful for debugging. - `includeInStateLogs` - Informs the client whether to include the property in state logs downloaded by users (`true`) or not (`false`). We must exclude any sensitive data that we don't want our support team to have access to (such as private keys). We include personally-identifiable data related to on-chain state here (we never collect this data, and we have a disclaimer about this in the UI when users download state logs), but other types of personally identifiable information must still be excluded. - `persist` — Informs the client whether the property should be placed in persistent storage (`true`) or not (`false`). Opting out is useful if you want to have a property in state for convenience reasons but you know that property is ephemeral and can be easily reconstructed. - `usedInUi` - Informs the client whether the property is used in the UI (`true`) or not (`false`). This is used to filter the state we send to the UI to improve performance. @@ -120,7 +120,7 @@ const keyringControllerMetadata = { vault: { // This property can be used to identify a user, so we want to make sure we // do not include it in Sentry. - includeInDebugLogs: false, + includeInDebugSnapshot: false, // We don't want to include this in state logs because it contains sensitive key material. includeInStateLogs: false, // We want to persist this property so it's restored automatically, as we