diff --git a/README.md b/README.md index 0b377cff988..f8738d22c9b 100644 --- a/README.md +++ b/README.md @@ -153,16 +153,22 @@ 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; address_book_controller --> base_controller; address_book_controller --> controller_utils; + address_book_controller --> messenger; announcement_controller --> base_controller; + announcement_controller --> messenger; app_metadata_controller --> base_controller; + app_metadata_controller --> messenger; 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; @@ -180,6 +186,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; @@ -200,6 +207,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; @@ -207,10 +215,12 @@ linkStyle default opacity:0.5 core_backend --> accounts_controller; core_backend --> keyring_controller; delegation_controller --> base_controller; + delegation_controller --> messenger; delegation_controller --> accounts_controller; 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; @@ -222,6 +232,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_block_tracker --> eth_json_rpc_provider; @@ -237,10 +248,13 @@ 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; + keyring_controller --> messenger; logging_controller --> base_controller; logging_controller --> controller_utils; + logging_controller --> messenger; message_manager --> base_controller; message_manager --> controller_utils; multichain_account_service --> base_controller; @@ -269,6 +283,7 @@ linkStyle default opacity:0.5 network_controller --> eth_json_rpc_middleware; 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; @@ -322,11 +337,13 @@ 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; transaction_controller --> base_controller; transaction_controller --> controller_utils; + transaction_controller --> messenger; transaction_controller --> accounts_controller; transaction_controller --> approval_controller; transaction_controller --> eth_block_tracker; diff --git a/docs/controller-guidelines.md b/docs/controller-guidelines.md index fdf1a32cac6..7a4c613304f 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 @@ -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, }, @@ -193,7 +195,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 +224,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** @@ -231,12 +233,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,22 +255,40 @@ 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'], + messenger: fooControllerMessenger, }); const fooController = new FooController({ messenger: fooControllerMessenger, }); ``` -## 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: @@ -312,12 +330,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< @@ -330,15 +346,23 @@ class FooController extends BaseController< } doSomething() { - this.messagingSystem.publish('FooController:someEvent'); + this.messenger.publish('FooController:someEvent'); } } // === 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 +531,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 +553,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 3rd 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 included in the actions type union and _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 actions type union 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 included in the actions type union and _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 event 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 events type union 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 +787,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 +1162,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< @@ -1216,7 +1203,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. @@ -1256,7 +1243,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 === */ @@ -1271,16 +1258,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 +1290,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 { @@ -1327,7 +1311,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 ... } @@ -1385,12 +1369,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 +1384,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 { @@ -1423,7 +1403,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( diff --git a/docs/data-services.md b/docs/data-services.md index 57d79671c0b..cfd3967d3dc 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 @@ -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/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 `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: @@ -436,15 +447,22 @@ type SendControllerEvents = ...; type AllowedEvents = ...; -type SendControllerMessenger = RestrictedMessenger< +type SendControllerMessenger = Messenger< 'SendController', SendControllerActions | AllowedActions, SendControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] >; ``` +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 @@ -452,7 +470,7 @@ class SendController extends BaseController { // ... await someMethodThatUsesGasPrices() { - const gasPrices = await this.#messagingSystem.call( + const gasPrices = await this.messenger.call( 'GasPricesService:fetchGasPrices', ); // ... use gasPrices somehow ... diff --git a/eslint-warning-thresholds.json b/eslint-warning-thresholds.json index f5a52c63e63..a2f2031da37 100644 --- a/eslint-warning-thresholds.json +++ b/eslint-warning-thresholds.json @@ -34,8 +34,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, @@ -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": { @@ -68,7 +68,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": { @@ -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, @@ -137,7 +134,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 @@ -378,8 +375,14 @@ "@typescript-eslint/no-unused-vars": 1, "@typescript-eslint/prefer-promise-reject-errors": 1 }, - "packages/permission-controller/src/Permission.ts": { - "prettier/prettier": 11 + "packages/network-controller/tests/create-network-client.test.ts": { + "import-x/order": 1 + }, + "packages/network-controller/tests/provider-api-tests/helpers.ts": { + "@typescript-eslint/prefer-promise-reject-errors": 1, + "import-x/namespace": 1, + "import-x/no-named-as-default-member": 1, + "promise/catch-or-return": 1 }, "packages/permission-controller/src/PermissionController.test.ts": { "jest/no-conditional-in-test": 4 @@ -408,9 +411,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/account-tree-controller/CHANGELOG.md b/packages/account-tree-controller/CHANGELOG.md index 9fd6c8488b1..3eccf5ebf90 100644 --- a/packages/account-tree-controller/CHANGELOG.md +++ b/packages/account-tree-controller/CHANGELOG.md @@ -19,6 +19,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`. - 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. diff --git a/packages/account-tree-controller/package.json b/packages/account-tree-controller/package.json index 4165b0e69fe..efe958fcd66 100644 --- a/packages/account-tree-controller/package.json +++ b/packages/account-tree-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.2", + "@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/account-tree-controller/src/AccountTreeController.test.ts b/packages/account-tree-controller/src/AccountTreeController.test.ts index 08733672915..10ad0ede285 100644 --- a/packages/account-tree-controller/src/AccountTreeController.test.ts +++ b/packages/account-tree-controller/src/AccountTreeController.test.ts @@ -9,7 +9,7 @@ import { type AccountGroupId, } from '@metamask/account-api'; import type { AccountId } from '@metamask/accounts-controller'; -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { BtcAccountType, EthAccountType, @@ -36,14 +36,11 @@ import type { BackupAndSyncAnalyticsEventPayload } from './backup-and-sync/analy import { BackupAndSyncService } from './backup-and-sync/service'; import { isAccountGroupNameUnique } from './group'; 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'; + getAccountTreeControllerMessenger, + getRootMessenger, +} from '../tests/mockMessenger'; // Local mock of EMPTY_ACCOUNT to avoid circular dependency const EMPTY_ACCOUNT_MOCK: InternalAccount = { @@ -233,53 +230,7 @@ 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:accountRemoved', - 'AccountsController:selectedAccountChange', - 'UserStorageController:stateChange', - 'MultichainAccountService:walletStatusChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedMultichainAccount', - 'AccountsController:setSelectedAccount', - 'UserStorageController:getState', - 'UserStorageController:performGetStorage', - 'UserStorageController:performGetStorageAllFeatureEntries', - 'UserStorageController:performSetStorage', - 'UserStorageController:performBatchSetStorage', - 'AuthenticationController:getSessionProfile', - 'MultichainAccountService:createMultichainAccountGroup', - 'KeyringController:getState', - 'SnapController:get', - ], - }); -} +const mockGetSelectedMultichainAccountActionHandler = jest.fn(); /** * Sets up the AccountTreeController for testing. @@ -317,10 +268,7 @@ function setup({ }, }: { state?: Partial; - messenger?: Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents - >; + messenger?: ReturnType; accounts?: InternalAccount[]; keyrings?: KeyringObject[]; config?: { @@ -338,9 +286,9 @@ function setup({ }; } = {}): { controller: AccountTreeController; - messenger: Messenger< - AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents + messenger: ReturnType; + accountTreeControllerMessenger: ReturnType< + typeof getAccountTreeControllerMessenger >; spies: { consoleWarn: jest.SpyInstance; @@ -401,6 +349,7 @@ function setup({ mocks.AccountsController.listMultichainAccounts.mockImplementation( () => mocks.AccountsController.accounts, ); + messenger.registerActionHandler( 'AccountsController:listMultichainAccounts', mocks.AccountsController.listMultichainAccounts, @@ -474,8 +423,10 @@ function setup({ ); } + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); const controller = new AccountTreeController({ - messenger: getAccountTreeControllerMessenger(messenger), + messenger: accountTreeControllerMessenger, state, ...(config && { config }), }); @@ -487,6 +438,7 @@ function setup({ return { controller, messenger, + accountTreeControllerMessenger, spies: { consoleWarn: consoleWarnSpy }, mocks, }; @@ -1802,12 +1754,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(); @@ -1839,7 +1794,7 @@ describe('AccountTreeController', () => { }, }, } as const; - const { controller, messenger } = setup({ + const { controller, accountTreeControllerMessenger } = setup({ accounts: [ MOCK_HD_ACCOUNT_1, nonEvmAccount2, // Wallet 2 > Account 1. @@ -1847,7 +1802,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(); @@ -1969,18 +1927,14 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when AccountsController returns EMPTY_ACCOUNT', () => { - const { controller, messenger } = 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 - messenger.unregisterActionHandler( - 'AccountsController:getSelectedMultichainAccount', - ); - messenger.registerActionHandler( - 'AccountsController:getSelectedMultichainAccount', - () => EMPTY_ACCOUNT_MOCK, + // Mock action handler BEFORE init + mockGetSelectedMultichainAccountActionHandler.mockReturnValue( + EMPTY_ACCOUNT_MOCK, ); controller.init(); @@ -1998,7 +1952,7 @@ describe('AccountTreeController', () => { }); it('falls back to first wallet first group when selected account is not in tree', () => { - const { controller, messenger } = setup({ + const { controller } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2], keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], }); @@ -2009,12 +1963,8 @@ describe('AccountTreeController', () => { id: 'unknown-account-id', }; - messenger.unregisterActionHandler( - 'AccountsController:getSelectedMultichainAccount', - ); - messenger.registerActionHandler( - 'AccountsController:getSelectedMultichainAccount', - () => unknownAccount, + mockGetSelectedMultichainAccountActionHandler.mockReturnValue( + unknownAccount, ); controller.init(); @@ -2032,18 +1982,14 @@ describe('AccountTreeController', () => { }); it('returns empty string when no wallets exist and getSelectedMultichainAccount returns EMPTY_ACCOUNT', () => { - const { controller, messenger } = setup({ + const { controller } = setup({ accounts: [], keyrings: [], }); - // Mock getSelectedMultichainAccount to return EMPTY_ACCOUNT_MOCK (id is '') BEFORE init - messenger.unregisterActionHandler( - 'AccountsController:getSelectedMultichainAccount', - ); - messenger.registerActionHandler( - 'AccountsController:getSelectedMultichainAccount', - () => EMPTY_ACCOUNT_MOCK, + // Mock getSelectedAccount to return EMPTY_ACCOUNT_MOCK (id is '') BEFORE init + mockGetSelectedMultichainAccountActionHandler.mockReturnValue( + EMPTY_ACCOUNT_MOCK, ); controller.init(); @@ -4328,7 +4274,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 372d357a338..dbe6c966f46 100644 --- a/packages/account-tree-controller/src/AccountTreeController.ts +++ b/packages/account-tree-controller/src/AccountTreeController.ts @@ -8,8 +8,8 @@ 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 { BaseController } 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'; import type { InternalAccount } from '@metamask/keyring-internal-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, }, }; @@ -185,11 +185,11 @@ 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), ]; // Initialize trace function @@ -213,28 +213,25 @@ export class AccountTreeController extends BaseController< this.#createBackupAndSyncContext(), ); - 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( + this.messenger.subscribe( 'UserStorageController:stateChange', (userStorageControllerState) => { this.#backupAndSyncService.handleUserStorageStateChange( @@ -243,7 +240,7 @@ export class AccountTreeController extends BaseController< }, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'MultichainAccountService:walletStatusChange', (walletId, status) => { this.#handleMultichainAccountWalletStatusChange(walletId, status); @@ -358,7 +355,7 @@ export class AccountTreeController extends BaseController< log( `Selected (initial) group is: [${this.state.accountTree.selectedAccountGroup}]`, ); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, this.state.accountTree.selectedAccountGroup, previousSelectedAccountGroup, @@ -488,10 +485,7 @@ export class AccountTreeController extends BaseController< let proposedName = ''; // Empty means there's no computed name for this group. for (const id of group.accounts) { - const account = this.messagingSystem.call( - 'AccountsController:getAccount', - id, - ); + const account = this.messenger.call('AccountsController:getAccount', id); if (!account || !account.metadata.name.length) { continue; } @@ -770,10 +764,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. @@ -834,7 +825,7 @@ export class AccountTreeController extends BaseController< } }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:accountTreeChange`, this.state.accountTree, ); @@ -892,14 +883,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, @@ -1056,9 +1047,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'); } /** @@ -1138,7 +1127,7 @@ export class AccountTreeController extends BaseController< `Selected group is now: [${this.state.accountTree.selectedAccountGroup}]`, ); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup, @@ -1146,7 +1135,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, ); @@ -1161,7 +1150,7 @@ export class AccountTreeController extends BaseController< #getDefaultSelectedAccountGroup(wallets: { [walletId: AccountWalletId]: AccountWalletObject; }): AccountGroupId | '' { - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getSelectedMultichainAccount', ); if (selectedAccount && selectedAccount.id) { @@ -1203,7 +1192,7 @@ export class AccountTreeController extends BaseController< this.update((state) => { state.accountTree.selectedAccountGroup = groupId; }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:selectedAccountGroupChange`, groupId, previousSelectedAccountGroup, @@ -1258,7 +1247,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, ); @@ -1301,7 +1290,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, ); @@ -1544,37 +1533,37 @@ 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), ); - 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), ); @@ -1621,7 +1610,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 f370fbfdf5b..20318183277 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,14 +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 type { AccountWalletObject } from './wallet'; +import { + getAccountTreeControllerMessenger, + getRootMessenger, +} from '../tests/mockMessenger'; const ETH_EOA_METHODS = [ EthMethod.PersonalSign, @@ -57,53 +53,15 @@ 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:getSelectedMultichainAccount', - '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 rule = new BaseRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new BaseRule(accountTreeControllerMessenger); - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -129,11 +87,12 @@ describe('BaseRule', () => { }); it('returns account name when account is found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new BaseRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new BaseRule(accountTreeControllerMessenger); - rootMessenger.registerActionHandler( + messenger.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 6874ddc7abf..9023d344de4 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, @@ -17,14 +16,11 @@ import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { AccountWalletEntropyObject } from 'src/wallet'; import { EntropyRule } from './entropy'; +import { + getAccountTreeControllerMessenger, + getRootMessenger, +} from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from '../types'; const ETH_EOA_METHODS = [ EthMethod.PersonalSign, @@ -64,53 +60,15 @@ 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:getSelectedMultichainAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); -} - describe('EntropyRule', () => { describe('getComputedAccountGroupName', () => { it('uses BaseRule implementation', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HD_ACCOUNT_1, ); @@ -138,11 +96,12 @@ describe('EntropyRule', () => { }); it('returns empty string when account is not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -180,9 +139,10 @@ describe('EntropyRule', () => { }); it('getComputedAccountGroupName returns account name with EVM priority', () => { - 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 mockEvmAccount: InternalAccount = { ...MOCK_HD_ACCOUNT_1, @@ -194,7 +154,7 @@ describe('EntropyRule', () => { }, }; - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => mockEvmAccount, ); @@ -220,11 +180,12 @@ describe('EntropyRule', () => { }); it('getComputedAccountGroupName returns empty string when no accounts found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new EntropyRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new EntropyRule(accountTreeControllerMessenger); - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -250,9 +211,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, @@ -264,7 +226,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 033b2c4239a..441c4652b14 100644 --- a/packages/account-tree-controller/src/rules/keyring.test.ts +++ b/packages/account-tree-controller/src/rules/keyring.test.ts @@ -4,20 +4,16 @@ 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, + getRootMessenger, +} from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from '../types'; import type { AccountWalletKeyringObject, AccountWalletObjectOf, @@ -70,52 +66,14 @@ 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:getSelectedMultichainAccount', - 'AccountsController:setSelectedAccount', - 'KeyringController:getState', - 'SnapController:get', - ], - }); - } - describe('getComputedAccountGroupName', () => { 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); - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HARDWARE_ACCOUNT_1, ); @@ -140,11 +98,12 @@ describe('keyring', () => { }); it('returns empty string when account is not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(rootMessenger); - const rule = new KeyringRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new KeyringRule(accountTreeControllerMessenger); - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -198,12 +157,13 @@ describe('keyring', () => { ); it('getComputedAccountGroupName returns computed name from base class', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(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 - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_HARDWARE_ACCOUNT_1, ); @@ -231,12 +191,13 @@ describe('keyring', () => { }); it('getComputedAccountGroupName returns empty string when account not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(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) - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -263,9 +224,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 839d859856d..672f074db72 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'; @@ -12,14 +11,11 @@ import type { SnapId } from '@metamask/snaps-sdk'; import type { Snap } from '@metamask/snaps-utils'; import { SnapRule } from './snap'; +import { + getAccountTreeControllerMessenger, + getRootMessenger, +} from '../../tests/mockMessenger'; import type { AccountGroupObjectOf } from '../group'; -import type { - AccountTreeControllerMessenger, - AccountTreeControllerActions, - AccountTreeControllerEvents, - AllowedActions, - AllowedEvents, -} from '../types'; import type { AccountWalletObjectOf, AccountWalletSnapObject } from '../wallet'; const ETH_EOA_METHODS = [ @@ -58,55 +54,16 @@ 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', - 'MultichainAccountService:walletStatusChange', - ], - allowedActions: [ - 'AccountsController:listMultichainAccounts', - 'AccountsController:getAccount', - 'AccountsController:getSelectedMultichainAccount', - '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 rule = new SnapRule(messenger); + const messenger = getRootMessenger(); + const accountTreeControllerMessenger = + getAccountTreeControllerMessenger(messenger); + const rule = new SnapRule(accountTreeControllerMessenger); // Mock the AccountsController to return an account - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => MOCK_SNAP_ACCOUNT_1, ); @@ -131,12 +88,13 @@ describe('SnapRule', () => { }); it('returns empty string when account not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(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) - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'AccountsController:getAccount', () => undefined, ); @@ -174,12 +132,13 @@ describe('SnapRule', () => { describe('getDefaultAccountWalletName', () => { it('returns snap proposed name when available', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(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 - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'SnapController:get', () => MOCK_SNAP_1 as unknown as Snap, ); @@ -199,9 +158,10 @@ describe('SnapRule', () => { }); it('returns cleaned snap ID when no proposed name available', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(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, @@ -215,7 +175,7 @@ describe('SnapRule', () => { }; // Mock SnapController to return snap without proposed name - rootMessenger.registerActionHandler( + messenger.registerActionHandler( 'SnapController:get', () => snapWithoutProposedName as unknown as Snap, ); @@ -239,15 +199,13 @@ describe('SnapRule', () => { }); it('returns cleaned snap ID when snap not found', () => { - const rootMessenger = getRootMessenger(); - const messenger = getAccountTreeControllerMessenger(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) - rootMessenger.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/src/types.ts b/packages/account-tree-controller/src/types.ts index 2f3b579e32e..da288a1b565 100644 --- a/packages/account-tree-controller/src/types.ts +++ b/packages/account-tree-controller/src/types.ts @@ -12,10 +12,10 @@ import type { import { type ControllerGetStateAction, type ControllerStateChangeEvent, - type RestrictedMessenger, } from '@metamask/base-controller'; import type { TraceCallback } from '@metamask/controller-utils'; import type { KeyringControllerGetStateAction } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { MultichainAccountServiceCreateMultichainAccountGroupAction } from '@metamask/multichain-account-service'; import type { AuthenticationController, @@ -174,12 +174,10 @@ export type AccountTreeControllerEvents = | AccountTreeControllerAccountTreeChangeEvent | AccountTreeControllerSelectedAccountGroupChangeEvent; -export type AccountTreeControllerMessenger = RestrictedMessenger< +export type AccountTreeControllerMessenger = Messenger< typeof controllerName, AccountTreeControllerActions | AllowedActions, - AccountTreeControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + AccountTreeControllerEvents | AllowedEvents >; export type AccountTreeControllerConfig = { diff --git a/packages/account-tree-controller/tests/mockMessenger.ts b/packages/account-tree-controller/tests/mockMessenger.ts new file mode 100644 index 00000000000..10fa770a675 --- /dev/null +++ b/packages/account-tree-controller/tests/mockMessenger.ts @@ -0,0 +1,71 @@ +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; + +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< + MockAnyNamespace, + AllAccountTreeControllerActions, + AllAccountTreeControllerEvents + >({ namespace: MOCK_ANY_NAMESPACE }); +} + +/** + * 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:selectedAccountChange', + 'UserStorageController:stateChange', + 'MultichainAccountService:walletStatusChange', + ], + actions: [ + 'AccountsController:listMultichainAccounts', + 'AccountsController:getAccount', + 'AccountsController:getSelectedMultichainAccount', + 'AccountsController:setSelectedAccount', + 'UserStorageController:getState', + 'UserStorageController:performGetStorage', + 'UserStorageController:performGetStorageAllFeatureEntries', + 'UserStorageController:performSetStorage', + 'UserStorageController:performBatchSetStorage', + 'AuthenticationController:getSessionProfile', + 'MultichainAccountService:createMultichainAccountGroup', + 'KeyringController:getState', + 'SnapController:get', + ], + }); + return accountTreeControllerMessenger; +} 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 ca31cc28bbc..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" }, @@ -20,5 +23,5 @@ "path": "../profile-sync-controller" } ], - "include": ["../../types", "./src"] + "include": ["../../types", "./src", "./tests"] } diff --git a/packages/accounts-controller/CHANGELOG.md b/packages/accounts-controller/CHANGELOG.md index 6f3975077bf..40081dde98f 100644 --- a/packages/accounts-controller/CHANGELOG.md +++ b/packages/accounts-controller/CHANGELOG.md @@ -22,6 +22,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/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)) - Bump `@metamask/network-controller` from `^24.2.2` to `^24.3.0` ([#6883](https://github.com/MetaMask/core/pull/6883)) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 14d01134071..5ad69009409 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -53,6 +53,7 @@ "@metamask/keyring-api": "^21.0.0", "@metamask/keyring-internal-api": "^9.0.0", "@metamask/keyring-utils": "^3.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/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 33165be78d6..45a54ebaa04 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,4 +1,4 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { InfuraNetworkType } from '@metamask/controller-utils'; import type { AccountAssetListUpdatedEventPayload, @@ -15,6 +15,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 type { NetworkClientId } from '@metamask/network-controller'; import type { SnapControllerState } from '@metamask/snaps-controllers'; import { SnapStatus } from '@metamask/snaps-utils'; @@ -23,11 +30,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 { @@ -41,6 +45,17 @@ import { keyringTypeToName, } from './utils'; +type AllAccountsControllerActions = + MessengerActions; + +type AllAccountsControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + 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 @@ -217,27 +232,37 @@ 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< - AccountsControllerActions | AllowedActions, - AccountsControllerEvents | AllowedEvents - >(); +function buildMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_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 root 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 +270,8 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { 'SnapKeyring:accountTransactionsUpdated', 'MultichainNetworkController:networkDidChange', ], - allowedActions: [ - 'KeyringController:getState', - 'KeyringController:getKeyringsByType', - ], }); + return accountsControllerMessenger; } /** @@ -257,7 +279,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 +287,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 +305,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 +1158,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 +1173,8 @@ describe('AccountsController', () => { messenger, }); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -1172,11 +1195,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 +1459,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 +1475,8 @@ describe('AccountsController', () => { messenger, }); + const messengerSpy = jest.spyOn(accountsControllerMessenger, 'publish'); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -1473,11 +1496,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, ); @@ -3165,19 +3187,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); @@ -3271,10 +3294,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, @@ -3349,16 +3372,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'); @@ -3922,19 +3946,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 +3977,7 @@ describe('AccountsController', () => { }); it('persists expected state', () => { - const { accountsController: controller } = setupAccountsController(); + const { accountsController: controller } = setupAccountsController({}); expect( deriveStateFromMetadata( @@ -3972,7 +3996,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 2bd0fb04af9..a4b7287d158 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, @@ -218,19 +217,17 @@ 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 = { internalAccounts: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -502,7 +499,7 @@ export class AccountsController extends BaseController< state.internalAccounts.selectedAccount = account.id; }); - this.messagingSystem.publish( + this.messenger.publish( 'AccountsController:accountRenamed', internalAccount, ); @@ -546,7 +543,7 @@ export class AccountsController extends BaseController< }); if (metadata.name) { - this.messagingSystem.publish( + this.messenger.publish( 'AccountsController:accountRenamed', internalAccount, ); @@ -566,9 +563,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); @@ -725,7 +720,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, ); @@ -749,7 +744,7 @@ export class AccountsController extends BaseController< event: EventType, ...payload: ExtractEventPayload ): void { - this.messagingSystem.publish(event, ...payload); + this.messenger.publish(event, ...payload); } /** @@ -903,14 +898,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); } }, ); @@ -971,12 +963,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, ); @@ -1218,17 +1210,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( @@ -1237,7 +1227,7 @@ export class AccountsController extends BaseController< ), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'SnapKeyring:accountBalancesUpdated', (snapAccountEvent) => this.#handleOnSnapKeyringAccountEvent( @@ -1246,7 +1236,7 @@ export class AccountsController extends BaseController< ), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'SnapKeyring:accountTransactionsUpdated', (snapAccountEvent) => this.#handleOnSnapKeyringAccountEvent( @@ -1256,7 +1246,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), ); @@ -1266,67 +1256,67 @@ 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:getAccounts`, this.getAccounts.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `AccountsController:updateAccountMetadata`, this.updateAccountMetadata.bind(this), ); 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"] } diff --git a/packages/address-book-controller/CHANGELOG.md b/packages/address-book-controller/CHANGELOG.md index f594585167d..7fa4ef6fe21 100644 --- a/packages/address-book-controller/CHANGELOG.md +++ b/packages/address-book-controller/CHANGELOG.md @@ -21,6 +21,8 @@ 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)) diff --git a/packages/address-book-controller/package.json b/packages/address-book-controller/package.json index a77761b577b..9b28bcf7695 100644 --- a/packages/address-book-controller/package.json +++ b/packages/address-book-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/address-book-controller/src/AddressBookController.test.ts b/packages/address-book-controller/src/AddressBookController.test.ts index 0a918ce8716..1b8e4ed5fb3 100644 --- a/packages/address-book-controller/src/AddressBookController.test.ts +++ b/packages/address-book-controller/src/AddressBookController.test.ts @@ -1,12 +1,17 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { toHex } from '@metamask/controller-utils'; +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, @@ -14,23 +19,39 @@ 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 messenger = new Messenger< - AddressBookControllerActions, - AddressBookControllerEvents - >(); - const restrictedMessenger = messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], + const rootMessenger = getRootMessenger(); + const addressBookControllerMessenger = new Messenger< + typeof controllerName, + AllActions, + AllEvents, + typeof rootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, }); const controller = new AddressBookController({ - messenger: restrictedMessenger, + messenger: addressBookControllerMessenger, }); // Set up mock event listeners @@ -38,11 +59,11 @@ function arrangeMocks() { const contactDeletedListener = jest.fn(); // Subscribe to events - messenger.subscribe( - 'AddressBookController:contactUpdated' as AddressBookControllerContactUpdatedEvent['type'], + rootMessenger.subscribe( + 'AddressBookController:contactUpdated', contactUpdatedListener, ); - messenger.subscribe( + rootMessenger.subscribe( 'AddressBookController:contactDeleted' as AddressBookControllerContactDeletedEvent['type'], contactDeletedListener, ); @@ -627,7 +648,7 @@ describe('AddressBookController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/address-book-controller/src/AddressBookController.ts b/packages/address-book-controller/src/AddressBookController.ts index e02200b0710..13d00a1561b 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'; +} from '@metamask/base-controller/next'; +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'; /** @@ -150,7 +150,7 @@ const addressBookControllerMetadata = { addressBook: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -170,12 +170,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 >; /** @@ -275,7 +273,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, ); @@ -335,10 +333,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; @@ -348,15 +343,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), ); 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"] } diff --git a/packages/announcement-controller/CHANGELOG.md b/packages/announcement-controller/CHANGELOG.md index e6dc4e4bedd..cd4c4c482a4 100644 --- a/packages/announcement-controller/CHANGELOG.md +++ b/packages/announcement-controller/CHANGELOG.md @@ -21,6 +21,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.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)) ## [7.0.3] diff --git a/packages/announcement-controller/package.json b/packages/announcement-controller/package.json index e1b17d44a3a..4247e711459 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.4.2" + "@metamask/base-controller": "^8.4.2", + "@metamask/messenger": "^0.3.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 32fed68792e..ee36b0ce4e5 100644 --- a/packages/announcement-controller/src/AnnouncementController.test.ts +++ b/packages/announcement-controller/src/AnnouncementController.test.ts @@ -1,4 +1,9 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { AnnouncementControllerState, @@ -16,15 +21,20 @@ const name = 'AnnouncementController'; * * @returns A restricted controller messenger. */ -function getRestrictedMessenger() { +function getMessenger() { const messenger = new Messenger< + MockAnyNamespace, AnnouncementControllerActions, AnnouncementControllerEvents - >(); - return messenger.getRestricted({ - name, - allowedActions: [], - allowedEvents: [], + >({ namespace: MOCK_ANY_NAMESPACE }); + return new Messenger< + typeof name, + AnnouncementControllerActions, + AnnouncementControllerEvents, + typeof messenger + >({ + namespace: name, + parent: messenger, }); } const allAnnouncements: AnnouncementMap = { @@ -89,7 +99,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 +120,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 +133,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 +152,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 +164,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, }); @@ -168,7 +178,7 @@ describe('announcement controller', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); @@ -176,7 +186,7 @@ describe('announcement controller', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -198,7 +208,7 @@ describe('announcement controller', () => { it('includes expected state in state logs', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); @@ -228,7 +238,7 @@ describe('announcement controller', () => { it('persists expected state', () => { const controller = new AnnouncementController({ - messenger: getRestrictedMessenger(), + messenger: getMessenger(), allAnnouncements, }); @@ -258,7 +268,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 66f9858031a..f98cca82034 100644 --- a/packages/announcement-controller/src/AnnouncementController.ts +++ b/packages/announcement-controller/src/AnnouncementController.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'; type ViewedAnnouncement = { [id: number]: boolean; @@ -64,17 +64,15 @@ const metadata: StateMetadata = { announcements: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, }; -export type AnnouncementControllerMessenger = RestrictedMessenger< +export type AnnouncementControllerMessenger = Messenger< typeof controllerName, AnnouncementControllerActions, - AnnouncementControllerEvents, - never, - never + AnnouncementControllerEvents >; /** diff --git a/packages/announcement-controller/tsconfig.build.json b/packages/announcement-controller/tsconfig.build.json index e5fd7422b9a..931c4d6594b 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..68c3ddfc2cd 100644 --- a/packages/announcement-controller/tsconfig.json +++ b/packages/announcement-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/app-metadata-controller/CHANGELOG.md b/packages/app-metadata-controller/CHANGELOG.md index 8d781a8776f..a7285fc295d 100644 --- a/packages/app-metadata-controller/CHANGELOG.md +++ b/packages/app-metadata-controller/CHANGELOG.md @@ -21,6 +21,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.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)) ## [1.0.0] diff --git a/packages/app-metadata-controller/package.json b/packages/app-metadata-controller/package.json index 06c2cbac699..17ce8732b34 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.4.2" + "@metamask/base-controller": "^8.4.2", + "@metamask/messenger": "^0.3.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 16c379b1a51..25722e514fd 100644 --- a/packages/app-metadata-controller/src/AppMetadataController.test.ts +++ b/packages/app-metadata-controller/src/AppMetadataController.test.ts @@ -1,9 +1,16 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, +} from '@metamask/messenger'; import { AppMetadataController, getDefaultAppMetadataControllerState, type AppMetadataControllerOptions, + type AppMetadataControllerActions, + type AppMetadataControllerEvents, } from './AppMetadataController'; describe('AppMetadataController', () => { @@ -128,7 +135,7 @@ describe('AppMetadataController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -219,12 +226,20 @@ function withController( ): ReturnValue { const [options = {}, fn] = args.length === 2 ? args : [{}, args[0]]; - const messenger = new Messenger(); - - const appMetadataControllerMessenger = messenger.getRestricted({ - name: 'AppMetadataController', - allowedActions: [], - allowedEvents: [], + const rootMessenger = new Messenger< + MockAnyNamespace, + AppMetadataControllerActions, + AppMetadataControllerEvents + >({ namespace: MOCK_ANY_NAMESPACE }); + + const appMetadataControllerMessenger = new Messenger< + 'AppMetadataController', + AppMetadataControllerActions, + AppMetadataControllerEvents, + 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 b71818b7fd6..ffd346aea48 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'; +} from '@metamask/base-controller/next'; +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 >; /** @@ -105,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; 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..68c3ddfc2cd 100644 --- a/packages/app-metadata-controller/tsconfig.json +++ b/packages/app-metadata-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/approval-controller/CHANGELOG.md b/packages/approval-controller/CHANGELOG.md index 1ded7e4bc93..a911aec0ec1 100644 --- a/packages/approval-controller/CHANGELOG.md +++ b/packages/approval-controller/CHANGELOG.md @@ -21,6 +21,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.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/approval-controller/package.json b/packages/approval-controller/package.json index cf16f60c99d..a22204434a3 100644 --- a/packages/approval-controller/package.json +++ b/packages/approval-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.2", + "@metamask/messenger": "^0.3.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/utils": "^11.8.1", "nanoid": "^3.3.8" diff --git a/packages/approval-controller/src/ApprovalController.test.ts b/packages/approval-controller/src/ApprovalController.test.ts index e817a38ed56..652a87ba4f3 100644 --- a/packages/approval-controller/src/ApprovalController.test.ts +++ b/packages/approval-controller/src/ApprovalController.test.ts @@ -1,6 +1,13 @@ /* eslint-disable jest/expect-expect */ -import { deriveStateFromMetadata, Messenger } 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 { errorCodes, JsonRpcError } from '@metamask/rpc-errors'; import { nanoid } from 'nanoid'; @@ -9,6 +16,7 @@ import type { AddApprovalOptions, ApprovalControllerActions, ApprovalControllerEvents, + ApprovalControllerMessenger, ErrorOptions, StartFlowOptions, SuccessOptions, @@ -28,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'; @@ -223,20 +237,26 @@ 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< - ApprovalControllerActions, - ApprovalControllerEvents - >(); - return messenger.getRestricted({ - name: 'ApprovalController', - allowedActions: [], - allowedEvents: [], +function getMessengers() { + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, }); + return { + rootMessenger, + approvalControllerMessenger: new Messenger< + typeof controllerName, + ApprovalControllerActions, + ApprovalControllerEvents, + typeof rootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }), + }; } describe('approval controller', () => { @@ -250,7 +270,7 @@ describe('approval controller', () => { showApprovalRequest = jest.fn(); approvalController = new ApprovalController({ - messenger: getRestrictedMessenger(), + messenger: getMessengers().approvalControllerMessenger, showApprovalRequest, }); }); @@ -445,7 +465,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 +658,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 +698,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 +1289,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 +1308,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 +1327,10 @@ describe('approval controller', () => { }); it('updateRequestState', () => { - const messenger = new Messenger< - ApprovalControllerActions, - ApprovalControllerEvents - >(); + const { approvalControllerMessenger } = getMessengers(); approvalController = new ApprovalController({ - messenger: messenger.getRestricted({ - name: controllerName, - allowedActions: [], - allowedEvents: [], - }), + messenger: approvalControllerMessenger, showApprovalRequest, }); @@ -1344,10 +1343,13 @@ describe('approval controller', () => { requestState: { foo: 'bar' }, }); - messenger.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', @@ -1719,7 +1721,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 9f577677cf5..f2e7efe13c3 100644 --- a/packages/approval-controller/src/ApprovalController.ts +++ b/packages/approval-controller/src/ApprovalController.ts @@ -1,9 +1,12 @@ -import type { ControllerGetStateAction } from '@metamask/base-controller'; +import type { + ControllerGetStateAction, + StateMetadata, +} from '@metamask/base-controller/next'; 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'; @@ -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, }, }; @@ -134,12 +137,10 @@ export type ApprovalControllerState = { approvalFlows: ApprovalFlowState[]; }; -export type ApprovalControllerMessenger = RestrictedMessenger< +export type ApprovalControllerMessenger = Messenger< typeof controllerName, ApprovalControllerActions, - ApprovalControllerEvents, - never, - never + ApprovalControllerEvents >; // Option Types @@ -413,12 +414,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) { @@ -428,47 +429,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/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/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 6b4d6e6f690..2d5ee3df749 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -38,12 +38,16 @@ 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` ### Changed +- **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`. +- 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` - 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/assets-controllers/package.json b/packages/assets-controllers/package.json index 07de6f4f141..0b332da6f79 100644 --- a/packages/assets-controllers/package.json +++ b/packages/assets-controllers/package.json @@ -59,6 +59,7 @@ "@metamask/controller-utils": "^11.14.1", "@metamask/eth-query": "^4.0.0", "@metamask/keyring-api": "^21.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/polling-controller": "^14.0.2", "@metamask/rpc-errors": "^7.0.2", diff --git a/packages/assets-controllers/src/AccountTrackerController.test.ts b/packages/assets-controllers/src/AccountTrackerController.test.ts index 8d37b7cd5ce..94e2b9f272c 100644 --- a/packages/assets-controllers/src/AccountTrackerController.test.ts +++ b/packages/assets-controllers/src/AccountTrackerController.test.ts @@ -1,6 +1,13 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; 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, @@ -14,26 +21,30 @@ import { 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 { AccountsApiBalanceFetcher } from './multi-chain-accounts-service/api-balance-fetcher'; 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'), @@ -1368,7 +1379,7 @@ describe('AccountTrackerController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -1428,10 +1439,7 @@ type WithControllerCallback = ({ controller, }: { controller: AccountTrackerController; - messenger: Messenger< - ExtractAvailableAction | AllowedActions, - ExtractAvailableEvent | AllowedEvents - >; + messenger: RootMessenger; triggerSelectedAccountChange: (account: InternalAccount) => void; refresh: ( clock: SinonFakeTimers, @@ -1475,10 +1483,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( @@ -1584,16 +1591,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: [ + events: [ 'AccountsController:selectedEvmAccountChange', 'TransactionController:unapprovedTransactionAdded', 'TransactionController:transactionConfirmed', diff --git a/packages/assets-controllers/src/AccountTrackerController.ts b/packages/assets-controllers/src/AccountTrackerController.ts index d0e07e9a96a..9d7cf07c856 100644 --- a/packages/assets-controllers/src/AccountTrackerController.ts +++ b/packages/assets-controllers/src/AccountTrackerController.ts @@ -8,8 +8,8 @@ import type { import type { ControllerStateChangeEvent, ControllerGetStateAction, - RestrictedMessenger, -} from '@metamask/base-controller'; + StateMetadata, +} from '@metamask/base-controller/next'; import { query, safelyExecuteWithTimeout, @@ -17,6 +17,7 @@ import { } from '@metamask/controller-utils'; import EthQuery from '@metamask/eth-query'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClient, NetworkClientId, @@ -127,11 +128,11 @@ export type AccountTrackerControllerState = { accountsByChainId: Record; }; -const accountTrackerMetadata = { +const accountTrackerMetadata: StateMetadata = { accountsByChainId: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -205,12 +206,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} */ @@ -243,7 +242,7 @@ export class AccountTrackerController extends StaticIntervalPollingController { if (newAddress !== prevAddress) { @@ -317,7 +316,7 @@ export class AccountTrackerController extends StaticIntervalPollingController event.address, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:unapprovedTransactionAdded', async (transactionMeta: TransactionMeta) => { await this.#refreshAddress( @@ -327,7 +326,7 @@ export class AccountTrackerController extends StaticIntervalPollingController { await this.#refreshAddress( @@ -342,12 +341,12 @@ export class AccountTrackerController extends StaticIntervalPollingController toChecksumHexAddress(internalAccount.address), @@ -401,12 +400,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, ); @@ -414,12 +413,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, ); @@ -460,13 +459,12 @@ export class AccountTrackerController extends StaticIntervalPollingController; + +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 a3d69a8670c..1313cd75ea6 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/packages/assets-controllers/src/CurrencyRateController.test.ts b/packages/assets-controllers/src/CurrencyRateController.test.ts index ce7e559b7a5..e11d7683785 100644 --- a/packages/assets-controllers/src/CurrencyRateController.test.ts +++ b/packages/assets-controllers/src/CurrencyRateController.test.ts @@ -1,32 +1,44 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; 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 +64,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 +94,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 +112,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 +135,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 +167,7 @@ describe('CurrencyRateController', () => { usd: 22, }, }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ interval: 100, fetchMultiExchangeRate: fetchMultiExchangeRateStub, @@ -188,7 +205,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 +231,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 +262,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 +314,7 @@ describe('CurrencyRateController', () => { }, }; }); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ fetchMultiExchangeRate: fetchMultiExchangeRateStub, messenger, @@ -337,7 +354,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 +411,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 +435,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 +467,7 @@ describe('CurrencyRateController', () => { }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -480,7 +497,7 @@ describe('CurrencyRateController', () => { }, }, }; - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state }); // Error should still be thrown @@ -505,7 +522,7 @@ describe('CurrencyRateController', () => { POL: { XYZ: 0.3 }, }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -548,7 +565,7 @@ describe('CurrencyRateController', () => { }) .persist(); - const messenger = getRestrictedMessenger(); + const messenger = getCurrencyRateControllerMessenger(); const controller = new CurrencyRateController({ messenger, state: { currentCurrency: 'xyz' }, @@ -576,7 +593,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 +617,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 +640,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 +664,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 +688,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 +716,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 +748,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 +778,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, @@ -781,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 { @@ -806,7 +823,7 @@ describe('CurrencyRateController', () => { it('includes expected state in state logs', () => { const controller = new CurrencyRateController({ - messenger: getRestrictedMessenger(), + messenger: getCurrencyRateControllerMessenger(), }); expect( @@ -831,7 +848,7 @@ describe('CurrencyRateController', () => { it('persists expected state', () => { const controller = new CurrencyRateController({ - messenger: getRestrictedMessenger(), + messenger: getCurrencyRateControllerMessenger(), }); expect( @@ -856,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 9827eb2ea67..a116e8ac337 100644 --- a/packages/assets-controllers/src/CurrencyRateController.ts +++ b/packages/assets-controllers/src/CurrencyRateController.ts @@ -1,12 +1,13 @@ import type { - RestrictedMessenger, ControllerGetStateAction, ControllerStateChangeEvent, -} from '@metamask/base-controller'; + StateMetadata, +} 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,25 +52,23 @@ 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 = { +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, }, }; @@ -113,7 +112,7 @@ export class CurrencyRateController extends StaticIntervalPollingController, - ExtractAvailableEvent +type AllDefiPositionsControllerActions = + MessengerActions; + +type AllDefiPositionsControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllDefiPositionsControllerActions, + AllDefiPositionsControllerEvents >; /** @@ -65,7 +74,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( @@ -73,10 +84,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', @@ -105,7 +125,7 @@ function setupController({ groupDeFiPositionsSpy.mockImplementation(mockGroupDeFiPositions); const controller = new DeFiPositionsController({ - messenger: restrictedMessenger, + messenger: defiPositionControllerMessenger, isEnabled, trackEvent: mockTrackEvent, }); @@ -512,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 53a61974e25..9b53e15443c 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'; @@ -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, }, }; @@ -123,12 +123,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 >; /** @@ -176,15 +174,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()) { @@ -195,7 +193,7 @@ export class DeFiPositionsController extends StaticIntervalPollingController()< }, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:accountAdded', async (account) => { if (!this.#isEnabled() || !account.type.startsWith('eip155:')) { @@ -214,9 +212,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; 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, diff --git a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts index 43c04096ab6..5d6d519dc02 100644 --- a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts +++ b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.test.ts @@ -1,21 +1,37 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; 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 AllowedActions, - type AllowedEvents, -} from './MultichainAssetsRatesController'; +import { type MultichainAssetsRatesControllerMessenger } 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', @@ -132,7 +148,9 @@ const setupController = ({ >; accountsAssets?: InternalAccount[]; } = {}) => { - const messenger = new Messenger(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); messenger.registerActionHandler( 'MultichainAssetsController:getState', @@ -180,16 +198,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', @@ -946,7 +973,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( @@ -1003,24 +1032,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(); @@ -1073,7 +1113,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 0b8054ed01a..c4a3260989b 100644 --- a/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts +++ b/packages/assets-controllers/src/MultichainAssetsRatesController/MultichainAssetsRatesController.ts @@ -4,16 +4,17 @@ import type { AccountsControllerGetSelectedMultichainAccountAction, } from '@metamask/accounts-controller'; import type { - RestrictedMessenger, ControllerStateChangeEvent, ControllerGetStateAction, -} from '@metamask/base-controller'; + StateMetadata, +} 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 { @@ -143,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 >; /** @@ -158,17 +157,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, }, }; @@ -213,7 +212,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, @@ -237,22 +236,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; @@ -262,7 +261,7 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro currencyRateControllerState.currentCurrency, ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'MultichainAssetsController:accountAssetListUpdated', async ({ assets }) => { const newAccountAssets = Object.entries(assets).map( @@ -313,9 +312,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'); } /** @@ -577,11 +574,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, @@ -736,7 +731,7 @@ export class MultichainAssetsRatesController extends StaticIntervalPollingContro async #handleSnapRequest(args: SnapRequestArgs): Promise { const { snapId, handler, params } = args; try { - return await this.messagingSystem.call('SnapController:handleRequest', { + return await this.messenger.call('SnapController:handleRequest', { snapId, origin: 'metamask', handler, 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, diff --git a/packages/assets-controllers/src/NftController.test.ts b/packages/assets-controllers/src/NftController.test.ts index 1f8a75e2f6e..6a8eecb93ba 100644 --- a/packages/assets-controllers/src/NftController.test.ts +++ b/packages/assets-controllers/src/NftController.test.ts @@ -6,7 +6,7 @@ import type { } from '@metamask/accounts-controller'; import type { ApprovalControllerMessenger } from '@metamask/approval-controller'; import { ApprovalController } from '@metamask/approval-controller'; -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { IPFS_DEFAULT_GATEWAY_URL, ERC1155, @@ -22,14 +22,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 +59,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'; @@ -226,15 +235,9 @@ function setupController({ mockGetNetworkClientIdByChainId?: Record; displayNftMedia?: boolean; } = {}) { - const messenger = new Messenger< - | ExtractAvailableAction - | NftControllerAllowedActions - | ExtractAvailableAction, - | ExtractAvailableEvent - | NftControllerAllowedEvents - | ExtractAvailableEvent - | AccountsControllerSelectedAccountChangeEvent - >(); + const messenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const getNetworkClientById = buildMockGetNetworkClientById( mockNetworkClientConfigurationsByNetworkClientId, @@ -331,10 +334,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({ @@ -350,14 +357,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', @@ -371,7 +382,7 @@ function setupController({ 'NetworkController:findNetworkClientIdByChainId', 'PhishingController:bulkScanUrls', ], - allowedEvents: [ + events: [ 'AccountsController:selectedEvmAccountChange', 'PreferencesController:stateChange', ], @@ -379,7 +390,7 @@ function setupController({ const nftController = new NftController({ onNftAdded: jest.fn(), - messenger: nftControllerMessenger as NftControllerMessenger, + messenger: nftControllerMessenger, ...options, }); @@ -408,6 +419,7 @@ function setupController({ return { nftController, messenger, + nftControllerMessenger, approvalController, triggerPreferencesStateChange, triggerSelectedAccountChange, @@ -645,11 +657,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( @@ -684,11 +696,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( @@ -719,7 +731,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -746,7 +758,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` @@ -846,7 +858,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -872,7 +884,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` @@ -972,7 +984,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -998,7 +1010,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` @@ -1098,7 +1110,7 @@ describe('NftController', () => { ); const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -1125,7 +1137,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` @@ -1226,7 +1238,7 @@ describe('NftController', () => { const { nftController, - messenger, + nftControllerMessenger, triggerPreferencesStateChange, triggerSelectedAccountChange, } = setupController({ @@ -1256,7 +1268,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` @@ -1359,20 +1371,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, @@ -1385,7 +1400,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` @@ -5951,7 +5966,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 adbe53087c4..1027997d7f1 100644 --- a/packages/assets-controllers/src/NftController.ts +++ b/packages/assets-controllers/src/NftController.ts @@ -5,14 +5,12 @@ 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'; + type StateMetadata, +} from '@metamask/base-controller/next'; import { safelyExecute, handleFetch, @@ -29,6 +27,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, @@ -238,23 +237,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, }, }; @@ -309,12 +308,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 => ({ @@ -399,7 +396,7 @@ export class NftController extends BaseController< }, }); - this.#selectedAccountId = this.messagingSystem.call( + this.#selectedAccountId = this.messenger.call( 'AccountsController:getSelectedAccount', ).id; this.#ipfsGateway = ipfsGateway; @@ -408,12 +405,12 @@ export class NftController extends BaseController< this.#isIpfsGatewayEnabled = isIpfsGatewayEnabled; this.#onNftAdded = onNftAdded; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'PreferencesController:stateChange', this.#onPreferencesControllerStateChange.bind(this), ); - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountsController:selectedEvmAccountChange', this.#onSelectedAccountChange.bind(this), ); @@ -441,7 +438,7 @@ export class NftController extends BaseController< displayNftMedia?: boolean; openSeaEnabled?: boolean; }) { - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getSelectedAccount', ); this.#selectedAccountId = selectedAccount.id; @@ -748,7 +745,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, @@ -761,7 +758,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, @@ -804,7 +801,7 @@ export class NftController extends BaseController< ): Promise { const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -853,12 +850,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, @@ -1055,7 +1052,7 @@ export class NftController extends BaseController< const { allNftContracts } = this.state; const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1388,7 +1385,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, @@ -1401,7 +1398,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, @@ -1520,7 +1517,7 @@ export class NftController extends BaseController< ); const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ); @@ -1574,7 +1571,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), ); @@ -1679,7 +1676,7 @@ export class NftController extends BaseController< const { configuration: { chainId }, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId as NetworkClientId, ); @@ -1721,7 +1718,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, ); @@ -1772,7 +1769,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, ); @@ -1848,7 +1845,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, ); @@ -1899,7 +1896,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, ); @@ -2070,7 +2067,7 @@ export class NftController extends BaseController< } async _requestApproval(suggestedNftMeta: SuggestedNftMeta) { - return this.messagingSystem.call( + return this.messenger.call( 'ApprovalController:addRequest', { id: suggestedNftMeta.id, @@ -2099,7 +2096,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, ); @@ -2212,7 +2209,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, ); diff --git a/packages/assets-controllers/src/NftDetectionController.test.ts b/packages/assets-controllers/src/NftDetectionController.test.ts index 98d4e9b58b7..5cb89d7549e 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, @@ -36,10 +42,15 @@ import { getDefaultNftControllerState } from './NftController'; import { NftDetectionController, BlockaidResultType, - type AllowedActions, - type AllowedEvents, + type NftDetectionControllerMessenger, } from './NftDetectionController'; +type AllActions = MessengerActions; + +type AllEvents = MessengerEvents; + +type RootMessenger = Messenger; + const controllerName = 'NftDetectionController' as const; const defaultSelectedAccount = createMockInternalAccount(); @@ -1069,7 +1080,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', @@ -1109,21 +1122,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 219ca024f92..3f6845a7a30 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, @@ -11,6 +14,7 @@ import { handleFetch, toHex, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClient, NetworkControllerGetNetworkClientByIdAction, @@ -37,6 +41,7 @@ const controllerName = 'NftDetectionController'; export type NFTDetectionControllerState = Record; export type AllowedActions = + | ControllerGetStateAction | AddApprovalRequest | NetworkControllerGetStateAction | NetworkControllerGetNetworkClientByIdAction @@ -45,15 +50,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 >; /** @@ -498,7 +505,7 @@ export class NftDetectionController extends BaseController< this.#getNftState = getNftState; this.#addNft = addNft; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'PreferencesController:stateChange', this.#onPreferencesControllerStateChange.bind(this), ); @@ -510,12 +517,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, ); @@ -586,8 +593,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) => @@ -693,7 +699,7 @@ export class NftDetectionController extends BaseController< collection && { collection }, chainId && { chainId }, ); - const networkClientId = this.messagingSystem.call( + const networkClientId = this.messenger.call( 'NetworkController:findNetworkClientIdByChainId', toHex(chainId), ); diff --git a/packages/assets-controllers/src/RatesController/RatesController.test.ts b/packages/assets-controllers/src/RatesController/RatesController.test.ts index e2b125ea4db..2fadaeda774 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,18 +76,19 @@ function setupRatesController({ }: { interval?: number; initialState?: Partial; - messenger: Messenger; + messenger: RootMessenger; includeUsdRate: boolean; 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', () => { @@ -89,9 +96,9 @@ describe('RatesController', () => { describe('construct', () => { it('constructs the RatesController with default values', () => { - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ initialState: {}, - messenger: buildMessenger(), + messenger: buildRootMessenger(), includeUsdRate: false, }); const { fiatCurrency, rates, cryptocurrencies } = ratesController.state; @@ -118,8 +125,7 @@ describe('RatesController', () => { }); it('starts the polling process with default values', async () => { - const messenger = buildMessenger(); - const publishActionSpy = jest.spyOn(messenger, 'publish'); + const messenger = buildRootMessenger(); jest.spyOn(global.Date, 'now').mockImplementation(() => getStubbedDate()); const mockBtcRateValue = 57715.42; @@ -135,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; @@ -216,13 +224,13 @@ describe('RatesController', () => { }); }); - const ratesController = setupRatesController({ + const { ratesController } = setupRatesController({ interval: 150, initialState: { cryptocurrencies: [Cryptocurrency.Btc], fiatCurrency: 'eur', }, - messenger: buildMessenger(), + messenger: buildRootMessenger(), includeUsdRate: true, fetchMultiExchangeRate: fetchExchangeRateStub, }); @@ -263,16 +271,17 @@ describe('RatesController', () => { }); it('stops the polling process', async () => { - const messenger = buildMessenger(); - const publishActionSpy = jest.spyOn(messenger, 'publish'); + const messenger = buildRootMessenger(); 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(); @@ -313,12 +322,12 @@ 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, }, - messenger: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -332,10 +341,10 @@ 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: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -363,10 +372,10 @@ 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: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -382,10 +391,10 @@ 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: buildMessenger(), + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); @@ -399,17 +408,17 @@ describe('RatesController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ - messenger: buildMessenger(), + const { ratesController } = setupRatesController({ + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); expect( deriveStateFromMetadata( - controller.state, - controller.metadata, - 'anonymous', + ratesController.state, + ratesController.metadata, + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -434,16 +443,16 @@ describe('RatesController', () => { it('includes expected state in state logs', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ - messenger: buildMessenger(), + const { ratesController } = setupRatesController({ + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'includeInStateLogs', ), ).toMatchInlineSnapshot(` @@ -459,16 +468,16 @@ describe('RatesController', () => { it('persists expected state', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ - messenger: buildMessenger(), + const { ratesController } = setupRatesController({ + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'persist', ), ).toMatchInlineSnapshot(` @@ -494,16 +503,16 @@ describe('RatesController', () => { it('exposes expected state to UI', () => { const fetchExchangeRateStub = jest.fn().mockResolvedValue({}); - const controller = setupRatesController({ - messenger: buildMessenger(), + const { ratesController } = setupRatesController({ + messenger: buildRootMessenger(), fetchMultiExchangeRate: fetchExchangeRateStub, includeUsdRate: false, }); expect( deriveStateFromMetadata( - controller.state, - controller.metadata, + ratesController.state, + ratesController.metadata, 'usedInUi', ), ).toMatchInlineSnapshot(` 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 >; /** diff --git a/packages/assets-controllers/src/TokenBalancesController.test.ts b/packages/assets-controllers/src/TokenBalancesController.test.ts index 024173d7d1b..a7388c9cf99 100644 --- a/packages/assets-controllers/src/TokenBalancesController.test.ts +++ b/packages/assets-controllers/src/TokenBalancesController.test.ts @@ -1,6 +1,13 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; 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'; @@ -10,12 +17,9 @@ import { useFakeTimers } from 'sinon'; import * as multicall from './multicall'; import { RpcBalanceFetcher } from './rpc-service/rpc-balance-fetcher'; import type { - AllowedActions, - AllowedEvents, ChainIdHex, + TokenBalancesControllerMessenger, ChecksumAddress, - TokenBalancesControllerActions, - TokenBalancesControllerEvents, TokenBalancesControllerState, } from './TokenBalancesController'; import { @@ -28,6 +32,18 @@ import { advanceTime, flushPromises } 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'), @@ -53,14 +69,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', @@ -72,7 +96,7 @@ const setupController = ({ 'AccountTrackerController:updateNativeBalances', 'AccountTrackerController:updateStakedBalances', ], - allowedEvents: [ + events: [ 'NetworkController:stateChange', 'PreferencesController:stateChange', 'TokensController:stateChange', @@ -169,7 +193,7 @@ const setupController = ({ }), ); const controller = new TokenBalancesController({ - messenger: tokenBalancesMessenger, + messenger: tokenBalancesControllerMessenger, ...config, }); const updateSpy = jest.spyOn(controller, 'update' as never); @@ -178,6 +202,7 @@ const setupController = ({ controller, updateSpy, messenger, + tokenBalancesControllerMessenger, }; }; @@ -1544,7 +1569,7 @@ describe('TokenBalancesController', () => { const accountAddress = '0x1111111111111111111111111111111111111111'; const chainId = '0x1'; - const { controller, messenger } = setupController({ + const { controller, tokenBalancesControllerMessenger } = setupController({ config: { accountsApiChainIds: () => [], allowExternalServices: () => true, @@ -1563,7 +1588,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') @@ -4355,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[] ) => { @@ -5194,7 +5223,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 2363246f258..ca0a2e1efd0 100644 --- a/packages/assets-controllers/src/TokenBalancesController.ts +++ b/packages/assets-controllers/src/TokenBalancesController.ts @@ -6,8 +6,8 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; + StateMetadata, +} from '@metamask/base-controller/next'; import { BNToHex, isValidHexAddress, @@ -20,6 +20,7 @@ import type { AccountActivityServiceStatusChangedEvent, } from '@metamask/core-backend'; import type { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, @@ -68,11 +69,11 @@ const CONTROLLER = 'TokenBalancesController' as const; const DEFAULT_INTERVAL_MS = 30_000; // 30 seconds const DEFAULT_WEBSOCKET_ACTIVE_POLLING_INTERVAL_MS = 300_000; // 5 minutes -const metadata = { +const metadata: StateMetadata = { tokenBalances: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -139,12 +140,10 @@ export type AllowedEvents = | AccountActivityServiceBalanceUpdatedEvent | AccountActivityServiceStatusChangedEvent; -export type TokenBalancesControllerMessenger = RestrictedMessenger< +export type TokenBalancesControllerMessenger = Messenger< typeof CONTROLLER, TokenBalancesControllerActions | AllowedActions, - TokenBalancesControllerEvents | AllowedEvents, - AllowedActions['type'], - AllowedEvents['type'] + TokenBalancesControllerEvents | AllowedEvents >; export type ChainPollingConfig = { @@ -326,12 +325,12 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ // initial token state & subscriptions const { allTokens, allDetectedTokens, allIgnoredTokens } = - this.messagingSystem.call('TokensController:getState'); + this.messenger.call('TokensController:getState'); this.#allTokens = allTokens; this.#detectedTokens = allDetectedTokens; this.#allIgnoredTokens = allIgnoredTokens; - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TokensController:stateChange', (tokensState: TokensControllerState) => { this.#onTokensChanged(tokensState).catch((error) => { @@ -339,34 +338,34 @@ 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, ); // 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), ); // 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), ); @@ -382,12 +381,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, ); @@ -395,12 +394,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, ); @@ -636,12 +635,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]; @@ -755,7 +752,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ ); // Get current AccountTracker state to compare existing balances - const accountTrackerState = this.messagingSystem.call( + const accountTrackerState = this.messenger.call( 'AccountTrackerController:getState', ); @@ -777,7 +774,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ }); if (balanceUpdates.length > 0) { - this.messagingSystem.call( + this.messenger.call( 'AccountTrackerController:updateNativeBalances', balanceUpdates, ); @@ -818,7 +815,7 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ }); if (stakedBalanceUpdates.length > 0) { - this.messagingSystem.call( + this.messenger.call( 'AccountTrackerController:updateStakedBalances', stakedBalanceUpdates, ); @@ -1148,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, ); @@ -1156,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, @@ -1265,10 +1262,10 @@ export class TokenBalancesController extends StaticIntervalPollingController<{ } // Unregister action handlers - this.messagingSystem.unregisterActionHandler( + this.messenger.unregisterActionHandler( `TokenBalancesController:updateChainPollingConfigs`, ); - this.messagingSystem.unregisterActionHandler( + this.messenger.unregisterActionHandler( `TokenBalancesController:getChainPollingConfig`, ); diff --git a/packages/assets-controllers/src/TokenDetectionController.test.ts b/packages/assets-controllers/src/TokenDetectionController.test.ts index 6e9c16ecd27..b94d1da4ffa 100644 --- a/packages/assets-controllers/src/TokenDetectionController.test.ts +++ b/packages/assets-controllers/src/TokenDetectionController.test.ts @@ -1,4 +1,3 @@ -import { Messenger } from '@metamask/base-controller'; import { ChainId, NetworkType, @@ -7,6 +6,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, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import { getDefaultNetworkControllerState, RpcEndpointType, @@ -36,11 +42,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, @@ -148,20 +150,48 @@ const mockNetworkConfigurations: Record = { }, }; -type MainMessenger = Messenger; +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', @@ -175,7 +205,7 @@ function buildTokenDetectionControllerMessenger( 'TokensController:addTokens', 'NetworkController:findNetworkClientIdByChainId', ], - allowedEvents: [ + events: [ 'AccountsController:selectedEvmAccountChange', 'KeyringController:lock', 'KeyringController:unlock', @@ -185,6 +215,7 @@ function buildTokenDetectionControllerMessenger( 'TransactionController:transactionConfirmed', ], }); + return tokenDetectionControllerMessenger; } const mockMultiChainAccountsService = () => { @@ -3918,7 +3949,7 @@ type WithControllerCallback = ({ triggerTransactionConfirmed, }: { controller: TokenDetectionController; - messenger: MainMessenger; + messenger: RootMessenger; mockGetAccount: (internalAccount: InternalAccount) => void; mockGetSelectedAccount: (address: string) => void; mockKeyringGetState: (state: KeyringControllerState) => void; @@ -3974,7 +4005,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( @@ -4075,12 +4106,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, diff --git a/packages/assets-controllers/src/TokenDetectionController.ts b/packages/assets-controllers/src/TokenDetectionController.ts index 5b93deaf696..af1a39ef05b 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, @@ -22,6 +21,7 @@ import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkClientId, NetworkControllerFindNetworkClientIdByChainIdAction, @@ -158,12 +158,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} */ @@ -275,7 +273,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', async ({ tokensChainsCache }) => { const isEqualValues = this.#compareTokensChainsCache( @@ -385,7 +382,7 @@ export class TokenDetectionController extends StaticIntervalPollingController { const selectedAccount = this.#getSelectedAccount(); @@ -402,10 +399,10 @@ export class TokenDetectionController extends StaticIntervalPollingController { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); @@ -422,7 +419,7 @@ export class TokenDetectionController extends StaticIntervalPollingController { await this.detectTokens({ @@ -521,10 +518,10 @@ export class TokenDetectionController extends StaticIntervalPollingController, - 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', () => { @@ -1276,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 c11e4d1692c..3b40ae7b6df 100644 --- a/packages/assets-controllers/src/TokenListController.ts +++ b/packages/assets-controllers/src/TokenListController.ts @@ -1,9 +1,10 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; + StateMetadata, +} from '@metamask/base-controller/next'; import { safelyExecute } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerStateChangeEvent, NetworkState, @@ -68,25 +69,23 @@ 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 = { +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, }, }; @@ -173,7 +172,7 @@ export class TokenListController extends StaticIntervalPollingController; + +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', () => { @@ -2689,7 +2710,7 @@ describe('TokenRatesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -2793,7 +2814,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 37ae246919f..f5d1d382d4f 100644 --- a/packages/assets-controllers/src/TokenRatesController.ts +++ b/packages/assets-controllers/src/TokenRatesController.ts @@ -6,13 +6,14 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; + StateMetadata, +} 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 +158,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 >; /** @@ -202,11 +201,11 @@ async function getCurrencyConversionRate({ } } -const tokenRatesControllerMetadata = { +const tokenRatesControllerMetadata: StateMetadata = { marketData: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -298,7 +297,7 @@ export class TokenRatesController extends StaticIntervalPollingController { - const { networkConfigurationsByChainId } = this.messagingSystem.call( + const { networkConfigurationsByChainId } = this.messenger.call( 'NetworkController:getState', ); diff --git a/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts b/packages/assets-controllers/src/TokenSearchDiscoveryDataController/TokenSearchDiscoveryDataController.test.ts index 2148acd60c2..86d1b7e01e2 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'; @@ -9,8 +16,6 @@ import { TokenSearchDiscoveryDataController, controllerName, MAX_TOKEN_DISPLAY_DATA_LENGTH, - type AllowedActions, - type AllowedEvents, type TokenSearchDiscoveryDataControllerMessenger, type TokenSearchDiscoveryDataControllerState, } from './TokenSearchDiscoveryDataController'; @@ -32,7 +37,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 +120,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 +223,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 +918,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', ); diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 5bfd1fedd6a..b84f5a901b5 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -1,10 +1,10 @@ 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 { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import contractMaps from '@metamask/contract-metadata'; import { ApprovalType, @@ -14,6 +14,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 +34,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 +44,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 +56,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); @@ -3477,7 +3483,7 @@ describe('TokensController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -3545,7 +3551,7 @@ type WithControllerCallback = ({ changeNetwork: (networkControllerState: { selectedNetworkClientId: NetworkClientId; }) => void; - messenger: UnrestrictedMessenger; + messenger: RootMessenger; approvalController: ApprovalController; triggerSelectedAccountChange: (internalAccount: InternalAccount) => void; triggerAccountRemoved: (accountAddress: string) => void; @@ -3603,12 +3609,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, @@ -3616,16 +3628,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', @@ -3663,7 +3684,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 571e28188de..8a45509a468 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -8,11 +8,11 @@ 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'; + StateMetadata, +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import contractsMap from '@metamask/contract-metadata'; import { toChecksumHexAddress, @@ -27,6 +27,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, @@ -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, }, }; @@ -158,12 +159,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 => { @@ -224,32 +223,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; @@ -419,7 +418,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; @@ -505,7 +504,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; @@ -581,7 +580,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; @@ -739,7 +738,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; @@ -798,7 +797,7 @@ export class TokensController extends BaseController< #getProvider(networkClientId?: NetworkClientId): Web3Provider { return new Web3Provider( networkClientId - ? this.messagingSystem.call( + ? this.messenger.call( 'NetworkController:getNetworkClientById', networkClientId, ).provider @@ -1082,7 +1081,7 @@ export class TokensController extends BaseController< } async #requestApproval(suggestedAssetMeta: SuggestedAssetMeta) { - return this.messagingSystem.call( + return this.messenger.call( 'ApprovalController:addRequest', { id: suggestedAssetMeta.id, @@ -1104,12 +1103,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, ); diff --git a/packages/assets-controllers/tsconfig.build.json b/packages/assets-controllers/tsconfig.build.json index 629b833e22a..5ef0e52c3b8 100644 --- a/packages/assets-controllers/tsconfig.build.json +++ b/packages/assets-controllers/tsconfig.build.json @@ -14,6 +14,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 ae60fdfc0d7..a537b98ca39 100644 --- a/packages/assets-controllers/tsconfig.json +++ b/packages/assets-controllers/tsconfig.json @@ -13,6 +13,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/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/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index ab805b8824b..850e3aee983 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -7,8 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `BridgeControllerGetStateAction` and `BridgeControllerStateChangeEvent` types ([#6444](https://github.com/MetaMask/core/pull/6444)) + ### 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`. - Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) - Bump `@metamask/gas-fee-controller` from `^24.1.0` to `^24.1.1` ([#6940](https://github.com/MetaMask/core/pull/6940)) - Bump `@metamask/multichain-network-controller` from `^1.0.1` to `^1.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) diff --git a/packages/bridge-controller/package.json b/packages/bridge-controller/package.json index 0219a82da3a..2f643cee151 100644 --- a/packages/bridge-controller/package.json +++ b/packages/bridge-controller/package.json @@ -56,6 +56,7 @@ "@metamask/controller-utils": "^11.14.1", "@metamask/gas-fee-controller": "^24.1.1", "@metamask/keyring-api": "^21.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/metamask-eth-abis": "^3.1.1", "@metamask/multichain-network-controller": "^1.0.2", "@metamask/polling-controller": "^14.0.2", diff --git a/packages/bridge-controller/src/bridge-controller.test.ts b/packages/bridge-controller/src/bridge-controller.test.ts index ce285d7f2bc..5dbf94634d1 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 { BtcScope, EthAccountType, @@ -2829,7 +2829,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 17a75203389..db6d7ef455a 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 type { InternalAccount } from '@metamask/keyring-internal-api'; import { abiERC20 } from '@metamask/metamask-eth-abis'; @@ -83,55 +83,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, }, }; @@ -239,31 +239,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), ); @@ -345,7 +345,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] @@ -368,7 +368,7 @@ 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, }; }; @@ -444,7 +444,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 @@ -548,9 +548,7 @@ export class BridgeController extends StaticIntervalPollingController { const quotesWithFees = await appendFeesToQuotes( [quote], - this.messagingSystem, + this.messenger, this.#getLayer1GasFee, selectedAccount, ); @@ -741,10 +739,7 @@ export class BridgeController extends StaticIntervalPollingController { state.minimumBalanceForRentExemptionInLamports = minimumBalanceForRentExemptionInLamports; @@ -758,7 +753,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 @@ -350,10 +362,7 @@ export type BridgeControllerActions = | BridgeControllerAction | BridgeControllerAction; -export type BridgeControllerEvents = ControllerStateChangeEvent< - typeof BRIDGE_CONTROLLER_NAME, - BridgeControllerState ->; +export type BridgeControllerEvents = BridgeControllerStateChangeEvent; export type AllowedActions = | AccountsControllerGetAccountByAddressAction @@ -370,10 +379,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 >; diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index e7a10bb5ebd..b1f3875defa 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -34,6 +34,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`. - 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/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index f5aaba1655e..2c54dc8c210 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,8 @@ /* eslint-disable jest/no-conditional-in-test */ /* eslint-disable jest/no-restricted-matchers */ -import type { AccountsControllerActions } from '@metamask/accounts-controller'; -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { - BridgeControllerActions, - BridgeControllerEvents, + BridgeControllerMessenger, TxData, } from '@metamask/bridge-controller'; import { @@ -17,13 +15,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'; @@ -36,11 +39,7 @@ import { DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE, MAX_ATTEMPTS, } from './constants'; -import type { - BridgeStatusControllerActions, - BridgeStatusControllerEvents, - StatusResponse, -} from './types'; +import type { StatusResponse } from './types'; import { type BridgeId, type StartPollingForBridgeTxStatusArgsSerialized, @@ -54,6 +53,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', })); @@ -3452,19 +3467,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; @@ -3474,37 +3486,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({ @@ -3937,7 +3950,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 7aadba4c2a6..0a9e1934a7f 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, }, }; @@ -167,27 +167,27 @@ 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), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${BRIDGE_STATUS_CONTROLLER_NAME}:getBridgeHistoryItemByTxMetaId`, this.getBridgeHistoryItemByTxMetaId.bind(this), ); @@ -195,7 +195,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { const { type, status, id } = transactionMeta; @@ -227,7 +227,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { const { type, id, chainId } = transactionMeta; @@ -283,10 +283,10 @@ 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', @@ -870,7 +867,7 @@ export class BridgeStatusController extends StaticIntervalPollingController => { const actionId = generateActionId().toString(); - const selectedAccount = this.messagingSystem.call( + const selectedAccount = this.messenger.call( 'AccountsController:getAccountByAddress', trade.from, ); @@ -880,7 +877,7 @@ export class BridgeStatusController extends StaticIntervalPollingController, ) => { const resetApproval = await getUSDTAllowanceResetTx( - this.messagingSystem, + this.messenger, quoteResponse, ); if (resetApproval) { @@ -937,7 +934,7 @@ export class BridgeStatusController extends StaticIntervalPollingController { - const { gasFeeEstimates } = this.messagingSystem.call( + const { gasFeeEstimates } = this.messenger.call( 'GasFeeController:getState', ); const { estimates: txGasFeeEstimates } = await this.#estimateGasFeeFn({ @@ -973,11 +970,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, }); @@ -999,7 +996,7 @@ export class BridgeStatusController extends StaticIntervalPollingController, isStxEnabledOnClient: boolean, ): Promise> => { - this.messagingSystem.call('BridgeController:stopPollingForQuotes'); + this.messenger.call('BridgeController:stopPollingForQuotes'); const selectedAccount = this.#getMultichainSelectedAccount(accountAddress); if (!selectedAccount) { @@ -1125,7 +1122,7 @@ export class BridgeStatusController extends StaticIntervalPollingController id === txMetaId); @@ -1288,7 +1285,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 456c1270e01..6ae84e2edf5 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1651,7 +1651,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, @@ -1674,7 +1674,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({}), @@ -1697,7 +1697,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, @@ -1720,7 +1720,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({}), @@ -1737,7 +1737,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({}), @@ -1754,7 +1754,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({}), @@ -1827,7 +1827,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1871,7 +1871,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1905,7 +1905,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1943,7 +1943,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -1985,7 +1985,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }; findAndUpdateTransactionsInBatch({ - messagingSystem: mockMessagingSystem, + messenger: mockMessagingSystem, batchId, txDataByType, updateTransactionFn: mockUpdateTransactionFn, @@ -2015,7 +2015,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 e2e5815221a..a204ba84a2b 100644 --- a/packages/bridge-status-controller/src/utils/transaction.ts +++ b/packages/bridge-status-controller/src/utils/transaction.ts @@ -290,7 +290,7 @@ export const toBatchTxParams = ( }; export const getAddTransactionBatchParams = async ({ - messagingSystem, + messenger, isBridgeTx, approval, resetApproval, @@ -307,7 +307,7 @@ export const getAddTransactionBatchParams = async ({ requireApproval = false, estimateGasFeeFn, }: { - messagingSystem: BridgeStatusControllerMessenger; + messenger: BridgeStatusControllerMessenger; isBridgeTx: boolean; trade: TxData; quoteResponse: Omit & @@ -318,7 +318,7 @@ export const getAddTransactionBatchParams = async ({ requireApproval?: boolean; }) => { const isGasless = gasIncluded || gasIncluded7702; - const selectedAccount = messagingSystem.call( + const selectedAccount = messenger.call( 'AccountsController:getAccountByAddress', trade.from, ); @@ -328,7 +328,7 @@ export const getAddTransactionBatchParams = async ({ ); } const hexChainId = formatChainIdToHex(trade.chainId); - const networkClientId = messagingSystem.call( + const networkClientId = messenger.call( 'NetworkController:findNetworkClientIdByChainId', hexChainId, ); @@ -340,7 +340,7 @@ export const getAddTransactionBatchParams = async ({ if (resetApproval) { const gasFees = await calculateGasFees( disable7702, - messagingSystem, + messenger, estimateGasFeeFn, resetApproval, networkClientId, @@ -357,7 +357,7 @@ export const getAddTransactionBatchParams = async ({ if (approval) { const gasFees = await calculateGasFees( disable7702, - messagingSystem, + messenger, estimateGasFeeFn, approval, networkClientId, @@ -373,7 +373,7 @@ export const getAddTransactionBatchParams = async ({ } const gasFees = await calculateGasFees( disable7702, - messagingSystem, + messenger, estimateGasFeeFn, trade, networkClientId, @@ -404,19 +404,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; diff --git a/packages/composable-controller/CHANGELOG.md b/packages/composable-controller/CHANGELOG.md index e3dcce00204..88a3c1bb63b 100644 --- a/packages/composable-controller/CHANGELOG.md +++ b/packages/composable-controller/CHANGELOG.md @@ -11,6 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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)) + - Also update generic parameter names `ControllerName` and `ControllerState` to `ChildControllerName`, `ChildControllerState` for reduced ambiguity. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [11.1.0] diff --git a/packages/composable-controller/package.json b/packages/composable-controller/package.json index 9053cfd589a..db8d2edc2ce 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.4.2" + "@metamask/base-controller": "^8.4.2", + "@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 ca4b15d8c84..d9e81c01e2a 100644 --- a/packages/composable-controller/src/ComposableController.test.ts +++ b/packages/composable-controller/src/ComposableController.test.ts @@ -1,15 +1,24 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import { BaseController, - Messenger, + type ControllerStateChangeEvent, + type ControllerGetStateAction, + type StateConstraint, deriveStateFromMetadata, -} from '@metamask/base-controller'; +} from '@metamask/base-controller/next'; import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MessengerActions, + type MessengerEvents, + type MockAnyNamespace, +} from '@metamask/messenger'; import type { Patch } from 'immer'; import * as sinon from 'sinon'; import type { ChildControllerStateChangeEvents, + ComposableControllerActions, ComposableControllerEvents, } from './ComposableController'; import { @@ -19,26 +28,37 @@ 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 = { foo: { persist: true, - anonymous: true, + includeInDebugSnapshot: true, + usedInUi: false, + includeInStateLogs: false, }, }; @@ -66,23 +86,28 @@ 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 = { quz: { persist: true, - anonymous: true, + includeInDebugSnapshot: true, + usedInUi: false, + includeInStateLogs: false, }, }; @@ -107,50 +132,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', () => { @@ -161,39 +153,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 fooMessenger: FooMessenger = new Messenger({ + namespace: 'FooController', + parent: messenger, + }); + messenger.delegate({ + messenger: fooMessenger, + events: ['QuzController:stateChange'], }); - const quzMessenger = messenger.getRestricted({ - name: 'QuzController', - allowedActions: [], - allowedEvents: [], + 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, @@ -216,20 +208,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, + }); + messenger.delegate({ + messenger: composableControllerMessenger, + events: ['FooController:stateChange'], }); new ComposableController< ComposableControllerState, @@ -242,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); @@ -260,26 +267,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, @@ -307,18 +335,80 @@ 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(); - 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( @@ -339,19 +429,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( () => @@ -374,97 +479,41 @@ 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(); - }); - describe('metadata', () => { it('includes expected state in debug snapshots', () => { type ComposableControllerState = { 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, @@ -480,7 +529,7 @@ describe('ComposableController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -496,24 +545,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, @@ -539,24 +599,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, @@ -588,24 +659,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, diff --git a/packages/composable-controller/src/ComposableController.ts b/packages/composable-controller/src/ComposableController.ts index e46fa4870a0..03bdb317341 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. @@ -51,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; /** @@ -75,13 +92,11 @@ export type AllowedEvents< */ export type ComposableControllerMessenger< ComposableControllerState extends ComposableControllerStateConstraint, -> = RestrictedMessenger< +> = Messenger< typeof controllerName, - never, + ComposableControllerActions, | ComposableControllerEvents - | AllowedEvents, - never, - AllowedEvents['type'] + | AllowedEvents >; /** @@ -106,7 +121,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, @@ -128,7 +143,7 @@ export class ComposableController< (metadata as StateMetadataConstraint)[name] = { includeInStateLogs: false, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }; return metadata; @@ -163,24 +178,22 @@ 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`, - (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 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" } diff --git a/packages/core-backend/CHANGELOG.md b/packages/core-backend/CHANGELOG.md index 93e788924bc..9185af8d614 100644 --- a/packages/core-backend/CHANGELOG.md +++ b/packages/core-backend/CHANGELOG.md @@ -9,8 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - Bump `@metamask/profile-sync-controller` from `^25.1.1` to `^25.1.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) +### 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. + ## [3.0.0] ### Added diff --git a/packages/core-backend/package.json b/packages/core-backend/package.json index 9ee949273e0..a0759dfd0ec 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.2", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/profile-sync-controller": "^25.1.2", "@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 c60fe7bdeb9..00e83d7a9d1 100644 --- a/packages/core-backend/src/AccountActivityService.test.ts +++ b/packages/core-backend/src/AccountActivityService.test.ts @@ -1,17 +1,17 @@ -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 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'; @@ -19,6 +19,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(); @@ -48,25 +60,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; + subscribe: jest.Mock; + channelHasSubscription: jest.Mock; + getSubscriptionsByChannel: jest.Mock; + findSubscriptionsByChannelPrefix: jest.Mock; + forceReconnection: 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:forceReconnection', + '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(); @@ -215,10 +272,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 - should flush tracked chains as down - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.ERROR, @@ -667,7 +721,7 @@ describe('AccountActivityService', () => { mocks.getSelectedAccount.mockReturnValue(null); // Publish WebSocket ERROR state event without any tracked chains - await rootMessenger.publish( + rootMessenger.publish( 'BackendWebSocketService:connectionStateChanged', { state: WebSocketState.ERROR, @@ -718,7 +772,7 @@ describe('AccountActivityService', () => { }); // Publish account change event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', solanaAccount, ); @@ -748,7 +802,7 @@ describe('AccountActivityService', () => { }); // Publish account change event - will be picked up by controller subscription - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', unknownAccount, ); @@ -770,7 +824,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, @@ -810,7 +864,7 @@ describe('AccountActivityService', () => { }); // Publish account change event on root messenger - await rootMessenger.publish( + rootMessenger.publish( 'AccountsController:selectedAccountChange', newAccount, ); @@ -848,7 +902,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 30c8b95d98c..e434a2d2c86 100644 --- a/packages/core-backend/src/AccountActivityService.ts +++ b/packages/core-backend/src/AccountActivityService.ts @@ -9,9 +9,9 @@ import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent, } from '@metamask/accounts-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { TraceCallback } from '@metamask/controller-utils'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types'; import type { @@ -96,7 +96,7 @@ export const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [ 'BackendWebSocketService:connectionStateChanged', ] as const; -export type AccountActivityServiceAllowedActions = +export type AllowedActions = | AccountsControllerGetSelectedAccountAction | BackendWebSocketServiceMethodActions; @@ -134,16 +134,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 987d2add4b5..55e34688c40 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,10 +12,6 @@ import { WebSocketState, type BackendWebSocketServiceOptions, type BackendWebSocketServiceMessenger, - type BackendWebSocketServiceActions, - type BackendWebSocketServiceAllowedActions, - type BackendWebSocketServiceEvents, - type BackendWebSocketServiceAllowedEvents, } from './BackendWebSocketService'; import { flushPromises } from '../../../tests/helpers'; @@ -20,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 // ===================================================== @@ -163,6 +177,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 @@ -171,19 +196,25 @@ class MockWebSocket extends EventTarget { */ const getMessenger = () => { // Create a unique root messenger for each test - const rootMessenger = new Messenger< - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents - >(); - const messenger = rootMessenger.getRestricted({ - name: 'BackendWebSocketService', - allowedActions: ['AuthenticationController:getBearerToken'], - allowedEvents: [ + const rootMessenger = getRootMessenger(); + const messenger = new Messenger< + 'BackendWebSocketService', + AllBackendWebSocketServiceActions, + AllBackendWebSocketServiceEvents, + RootMessenger + >({ + namespace: 'BackendWebSocketService', + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: ['AuthenticationController:getBearerToken'], + events: [ 'AuthenticationController:stateChange', 'KeyringController:lock', 'KeyringController:unlock', ], - }) as unknown as BackendWebSocketServiceMessenger; + messenger, + }); // Create mock action handlers const mockGetBearerToken = jest.fn().mockResolvedValue('valid-default-token'); @@ -252,10 +283,7 @@ type TestSetupOptions = { type TestSetup = { service: BackendWebSocketService; messenger: BackendWebSocketServiceMessenger; - rootMessenger: Messenger< - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getBearerToken: jest.Mock; }; @@ -270,10 +298,7 @@ type TestSetup = { type WithServiceCallback = (payload: { service: BackendWebSocketService; messenger: BackendWebSocketServiceMessenger; - rootMessenger: Messenger< - BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions, - BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents - >; + rootMessenger: RootMessenger; mocks: { getBearerToken: jest.Mock; }; diff --git a/packages/core-backend/src/BackendWebSocketService.ts b/packages/core-backend/src/BackendWebSocketService.ts index 3bbfd48a0bd..a8c9066dc25 100644 --- a/packages/core-backend/src/BackendWebSocketService.ts +++ b/packages/core-backend/src/BackendWebSocketService.ts @@ -1,10 +1,10 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { TraceCallback } from '@metamask/controller-utils'; import { ExponentialBackoff } from '@metamask/controller-utils'; import type { KeyringControllerLockEvent, KeyringControllerUnlockEvent, } from '@metamask/keyring-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'; @@ -228,29 +228,27 @@ export type WebSocketConnectionInfo = { export type BackendWebSocketServiceActions = BackendWebSocketServiceMethodActions; -export type BackendWebSocketServiceAllowedActions = +type AllowedActions = AuthenticationController.AuthenticationControllerGetBearerToken; -export type BackendWebSocketServiceAllowedEvents = - | AuthenticationController.AuthenticationControllerStateChangeEvent - | KeyringControllerLockEvent - | KeyringControllerUnlockEvent; - // Event types for WebSocket connection state changes export type BackendWebSocketServiceConnectionStateChangedEvent = { type: 'BackendWebSocketService:connectionStateChanged'; payload: [WebSocketConnectionInfo]; }; +type AllowedEvents = + | AuthenticationController.AuthenticationControllerStateChangeEvent + | KeyringControllerLockEvent + | KeyringControllerUnlockEvent; + 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 4f4a385017b..e386c7a5ecc 100644 --- a/packages/core-backend/tsconfig.build.json +++ b/packages/core-backend/tsconfig.build.json @@ -7,9 +7,9 @@ }, "references": [ { "path": "../accounts-controller/tsconfig.build.json" }, - { "path": "../base-controller/tsconfig.build.json" }, { "path": "../controller-utils/tsconfig.build.json" }, { "path": "../keyring-controller/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 6d40debda24..b44a91d630c 100644 --- a/packages/core-backend/tsconfig.json +++ b/packages/core-backend/tsconfig.json @@ -9,15 +9,15 @@ { "path": "../accounts-controller" }, - { - "path": "../base-controller" - }, { "path": "../controller-utils" }, { "path": "../keyring-controller" }, + { + "path": "../messenger" + }, { "path": "../profile-sync-controller" } diff --git a/packages/delegation-controller/CHANGELOG.md b/packages/delegation-controller/CHANGELOG.md index 6445443bc83..dc23bb8ecf4 100644 --- a/packages/delegation-controller/CHANGELOG.md +++ b/packages/delegation-controller/CHANGELOG.md @@ -11,6 +11,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.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [0.8.0] diff --git a/packages/delegation-controller/package.json b/packages/delegation-controller/package.json index 06959ea7408..5ff8d39e1ad 100644 --- a/packages/delegation-controller/package.json +++ b/packages/delegation-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.2", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1" }, "devDependencies": { diff --git a/packages/delegation-controller/src/DelegationController.test.ts b/packages/delegation-controller/src/DelegationController.test.ts index ba7ee483b84..6c701639968 100644 --- a/packages/delegation-controller/src/DelegationController.test.ts +++ b/packages/delegation-controller/src/DelegationController.test.ts @@ -1,9 +1,12 @@ -import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller'; -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +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 +14,7 @@ import { controllerName, DelegationController } from './DelegationController'; import type { Address, Delegation, - DelegationControllerEvents, + DelegationControllerMessenger, DelegationControllerState, DelegationEntry, DeleGatorEnvironment, @@ -19,6 +22,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 +75,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 +97,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, }; } @@ -679,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 28978762cc7..95a149ad3c7 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'; @@ -23,7 +23,7 @@ const delegationControllerMetadata = { delegations: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, } satisfies StateMetadata; @@ -115,7 +115,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, @@ -154,7 +154,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/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"] } diff --git a/packages/earn-controller/CHANGELOG.md b/packages/earn-controller/CHANGELOG.md index 6f89deb2b7b..40bdd77f17a 100644 --- a/packages/earn-controller/CHANGELOG.md +++ b/packages/earn-controller/CHANGELOG.md @@ -11,6 +11,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.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)) diff --git a/packages/earn-controller/package.json b/packages/earn-controller/package.json index c2c8ce54645..6591bd96788 100644 --- a/packages/earn-controller/package.json +++ b/packages/earn-controller/package.json @@ -52,6 +52,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@metamask/keyring-api": "^21.0.0", + "@metamask/messenger": "^0.3.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 1920646043e..85996af48f6 100644 --- a/packages/earn-controller/src/EarnController.test.ts +++ b/packages/earn-controller/src/EarnController.test.ts @@ -1,7 +1,14 @@ /* eslint-disable jest/no-conditional-in-test */ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { toHex } 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 { getDefaultNetworkControllerState } from '@metamask/network-controller'; import { EarnSdk, @@ -17,10 +24,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 +32,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 +94,45 @@ jest.mock('@metamask/stake-sdk', () => ({ })); /** - * Builds a new instance of the Messenger class for the EarnController. + * Builds a new instance of the root messenger. * - * @returns A new instance of the Messenger class for the EarnController. + * @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', 'AccountTreeController:getAccountsFromSelectedAccountGroup', ], - allowedEvents: [ + events: [ 'NetworkController:networkDidChange', 'AccountTreeController:selectedAccountGroupChange', 'TransactionController:transactionConfirmed', ], }); + return earnControllerMessenger; } const mockAccount1Address = '0x1234'; @@ -1461,10 +1480,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(); @@ -2575,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 90bf5387fe0..1a3c367b620 100644 --- a/packages/earn-controller/src/EarnController.ts +++ b/packages/earn-controller/src/EarnController.ts @@ -6,13 +6,13 @@ 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 { isEvmAccountType } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerNetworkDidChangeEvent, @@ -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, }, }; @@ -273,12 +273,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 === @@ -344,7 +342,7 @@ export class EarnController extends BaseController< this.refreshLendingData().catch(console.error); // Listen for network changes - this.messagingSystem.subscribe( + this.messenger.subscribe( 'NetworkController:networkDidChange', (networkControllerState: NetworkState) => { this.#selectedNetworkClientId = @@ -365,7 +363,7 @@ export class EarnController extends BaseController< ); // Listen for account group changes - this.messagingSystem.subscribe( + this.messenger.subscribe( 'AccountTreeController:selectedAccountGroupChange', () => { const address = this.#getSelectedEvmAccountAddress(); @@ -379,7 +377,7 @@ export class EarnController extends BaseController< ); // Listen for confirmed staking transactions - this.messagingSystem.subscribe( + this.messenger.subscribe( 'TransactionController:transactionConfirmed', (transactionMeta: TransactionMeta) => { /** @@ -419,7 +417,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, ); @@ -460,7 +458,7 @@ export class EarnController extends BaseController< * @returns The EVM account or undefined if no EVM account is found. */ #getSelectedEvmAccount(): InternalAccount | undefined { - return this.messagingSystem + return this.messenger .call('AccountTreeController:getAccountsFromSelectedAccountGroup') .find((account: InternalAccount) => isEvmAccountType(account.type)); } 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" } ] } diff --git a/packages/ens-controller/CHANGELOG.md b/packages/ens-controller/CHANGELOG.md index 9aa504b40b5..5f2f6ae6a7d 100644 --- a/packages/ens-controller/CHANGELOG.md +++ b/packages/ens-controller/CHANGELOG.md @@ -22,6 +22,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.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)) diff --git a/packages/ens-controller/package.json b/packages/ens-controller/package.json index 546134164d5..2548199c45e 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.4.2", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "punycode": "^2.1.1" }, diff --git a/packages/ens-controller/src/EnsController.test.ts b/packages/ens-controller/src/EnsController.test.ts index 546fbbc0195..52bd47d6875 100644 --- a/packages/ens-controller/src/EnsController.test.ts +++ b/packages/ens-controller/src/EnsController.test.ts @@ -1,20 +1,23 @@ import * as providersModule from '@ethersproject/providers'; -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; 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 +26,6 @@ import { EnsController, DEFAULT_ENS_NETWORK_MAP } from './EnsController'; import type { EnsControllerState, EnsControllerMessenger, - AllowedActions, } from './EnsController'; const defaultState: EnsControllerState = { @@ -54,9 +56,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 +83,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 +119,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 +150,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 +159,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 +172,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 +181,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 +195,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 +216,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 +237,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 +251,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 +266,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 +281,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 +296,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 +311,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 +338,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 +352,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 +366,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 +376,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 +386,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 +401,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 +413,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 +427,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 +438,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 +453,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 +464,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 +480,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 +502,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 +519,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 +528,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 +548,7 @@ describe('EnsController', () => { chainId: '0x9999999', }), }); - const ensControllerMessenger = getRestrictedMessenger( + const ensControllerMessenger = getEnsControllerMessenger( rootMessenger, getNetworkClientById, ); @@ -553,7 +566,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 +590,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 +609,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 +628,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 +650,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 +672,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 +696,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); @@ -708,7 +721,7 @@ describe('EnsController', () => { describe('metadata', () => { it('includes expected state in debug snapshots', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -717,14 +730,14 @@ describe('EnsController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); it('includes expected state in state logs', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -788,7 +801,7 @@ describe('EnsController', () => { it('persists expected state', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); @@ -852,7 +865,7 @@ describe('EnsController', () => { it('exposes expected state to UI', () => { const rootMessenger = getRootMessenger(); - const ensControllerMessenger = getRestrictedMessenger(rootMessenger); + const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ messenger: ensControllerMessenger, }); diff --git a/packages/ens-controller/src/EnsController.ts b/packages/ens-controller/src/EnsController.ts index 14023726482..f7659ba3343 100644 --- a/packages/ens-controller/src/EnsController.ts +++ b/packages/ens-controller/src/EnsController.ts @@ -1,6 +1,10 @@ import { Web3Provider } from '@ethersproject/providers'; -import type { RestrictedMessenger } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type StateMetadata, + type ControllerGetStateAction, + type ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import type { ChainId } from '@metamask/controller-utils'; import { normalizeEnsName, @@ -11,6 +15,7 @@ import { convertHexToDecimal, toHex, } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, @@ -69,29 +74,37 @@ 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 = { +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, }, }; @@ -288,7 +301,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); @@ -301,7 +314,7 @@ export class EnsController extends BaseController< const { configuration: { chainId: currentChainId }, provider, - } = this.messagingSystem.call( + } = this.messenger.call( 'NetworkController:getNetworkClientById', selectedNetworkClientId, ); 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..c6a3a4c830a 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"] } diff --git a/packages/error-reporting-service/CHANGELOG.md b/packages/error-reporting-service/CHANGELOG.md index 0a3d1c2500c..086a1df4d7b 100644 --- a/packages/error-reporting-service/CHANGELOG.md +++ b/packages/error-reporting-service/CHANGELOG.md @@ -29,6 +29,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.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)) ## [2.0.0] diff --git a/packages/error-reporting-service/package.json b/packages/error-reporting-service/package.json index b712bdb4951..bc9db4a21c6 100644 --- a/packages/error-reporting-service/package.json +++ b/packages/error-reporting-service/package.json @@ -47,7 +47,8 @@ "since-latest-release": "../../scripts/since-latest-release.sh" }, "dependencies": { - "@metamask/base-controller": "^8.4.2" + "@metamask/base-controller": "^8.4.2", + "@metamask/messenger": "^0.3.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 c3633d098e4..a1cbf903f91 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/packages/gas-fee-controller/CHANGELOG.md b/packages/gas-fee-controller/CHANGELOG.md index d5853050c10..f220860fbab 100644 --- a/packages/gas-fee-controller/CHANGELOG.md +++ b/packages/gas-fee-controller/CHANGELOG.md @@ -20,9 +20,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` ([#6473](https://github.com/MetaMask/core/pull/6473)) +- Export `GasFeeMessenger` type ([#6386](https://github.com/MetaMask/core/pull/6386), [#6444](https://github.com/MetaMask/core/pull/6444)) ### 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/gas-fee-controller/src/GasFeeController.test.ts b/packages/gas-fee-controller/src/GasFeeController.test.ts index 4153f3829b2..9e736d00018 100644 --- a/packages/gas-fee-controller/src/GasFeeController.test.ts +++ b/packages/gas-fee-controller/src/GasFeeController.test.ts @@ -1,16 +1,20 @@ -import { deriveStateFromMetadata, Messenger } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; 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 +34,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 +51,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 +127,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 +302,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, @@ -1277,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 6a50f0cfbdf..9daf3a70858 100644 --- a/packages/gas-fee-controller/src/GasFeeController.ts +++ b/packages/gas-fee-controller/src/GasFeeController.ts @@ -1,14 +1,15 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; + StateMetadata, +} 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, @@ -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, }, }; @@ -262,12 +263,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 = { @@ -398,14 +397,14 @@ export class GasFeeController extends StaticIntervalPollingController { describe('constructor', () => { it('creates GatorPermissionsController with default state', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect(controller.state.isGatorPermissionsEnabled).toBe(false); @@ -113,7 +116,7 @@ describe('GatorPermissionsController', () => { }; const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), state: customState, }); @@ -128,7 +131,7 @@ describe('GatorPermissionsController', () => { it('creates GatorPermissionsController with default config', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect(controller.permissionsProviderSnapId).toBe( @@ -140,7 +143,7 @@ describe('GatorPermissionsController', () => { it('isFetchingGatorPermissions is false on initialization', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), state: { isFetchingGatorPermissions: true, }, @@ -153,7 +156,7 @@ describe('GatorPermissionsController', () => { describe('disableGatorPermissions', () => { it('disables gator permissions successfully', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.enableGatorPermissions(); @@ -177,7 +180,7 @@ describe('GatorPermissionsController', () => { describe('fetchAndUpdateGatorPermissions', () => { it('fetches and updates gator permissions successfully', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.enableGatorPermissions(); @@ -224,7 +227,7 @@ describe('GatorPermissionsController', () => { it('throws error when gator permissions are not enabled', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.disableGatorPermissions(); @@ -240,7 +243,7 @@ describe('GatorPermissionsController', () => { }); const controller = new GatorPermissionsController({ - messenger: getMessenger(rootMessenger), + messenger: getGatorPermissionsControllerMessenger(rootMessenger), }); await controller.enableGatorPermissions(); @@ -262,7 +265,7 @@ describe('GatorPermissionsController', () => { }); const controller = new GatorPermissionsController({ - messenger: getMessenger(rootMessenger), + messenger: getGatorPermissionsControllerMessenger(rootMessenger), }); await controller.enableGatorPermissions(); @@ -286,7 +289,7 @@ describe('GatorPermissionsController', () => { }); const controller = new GatorPermissionsController({ - messenger: getMessenger(rootMessenger), + messenger: getGatorPermissionsControllerMessenger(rootMessenger), }); await controller.enableGatorPermissions(); @@ -302,7 +305,7 @@ describe('GatorPermissionsController', () => { describe('gatorPermissionsMap getter tests', () => { it('returns parsed gator permissions map', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); const { gatorPermissionsMap } = controller; @@ -340,7 +343,7 @@ describe('GatorPermissionsController', () => { }; const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), state: { gatorPermissionsMapSerialized: JSON.stringify(mockState), }, @@ -354,7 +357,7 @@ describe('GatorPermissionsController', () => { describe('message handlers tests', () => { it('registers all message handlers', () => { - const messenger = getMessenger(); + const messenger = getGatorPermissionsControllerMessenger(); const mockRegisterActionHandler = jest.spyOn( messenger, 'registerActionHandler', @@ -382,7 +385,7 @@ describe('GatorPermissionsController', () => { describe('enableGatorPermissions', () => { it('enables gator permissions successfully', async () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); await controller.enableGatorPermissions(); @@ -394,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( @@ -429,7 +432,7 @@ describe('GatorPermissionsController', () => { it('persists expected state', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect( @@ -448,7 +451,7 @@ describe('GatorPermissionsController', () => { it('exposes expected state to UI', () => { const controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); expect( @@ -484,7 +487,7 @@ describe('GatorPermissionsController', () => { beforeEach(() => { controller = new GatorPermissionsController({ - messenger: getMessenger(), + messenger: getGatorPermissionsControllerMessenger(), }); }); @@ -693,15 +696,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. @@ -725,8 +736,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', @@ -740,18 +753,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 aeaa9886681..90122bea62d 100644 --- a/packages/gator-permissions-controller/src/GatorPermissionsController.ts +++ b/packages/gator-permissions-controller/src/GatorPermissionsController.ts @@ -1,12 +1,12 @@ import type { Signer } from '@metamask/7715-permission-types'; 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 { DELEGATOR_CONTRACTS } from '@metamask/delegation-deployments'; +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'; @@ -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 @@ -222,12 +223,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 >; /** @@ -279,22 +278,22 @@ 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), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:decodePermissionFromPermissionContextForOrigin`, this.decodePermissionFromPermissionContextForOrigin.bind(this), ); @@ -326,7 +325,7 @@ export default class GatorPermissionsController extends BaseController< StoredGatorPermission[] | null > { try { - const response = (await this.messagingSystem.call( + const response = (await this.messenger.call( 'SnapController:handleRequest', { snapId, 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"] } diff --git a/packages/keyring-controller/CHANGELOG.md b/packages/keyring-controller/CHANGELOG.md index 2a1d613a60f..6363520aa6d 100644 --- a/packages/keyring-controller/CHANGELOG.md +++ b/packages/keyring-controller/CHANGELOG.md @@ -16,6 +16,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.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [23.1.1] diff --git a/packages/keyring-controller/package.json b/packages/keyring-controller/package.json index ba795718035..ad2564d12f7 100644 --- a/packages/keyring-controller/package.json +++ b/packages/keyring-controller/package.json @@ -55,6 +55,7 @@ "@metamask/eth-simple-keyring": "^11.0.0", "@metamask/keyring-api": "^21.0.0", "@metamask/keyring-internal-api": "^9.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "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 eab73e98fc2..681f2c4f714 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 { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { HdKeyring } from '@metamask/eth-hd-keyring'; import { normalize, @@ -14,6 +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 { + 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'; @@ -44,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'), @@ -4272,7 +4289,7 @@ describe('KeyringController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -4356,7 +4373,7 @@ type WithControllerCallback = ({ controller: KeyringController; encryptor: MockEncryptor; initialState: KeyringControllerState; - messenger: KeyringControllerMessenger; + messenger: RootMessenger; }) => Promise | ReturnValue; type WithControllerOptions = Partial & { @@ -4387,27 +4404,28 @@ function stubKeyringClassWithAccount( } /** - * Build a messenger that includes all events used by the keyring - * controller. + * Build a root messenger. * - * @returns The messenger. + * @returns The root messenger. */ -function buildMessenger() { - return new Messenger(); +function buildRootMessenger(): RootMessenger { + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** - * Build a restricted messenger for the keyring controller. + * Build a messenger for the keyring controller. * - * @param messenger - A messenger. + * @param messenger - An optional root messenger to use as the base for the + * controller messenger * @returns The keyring controller restricted messenger. */ -function buildKeyringControllerMessenger(messenger = buildMessenger()) { - return messenger.getRestricted({ - name: 'KeyringController', - allowedActions: [], - allowedEvents: [], - }); +function buildKeyringControllerMessenger(messenger = buildRootMessenger()) { + return new Messenger< + 'KeyringController', + KeyringControllerActions, + KeyringControllerEvents, + typeof messenger + >({ namespace: 'KeyringController', parent: messenger }); } /** @@ -4425,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) { diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index 5e4cf83d7ef..e9fffe8215c 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, @@ -246,12 +246,10 @@ export type KeyringControllerEvents = | KeyringControllerUnlockEvent | KeyringControllerAccountRemovedEvent; -export type KeyringControllerMessenger = RestrictedMessenger< +export type KeyringControllerMessenger = Messenger< typeof name, KeyringControllerActions, - KeyringControllerEvents, - never, - never + KeyringControllerEvents >; export type KeyringControllerOptions = { @@ -694,31 +692,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, }, }, @@ -1191,7 +1189,7 @@ export class KeyringController extends BaseController< } }); - this.messagingSystem.publish(`${name}:accountRemoved`, address); + this.messenger.publish(`${name}:accountRemoved`, address); } /** @@ -1213,7 +1211,7 @@ export class KeyringController extends BaseController< delete state.encryptionSalt; }); - this.messagingSystem.publish(`${name}:lock`); + this.messenger.publish(`${name}:lock`); }); } @@ -1709,96 +1707,96 @@ export class KeyringController extends BaseController< } /** - * Constructor helper for registering this controller's messaging system + * Constructor helper for registering this controller's messeger * 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), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:addNewKeyring`, this.addNewKeyring.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:createNewVaultAndKeychain`, this.createNewVaultAndKeychain.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${name}:createNewVaultAndRestore`, this.createNewVaultAndRestore.bind(this), ); @@ -2472,7 +2470,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/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"] diff --git a/packages/logging-controller/CHANGELOG.md b/packages/logging-controller/CHANGELOG.md index 09284b5cdae..ef69be5a6b3 100644 --- a/packages/logging-controller/CHANGELOG.md +++ b/packages/logging-controller/CHANGELOG.md @@ -11,6 +11,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.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [6.1.0] diff --git a/packages/logging-controller/package.json b/packages/logging-controller/package.json index 0082a0da55b..d9ecc972786 100644 --- a/packages/logging-controller/package.json +++ b/packages/logging-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", + "@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 7c7d89023ab..7fbd5421fd9 100644 --- a/packages/logging-controller/src/LoggingController.test.ts +++ b/packages/logging-controller/src/LoggingController.test.ts @@ -1,7 +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 * 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 +22,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 +89,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 +123,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 +132,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 +143,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 +172,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, @@ -186,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, }); @@ -196,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, }); @@ -222,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, }); @@ -242,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 538b201a74b..61e3fc6478a 100644 --- a/packages/logging-controller/src/LoggingController.ts +++ b/packages/logging-controller/src/LoggingController.ts @@ -1,9 +1,10 @@ import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; + StateMetadata, +} 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,19 +55,17 @@ 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 = { +const metadata: StateMetadata = { logs: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, }; @@ -107,9 +106,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/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/packages/message-manager/CHANGELOG.md b/packages/message-manager/CHANGELOG.md index cc20eb08900..905d548c4b5 100644 --- a/packages/message-manager/CHANGELOG.md +++ b/packages/message-manager/CHANGELOG.md @@ -11,6 +11,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`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [13.0.1] diff --git a/packages/message-manager/package.json b/packages/message-manager/package.json index f226d748bb2..2ab46266c63 100644 --- a/packages/message-manager/package.json +++ b/packages/message-manager/package.json @@ -50,6 +50,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@metamask/eth-sig-util": "^8.2.0", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "@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 196f42a1df1..0b0560e9073 100644 --- a/packages/message-manager/src/AbstractMessageManager.test.ts +++ b/packages/message-manager/src/AbstractMessageManager.test.ts @@ -1,16 +1,19 @@ import { deriveStateFromMetadata, - type RestrictedMessenger, -} from '@metamask/base-controller'; + 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; @@ -24,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, @@ -71,13 +84,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'>; const MOCK_INITIAL_OPTIONS = { additionalFinishStatuses: undefined, @@ -579,7 +586,7 @@ describe('AbstractTestManager', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/message-manager/src/AbstractMessageManager.ts b/packages/message-manager/src/AbstractMessageManager.ts index 14c20d85edd..4496834556c 100644 --- a/packages/message-manager/src/AbstractMessageManager.ts +++ b/packages/message-manager/src/AbstractMessageManager.ts @@ -1,10 +1,14 @@ -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type ControllerStateChangeEvent, + type ControllerGetStateAction, +} from '@metamask/base-controller/next'; +import type { ApprovalType } from '@metamask/controller-utils'; import type { - ActionConstraint, + Messenger, EventConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; -import type { ApprovalType } from '@metamask/controller-utils'; + ActionConstraint, +} 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 @@ -16,13 +20,13 @@ const stateMetadata = { unapprovedMessages: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, unapprovedMessagesCount: { includeInStateLogs: true, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -133,17 +137,17 @@ export type SecurityProviderRequest = ( 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: RestrictedMessenger< - Name, - Action, - Event | UpdateBadgeEvent, - string, - string - >; + messenger: MessageManagerMessenger; name: Name; securityProviderRequest?: SecurityProviderRequest; state?: MessageManagerState; @@ -157,18 +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, - RestrictedMessenger< - Name, - Action, - Event | UpdateBadgeEvent, - string, - string - > + MessageManagerMessenger > { protected messages: Message[]; @@ -184,7 +188,7 @@ export abstract class AbstractMessageManager< name, securityProviderRequest, state = {} as MessageManagerState, - }: AbstractMessageManagerOptions) { + }: AbstractMessageManagerOptions) { super({ messenger, metadata: stateMetadata, @@ -200,7 +204,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. @@ -257,7 +261,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..87176a021c8 100644 --- a/packages/message-manager/src/DecryptMessageManager.ts +++ b/packages/message-manager/src/DecryptMessageManager.ts @@ -1,9 +1,10 @@ import type { - ActionConstraint, - EventConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; + 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,14 +31,20 @@ export type DecryptMessageManagerUpdateBadgeEvent = { payload: []; }; -export type DecryptMessageManagerMessenger = RestrictedMessenger< +type DecryptMessageManagerActions = ControllerGetStateAction< typeof managerName, - ActionConstraint, - | EventConstraint + DecryptMessageManagerState +>; + +type DecryptMessageManagerEvents = + | ControllerStateChangeEvent | DecryptMessageManagerUnapprovedMessageAddedEvent - | DecryptMessageManagerUpdateBadgeEvent, - string, - string + | DecryptMessageManagerUpdateBadgeEvent; + +export type DecryptMessageManagerMessenger = Messenger< + typeof managerName, + DecryptMessageManagerActions, + DecryptMessageManagerEvents >; type DecryptMessageManagerOptions = { @@ -97,10 +104,7 @@ export class DecryptMessageManager extends AbstractMessageManager< DecryptMessage, DecryptMessageParams, DecryptMessageParamsMetamask, - ActionConstraint, - | EventConstraint - | DecryptMessageManagerUnapprovedMessageAddedEvent - | DecryptMessageManagerUpdateBadgeEvent + DecryptMessageManagerMessenger > { constructor({ additionalFinishStatuses, @@ -194,7 +198,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..56e7b9455fd 100644 --- a/packages/message-manager/src/EncryptionPublicKeyManager.ts +++ b/packages/message-manager/src/EncryptionPublicKeyManager.ts @@ -1,9 +1,9 @@ import type { - ActionConstraint, - EventConstraint, - RestrictedMessenger, -} from '@metamask/base-controller'; + ControllerGetStateAction, + ControllerStateChangeEvent, +} from '@metamask/base-controller/next'; import { ApprovalType } from '@metamask/controller-utils'; +import type { Messenger } from '@metamask/messenger'; import type { AbstractMessage, @@ -31,14 +31,23 @@ export type EncryptionPublicKeyManagerUpdateBadgeEvent = { payload: []; }; -export type EncryptionPublicKeyManagerMessenger = RestrictedMessenger< +type EncryptionPublicKeyManagerActions = ControllerGetStateAction< typeof managerName, - ActionConstraint, - | EventConstraint + EncryptionPublicKeyManagerState +>; + +type EncryptionPublicKeyManagerEvents = + | ControllerStateChangeEvent< + typeof managerName, + EncryptionPublicKeyManagerState + > | EncryptionPublicKeyManagerUnapprovedMessageAddedEvent - | EncryptionPublicKeyManagerUpdateBadgeEvent, - string, - string + | EncryptionPublicKeyManagerUpdateBadgeEvent; + +export type EncryptionPublicKeyManagerMessenger = Messenger< + typeof managerName, + EncryptionPublicKeyManagerActions, + EncryptionPublicKeyManagerEvents >; type EncryptionPublicKeyManagerOptions = { @@ -95,10 +104,7 @@ export class EncryptionPublicKeyManager extends AbstractMessageManager< EncryptionPublicKey, EncryptionPublicKeyParams, EncryptionPublicKeyParamsMetamask, - ActionConstraint, - | EventConstraint - | EncryptionPublicKeyManagerUnapprovedMessageAddedEvent - | EncryptionPublicKeyManagerUpdateBadgeEvent + EncryptionPublicKeyManagerMessenger > { constructor({ additionalFinishStatuses, @@ -185,7 +191,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/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/packages/multichain-account-service/CHANGELOG.md b/packages/multichain-account-service/CHANGELOG.md index ab33b872bc3..910b9925926 100644 --- a/packages/multichain-account-service/CHANGELOG.md +++ b/packages/multichain-account-service/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [1.6.1] diff --git a/packages/multichain-account-service/package.json b/packages/multichain-account-service/package.json index 485ab1d45cb..51c9295c4a2 100644 --- a/packages/multichain-account-service/package.json +++ b/packages/multichain-account-service/package.json @@ -55,6 +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.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/src/MultichainAccountGroup.test.ts b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts index ae915804aaa..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'; @@ -22,13 +21,8 @@ import { setupNamedAccountProvider, getMultichainAccountServiceMessenger, getRootMessenger, + type RootMessenger, } from './tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from './types'; function setup({ groupIndex = 0, @@ -44,10 +38,7 @@ function setup({ ], }: { groupIndex?: number; - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[][]; } = {}): { wallet: MultichainAccountWallet>; diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index 1cdac410036..424b5f49805 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 { mnemonicPhraseToBytes } from '@metamask/key-tree'; import type { KeyringAccount } from '@metamask/keyring-api'; import { EthAccountType, SolAccountType } from '@metamask/keyring-api'; @@ -37,14 +36,9 @@ import { makeMockAccountProvider, mockAsInternalAccount, setupNamedAccountProvider, + type RootMessenger, } from './tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, - MultichainAccountServiceMessenger, -} from './types'; +import type { MultichainAccountServiceMessenger } from './types'; // Mock providers. jest.mock('./providers/EvmAccountProvider', () => { @@ -93,25 +87,19 @@ function mockAccountProvider( } function setup({ - messenger = getRootMessenger(), + rootMessenger = getRootMessenger(), keyrings = [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2], accounts, providerConfigs, }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + rootMessenger?: RootMessenger; keyrings?: KeyringObject[]; accounts?: KeyringAccount[]; providerConfigs?: MultichainAccountServiceOptions['providerConfigs']; } = {}): { service: MultichainAccountService; - serviceMessenger: MultichainAccountServiceMessenger; - messenger: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + rootMessenger: RootMessenger; + messenger: MultichainAccountServiceMessenger; mocks: Mocks; } { const mocks: Mocks = { @@ -136,17 +124,17 @@ function setup({ keyrings: mocks.KeyringController.keyrings, })); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'KeyringController:getState', mocks.KeyringController.getState, ); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'KeyringController:getKeyringsByType', mocks.KeyringController.getKeyringsByType, ); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'KeyringController:addNewKeyring', mocks.KeyringController.addNewKeyring, ); @@ -156,7 +144,7 @@ function setup({ () => accounts, ); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:listMultichainAccounts', mocks.AccountsController.listMultichainAccounts, ); @@ -180,14 +168,21 @@ function setup({ ); } - const serviceMessenger = getMultichainAccountServiceMessenger(messenger); + const messenger = getMultichainAccountServiceMessenger(rootMessenger); + const service = new MultichainAccountService({ - messenger: serviceMessenger, + messenger, providerConfigs, }); + service.init(); - return { service, serviceMessenger, messenger, mocks }; + return { + service, + rootMessenger, + messenger, + mocks, + }; } describe('MultichainAccountService', () => { @@ -216,17 +211,17 @@ describe('MultichainAccountService', () => { }, }; - const { mocks, serviceMessenger } = setup({ + const { mocks, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_SOL_ACCOUNT_1], providerConfigs, }); expect(mocks.EvmAccountProvider.constructor).toHaveBeenCalledWith( - serviceMessenger, + messenger, providerConfigs[EvmAccountProvider.NAME], ); expect(mocks.SolAccountProvider.constructor).toHaveBeenCalledWith( - serviceMessenger, + messenger, providerConfigs[SolAccountProvider.NAME], ); }); @@ -249,17 +244,17 @@ describe('MultichainAccountService', () => { // No `EVM_ACCOUNT_PROVIDER_NAME`, cause it's optional in this test. }; - const { mocks, serviceMessenger } = setup({ + const { mocks, messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1, MOCK_SOL_ACCOUNT_1], providerConfigs, }); expect(mocks.EvmAccountProvider.constructor).toHaveBeenCalledWith( - serviceMessenger, + messenger, undefined, ); expect(mocks.SolAccountProvider.constructor).toHaveBeenCalledWith( - serviceMessenger, + messenger, providerConfigs[SolAccountProvider.NAME], ); }); @@ -474,7 +469,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, @@ -483,7 +478,7 @@ describe('MultichainAccountService', () => { // Now we're adding `account2`. mocks.EvmAccountProvider.accounts = [account1, account2]; - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(account2), ); @@ -514,7 +509,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, @@ -523,7 +518,7 @@ describe('MultichainAccountService', () => { // Now we're adding `account2`. mocks.EvmAccountProvider.accounts = [account1, otherAccount1]; - messenger.publish( + rootMessenger.publish( 'AccountsController:accountAdded', mockAsInternalAccount(otherAccount1), ); @@ -555,12 +550,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), ); @@ -574,7 +569,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], }); @@ -592,7 +587,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), ); @@ -616,7 +611,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, @@ -626,7 +624,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), ); @@ -638,7 +636,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, @@ -647,7 +645,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( @@ -681,7 +679,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({ @@ -735,7 +735,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({ @@ -926,7 +928,9 @@ describe('MultichainAccountService', () => { }); it('sets basic functionality with MultichainAccountService:setBasicFunctionality', async () => { - const { messenger } = setup({ accounts: [MOCK_HD_ACCOUNT_1] }); + const { messenger } = setup({ + accounts: [MOCK_HD_ACCOUNT_1], + }); // This tests the action handler registration expect( @@ -986,11 +990,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 @@ -1001,7 +1007,7 @@ describe('MultichainAccountService', () => { jest.spyOn(solProvider, 'isAccountCompatible'); wrapper = new AccountProviderWrapper( - getMultichainAccountServiceMessenger(messenger), + getMultichainAccountServiceMessenger(rootMessenger), solProvider, ); }); diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts index 09d1b0e9420..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, @@ -31,14 +30,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 +49,7 @@ function setup({ ], }: { entropySource?: EntropySourceId; - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; providers?: MockAccountProvider[]; accounts?: InternalAccount[][]; } = {}): { 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; diff --git a/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts b/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts index b05986d4c9b..251dfd7cb56 100644 --- a/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/EvmAccountProvider.test.ts @@ -1,6 +1,5 @@ /* eslint-disable jsdoc/require-jsdoc */ import { publicToAddress } from '@ethereumjs/util'; -import type { Messenger } from '@metamask/base-controller'; import { type KeyringMetadata } from '@metamask/keyring-controller'; import type { EthKeyring, @@ -22,13 +21,8 @@ import { MOCK_HD_ACCOUNT_2, MOCK_HD_KEYRING_1, MockAccountBuilder, + type RootMessenger, } from '../tests'; -import type { - AllowedActions, - AllowedEvents, - MultichainAccountServiceActions, - MultichainAccountServiceEvents, -} from '../types'; jest.mock('@ethereumjs/util', () => ({ publicToAddress: jest.fn(), @@ -127,20 +121,14 @@ function setup({ accounts = [], discovery, }: { - messenger?: Messenger< - MultichainAccountServiceActions | AllowedActions, - MultichainAccountServiceEvents | AllowedEvents - >; + messenger?: RootMessenger; accounts?: InternalAccount[]; discovery?: { transactionCount: string; }; } = {}): { 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 ccb380a4b74..d808d429b4b 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/providers/TrxAccountProvider.test.ts b/packages/multichain-account-service/src/providers/TrxAccountProvider.test.ts index d52aaa25f95..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 { @@ -17,13 +16,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 +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: MockTronKeyring; 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 0eba196ed77..c1843463bf1 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', @@ -48,5 +63,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 39372186d6a..a0c9f84879c 100644 --- a/packages/multichain-account-service/src/types.ts +++ b/packages/multichain-account-service/src/types.ts @@ -11,7 +11,6 @@ import type { AccountsControllerGetAccountByAddressAction, AccountsControllerListMultichainAccountsAction, } from '@metamask/accounts-controller'; -import type { RestrictedMessenger } from '@metamask/base-controller'; import type { KeyringAccount } from '@metamask/keyring-api'; import type { KeyringControllerAddNewKeyringAction, @@ -20,6 +19,7 @@ import type { KeyringControllerStateChangeEvent, KeyringControllerWithKeyringAction, } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { NetworkControllerFindNetworkClientIdByChainIdAction, NetworkControllerGetNetworkClientByIdAction, @@ -125,7 +125,7 @@ export type MultichainAccountServiceEvents = * All actions registered by other modules that {@link MultichainAccountService} * calls. */ -export type AllowedActions = +type AllowedActions = | AccountsControllerListMultichainAccountsAction | AccountsControllerGetAccountAction | AccountsControllerGetAccountByAddressAction @@ -141,7 +141,7 @@ export type AllowedActions = * All events published by other modules that {@link MultichainAccountService} * subscribes to. */ -export type AllowedEvents = +type AllowedEvents = | KeyringControllerStateChangeEvent | AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent; @@ -150,10 +150,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/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/packages/multichain-network-controller/CHANGELOG.md b/packages/multichain-network-controller/CHANGELOG.md index a2d14a13cec..3ca24fe549e 100644 --- a/packages/multichain-network-controller/CHANGELOG.md +++ b/packages/multichain-network-controller/CHANGELOG.md @@ -11,6 +11,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.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)) diff --git a/packages/multichain-network-controller/package.json b/packages/multichain-network-controller/package.json index ecac9715e1a..884e8125bb6 100644 --- a/packages/multichain-network-controller/package.json +++ b/packages/multichain-network-controller/package.json @@ -51,6 +51,7 @@ "@metamask/controller-utils": "^11.14.1", "@metamask/keyring-api": "^21.0.0", "@metamask/keyring-internal-api": "^9.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/superstruct": "^3.1.0", "@metamask/utils": "^11.8.1", "@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 513c2d95351..17576626908 100644 --- a/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.test.ts +++ b/packages/multichain-network-controller/src/MultichainNetworkController/MultichainNetworkController.test.ts @@ -1,4 +1,4 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { InfuraNetworkType } from '@metamask/controller-utils'; import type { AnyAccountType } from '@metamask/keyring-api'; import { @@ -12,6 +12,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 +33,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 +57,32 @@ function createMockNetworkService( }; } +const controllerName = 'MultichainNetworkController'; + +type AllMultichainNetworkControllerActions = + MessengerActions; + +type AllMultichainNetworkControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllMultichainNetworkControllerActions, + AllMultichainNetworkControllerEvents, + RootMessenger +>; + +/** + * 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 +130,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 +190,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 +210,7 @@ function setupController({ 'NetworkController:findNetworkClientIdByChainId', 'AccountsController:listMultichainAccounts', ], - allowedEvents: ['AccountsController:selectedAccountChange'], + events: ['AccountsController:selectedAccountChange'], }); const defaultNetworkService = createMockNetworkService(); @@ -224,6 +252,8 @@ function setupController({ messenger.publish('AccountsController:selectedAccountChange', mockAccount); }; + const publishSpy = jest.spyOn(controllerMessenger, 'publish'); + return { messenger, controller, @@ -662,7 +692,7 @@ describe('MultichainNetworkController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { 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 166be5560d8..2384a64a8ed 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, @@ -160,25 +160,25 @@ export const MULTICHAIN_NETWORK_CONTROLLER_METADATA = { multichainNetworkConfigurationsByChainId: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, selectedMultichainNetworkChainId: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, isEvmSelected: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, networksWithTransactionActivity: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, } satisfies StateMetadata; 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/packages/multichain-transactions-controller/CHANGELOG.md b/packages/multichain-transactions-controller/CHANGELOG.md index 86d8b268199..4720bca49e2 100644 --- a/packages/multichain-transactions-controller/CHANGELOG.md +++ b/packages/multichain-transactions-controller/CHANGELOG.md @@ -11,6 +11,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.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) - Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) diff --git a/packages/multichain-transactions-controller/package.json b/packages/multichain-transactions-controller/package.json index 238c020c094..d95ea029daf 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.2", "@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/packages/name-controller/CHANGELOG.md b/packages/name-controller/CHANGELOG.md index 08ecd450191..618739595c9 100644 --- a/packages/name-controller/CHANGELOG.md +++ b/packages/name-controller/CHANGELOG.md @@ -11,6 +11,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/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [8.1.0] diff --git a/packages/name-controller/package.json b/packages/name-controller/package.json index 4e50fa44f20..64effc8ad58 100644 --- a/packages/name-controller/package.json +++ b/packages/name-controller/package.json @@ -50,6 +50,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", + "@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/packages/network-controller/CHANGELOG.md b/packages/network-controller/CHANGELOG.md index 8fd053404e0..1106f819698 100644 --- a/packages/network-controller/CHANGELOG.md +++ b/packages/network-controller/CHANGELOG.md @@ -11,6 +11,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.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [24.3.0] diff --git a/packages/network-controller/package.json b/packages/network-controller/package.json index 62c03db1293..f3377903f3c 100644 --- a/packages/network-controller/package.json +++ b/packages/network-controller/package.json @@ -55,6 +55,7 @@ "@metamask/eth-json-rpc-provider": "^5.0.1", "@metamask/eth-query": "^4.0.0", "@metamask/json-rpc-engine": "^10.1.1", + "@metamask/messenger": "^0.3.0", "@metamask/rpc-errors": "^7.0.2", "@metamask/swappable-obj-proxy": "^2.3.0", "@metamask/utils": "^11.8.1", diff --git a/packages/network-controller/src/NetworkController.ts b/packages/network-controller/src/NetworkController.ts index 00ad77d0dea..5ddfc752c3e 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 >; /** @@ -1202,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, }, }, @@ -1235,7 +1233,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`, @@ -1244,80 +1242,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`, @@ -1541,15 +1539,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(); } @@ -1768,7 +1760,7 @@ export class NetworkController extends BaseController< const listener = () => { networkChanged = true; try { - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'NetworkController:networkDidChange', listener, ); @@ -1792,10 +1784,7 @@ export class NetworkController extends BaseController< } } }; - this.messagingSystem.subscribe( - 'NetworkController:networkDidChange', - listener, - ); + this.messenger.subscribe('NetworkController:networkDidChange', listener); const { isInfura, networkStatus, isEIP1559Compatible } = await this.#determineNetworkMetadata(this.state.selectedNetworkClientId); @@ -1807,7 +1796,7 @@ export class NetworkController extends BaseController< } try { - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'NetworkController:networkDidChange', listener, ); @@ -1829,15 +1818,15 @@ export class NetworkController extends BaseController< if (isInfura) { if (networkStatus === NetworkStatus.Available) { - this.messagingSystem.publish('NetworkController:infuraIsUnblocked'); + this.messenger.publish('NetworkController:infuraIsUnblocked'); } else if (networkStatus === 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'); } } @@ -2108,7 +2097,7 @@ export class NetworkController extends BaseController< }); }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:networkAdded`, newNetworkConfiguration, ); @@ -2445,7 +2434,7 @@ export class NetworkController extends BaseController< }); }); - this.messagingSystem.publish( + this.messenger.publish( 'NetworkController:networkRemoved', existingNetworkConfiguration, ); @@ -2849,7 +2838,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, logger: this.#log, }); @@ -2866,7 +2855,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, logger: this.#log, }); @@ -3029,7 +3018,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, logger: this.#log, }), @@ -3047,7 +3036,7 @@ export class NetworkController extends BaseController< }, getRpcServiceOptions: this.#getRpcServiceOptions, getBlockTrackerOptions: this.#getBlockTrackerOptions, - messenger: this.messagingSystem, + messenger: this.messenger, isRpcFailoverEnabled: this.#isRpcFailoverEnabled, logger: this.#log, }), 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 358fbb79e2d..4841f92b647 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, @@ -166,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: {}, }, @@ -187,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({ @@ -213,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({ @@ -246,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({ @@ -278,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({ @@ -310,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({ @@ -357,9 +357,9 @@ describe('NetworkController', () => { 'ErrorReportingService:captureException', jest.fn(), ); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); const controller = new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { selectedNetworkClientId: 'nonexistent', networkConfigurationsByChainId: { @@ -398,10 +398,10 @@ describe('NetworkController', () => { 'ErrorReportingService:captureException', captureExceptionMock, ); - const restrictedMessenger = buildNetworkControllerMessenger(messenger); + const controllerMessenger = buildNetworkControllerMessenger(messenger); new NetworkController({ - messenger: restrictedMessenger, + messenger: controllerMessenger, state: { selectedNetworkClientId: 'nonexistent', networkConfigurationsByChainId: { @@ -442,11 +442,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, @@ -2385,7 +2385,7 @@ describe('NetworkController', () => { }, infuraProjectId, }, - async ({ controller, messenger }) => { + async ({ controller, networkControllerMessenger }) => { const fakeProvider = buildFakeProvider([ // Called during provider initialization { @@ -2409,7 +2409,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 @@ -2896,7 +2896,7 @@ describe('NetworkController', () => { }, infuraProjectId, }, - async ({ controller, messenger }) => { + async ({ controller, networkControllerMessenger }) => { const fakeProvider = buildFakeProvider([ // Called during provider initialization { @@ -2920,7 +2920,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 @@ -14507,7 +14507,7 @@ describe('NetworkController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); diff --git a/packages/network-controller/tests/helpers.ts b/packages/network-controller/tests/helpers.ts index 77d6100549b..bff0d7f5010 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,13 @@ import { NetworksTicker, toHex, } 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'; @@ -13,10 +19,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 +44,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 + MockAnyNamespace, + AllNetworkControllerActions, + AllNetworkControllerEvents >; /** @@ -76,23 +85,32 @@ export const TESTNET = { * @returns The messenger. */ export function buildRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** - * 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; } /** diff --git a/packages/network-controller/tsconfig.build.json b/packages/network-controller/tsconfig.build.json index 0cbfa18f6af..3aa1aa62e0f 100644 --- a/packages/network-controller/tsconfig.build.json +++ b/packages/network-controller/tsconfig.build.json @@ -12,7 +12,8 @@ { "path": "../eth-json-rpc-middleware/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 7a59daff571..9f1911d6466 100644 --- a/packages/network-controller/tsconfig.json +++ b/packages/network-controller/tsconfig.json @@ -11,7 +11,8 @@ { "path": "../eth-json-rpc-middleware" }, { "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/network-enablement-controller/CHANGELOG.md b/packages/network-enablement-controller/CHANGELOG.md index fed634eceea..1edbd230d42 100644 --- a/packages/network-enablement-controller/CHANGELOG.md +++ b/packages/network-enablement-controller/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - 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)) diff --git a/packages/network-enablement-controller/package.json b/packages/network-enablement-controller/package.json index 9efdb47db6a..8dc0f474d40 100644 --- a/packages/network-enablement-controller/package.json +++ b/packages/network-enablement-controller/package.json @@ -64,6 +64,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@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..cf885764a05 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,71 @@ 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; + messenger: NetworkEnablementControllerMessenger; +} => { + const rootMessenger = getRootMessenger(); + + const networkEnablementControllerMessenger = new Messenger< + typeof controllerName, + AllNetworkEnablementControllerActions, + AllNetworkEnablementControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: rootMessenger, + }); + + rootMessenger.delegate({ + messenger: networkEnablementControllerMessenger, + actions: [ + 'NetworkController:getState', + 'MultichainNetworkController:getState', + ], + events: [ + 'NetworkController:networkAdded', + 'NetworkController:networkRemoved', + 'TransactionController:transactionSubmitted', + ], + }); - messenger.registerActionHandler( + rootMessenger.registerActionHandler( 'NetworkController:getState', jest.fn().mockImplementation(() => ({ networkConfigurationsByChainId: { @@ -78,20 +114,11 @@ const setupController = ({ return { controller, - messenger, + rootMessenger, + messenger: networkEnablementControllerMessenger, }; }; -// 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 +160,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 +210,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 +256,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 +283,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 +300,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 +316,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 +328,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, @@ -352,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['messagingSystem'], '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 @@ -532,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['messagingSystem'], '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 @@ -587,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 @@ -596,8 +621,7 @@ describe('NetworkEnablementController', () => { }); jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: unknown[]): any => { const responses = { @@ -644,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['messagingSystem'], '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 @@ -696,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['messagingSystem'], '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 @@ -750,12 +772,11 @@ describe('NetworkEnablementController', () => { describe('enableAllPopularNetworks', () => { it('enables all popular networks that exist in controller configurations and Solana mainnet', () => { - const { controller } = setupInitializedController(); + const { controller, messenger } = setupController(); // Mock the network configurations jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], '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 @@ -937,12 +958,11 @@ describe('NetworkEnablementController', () => { }); it('disables existing networks and enables only popular networks (exclusive behavior)', async () => { - const { controller, messenger } = setupInitializedController(); + const { controller, rootMessenger, messenger } = setupController(); // Mock the network configurations to include popular networks jest - // eslint-disable-next-line dot-notation - .spyOn(controller['messagingSystem'], '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 @@ -981,7 +1001,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, @@ -1021,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['messagingSystem'], 'call') + .spyOn(messenger, 'call') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((actionType: string, ..._args: unknown[]): any => { const responses = { @@ -1071,7 +1090,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 +1150,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 +1330,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 +1380,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 +1421,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 +1475,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 +1484,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 +1496,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 +1510,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 +1545,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 +1560,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 +1582,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 +1612,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 +1629,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 +1833,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 +2067,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 +2135,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 +2157,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 +2180,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 +2202,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 +2217,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 +2238,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 +2259,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 +2290,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 +2303,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 +2336,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 +2387,7 @@ describe('NetworkEnablementController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { @@ -2512,7 +2531,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 +2543,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 +2568,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 +2576,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 +2601,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 +2609,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 +2634,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 +2643,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/packages/notification-services-controller/CHANGELOG.md b/packages/notification-services-controller/CHANGELOG.md index f24956f3d89..56507eb043f 100644 --- a/packages/notification-services-controller/CHANGELOG.md +++ b/packages/notification-services-controller/CHANGELOG.md @@ -11,8 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) +### Removed + +- **BREAKING:** Remove package-level exports of `AllowedActions` and `AllowedEvents` from `NotificationServicesController` and `NotificationServicesPushController` ([#6538](https://github.com/MetaMask/core/pull/6538)) + ## [18.3.0] ### Added diff --git a/packages/notification-services-controller/package.json b/packages/notification-services-controller/package.json index e48ad171464..722d2f8a70d 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.2", "@metamask/controller-utils": "^11.14.1", + "@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/packages/permission-controller/CHANGELOG.md b/packages/permission-controller/CHANGELOG.md index 64c757bd669..9d65a67a833 100644 --- a/packages/permission-controller/CHANGELOG.md +++ b/packages/permission-controller/CHANGELOG.md @@ -11,6 +11,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/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [11.1.0] diff --git a/packages/permission-controller/package.json b/packages/permission-controller/package.json index daede4f3adc..b6d248e3b8c 100644 --- a/packages/permission-controller/package.json +++ b/packages/permission-controller/package.json @@ -50,6 +50,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@metamask/json-rpc-engine": "^10.1.1", + "@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/packages/permission-log-controller/CHANGELOG.md b/packages/permission-log-controller/CHANGELOG.md index e5bf1340ab3..dfb3ce40b14 100644 --- a/packages/permission-log-controller/CHANGELOG.md +++ b/packages/permission-log-controller/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- 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`. + ## [4.1.1] ### Changed diff --git a/packages/permission-log-controller/package.json b/packages/permission-log-controller/package.json index 26c71d4de3e..c4af8a5091d 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.2", "@metamask/json-rpc-engine": "^10.1.1", + "@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/packages/phishing-controller/CHANGELOG.md b/packages/phishing-controller/CHANGELOG.md index d621b21ee5e..a28693b8087 100644 --- a/packages/phishing-controller/CHANGELOG.md +++ b/packages/phishing-controller/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) - Bump `@metamask/transaction-controller` from `^60.7.0` to `^60.8.0` ([#6883](https://github.com/MetaMask/core/pull/6883)) diff --git a/packages/phishing-controller/package.json b/packages/phishing-controller/package.json index b6960033184..785af5e2722 100644 --- a/packages/phishing-controller/package.json +++ b/packages/phishing-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", + "@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/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, }); } diff --git a/packages/phishing-controller/src/PhishingController.test.ts b/packages/phishing-controller/src/PhishingController.test.ts index e2a2ff23d6f..52dce0de2b8 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,59 @@ import { getHostnameFromUrl } from './utils'; const controllerName = 'PhishingController'; +type AllPhishingControllerActions = + MessengerActions; + +type AllPhishingControllerEvents = MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllPhishingControllerActions, + AllPhishingControllerEvents, + RootMessenger +>; + /** - * 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 +100,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 +448,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 +3537,7 @@ describe('URL Scan Cache', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -3561,18 +3603,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 1ecd3844860..3631a893f50 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/packages/polling-controller/CHANGELOG.md b/packages/polling-controller/CHANGELOG.md index 8d6f8b906d6..84d426fccab 100644 --- a/packages/polling-controller/CHANGELOG.md +++ b/packages/polling-controller/CHANGELOG.md @@ -11,6 +11,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` 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.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)) diff --git a/packages/polling-controller/src/BlockTrackerPollingController.test.ts b/packages/polling-controller/src/BlockTrackerPollingController.test.ts index d5c8615a3ff..88321e80b97 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, + type 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, 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: {}, diff --git a/packages/polling-controller/src/StaticIntervalPollingController.ts b/packages/polling-controller/src/StaticIntervalPollingController.ts index 5076dfcffdf..e71f3e44417 100644 --- a/packages/polling-controller/src/StaticIntervalPollingController.ts +++ b/packages/polling-controller/src/StaticIntervalPollingController.ts @@ -1,4 +1,4 @@ -import { BaseController } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller/next'; import type { Json } from '@metamask/utils'; import { 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"] } diff --git a/packages/preferences-controller/CHANGELOG.md b/packages/preferences-controller/CHANGELOG.md index 4e7d848c81c..1e791875dbe 100644 --- a/packages/preferences-controller/CHANGELOG.md +++ b/packages/preferences-controller/CHANGELOG.md @@ -21,6 +21,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 diff --git a/packages/preferences-controller/package.json b/packages/preferences-controller/package.json index 8bfa0ac2a3e..6e2393df437 100644 --- a/packages/preferences-controller/package.json +++ b/packages/preferences-controller/package.json @@ -48,7 +48,8 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.2", - "@metamask/controller-utils": "^11.14.1" + "@metamask/controller-utils": "^11.14.1", + "@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 0103f5d81db..5c6fc4249ab 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 { @@ -839,22 +844,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, + }); } /** @@ -867,22 +877,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 7b7fba907a3..7a42d479844 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/packages/profile-sync-controller/CHANGELOG.md b/packages/profile-sync-controller/CHANGELOG.md index 83596fcb1c4..28ec8a5b8cd 100644 --- a/packages/profile-sync-controller/CHANGELOG.md +++ b/packages/profile-sync-controller/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [25.1.1] diff --git a/packages/profile-sync-controller/package.json b/packages/profile-sync-controller/package.json index 5f858a3de14..9bea4eb4b0e 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.2", + "@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.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 { 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/packages/rate-limit-controller/CHANGELOG.md b/packages/rate-limit-controller/CHANGELOG.md index eb01b8821cd..0132ee54f2b 100644 --- a/packages/rate-limit-controller/CHANGELOG.md +++ b/packages/rate-limit-controller/CHANGELOG.md @@ -11,6 +11,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/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [6.1.0] diff --git a/packages/rate-limit-controller/package.json b/packages/rate-limit-controller/package.json index ce39a239cc4..b7f3e4aa107 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.2", + "@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/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/remote-feature-flag-controller/CHANGELOG.md b/packages/remote-feature-flag-controller/CHANGELOG.md index d22bb94fef3..f9a73a6f092 100644 --- a/packages/remote-feature-flag-controller/CHANGELOG.md +++ b/packages/remote-feature-flag-controller/CHANGELOG.md @@ -11,6 +11,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/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [1.9.0] diff --git a/packages/remote-feature-flag-controller/package.json b/packages/remote-feature-flag-controller/package.json index 78dae8f5aec..cda703d0aca 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.2", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/utils": "^11.8.1", "uuid": "^8.3.2" }, diff --git a/packages/remote-feature-flag-controller/src/index.ts b/packages/remote-feature-flag-controller/src/index.ts index 420a92b9c68..9906d3bc04d 100644 --- a/packages/remote-feature-flag-controller/src/index.ts +++ b/packages/remote-feature-flag-controller/src/index.ts @@ -1,5 +1,6 @@ export { RemoteFeatureFlagController } from './remote-feature-flag-controller'; export type { + RemoteFeatureFlagControllerState, RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerGetStateAction, @@ -13,9 +14,6 @@ export { EnvironmentType, } from './remote-feature-flag-controller-types'; -export type { - RemoteFeatureFlagControllerState, - FeatureFlags, -} from './remote-feature-flag-controller-types'; +export type { FeatureFlags } from './remote-feature-flag-controller-types'; export { ClientConfigApiService } from './client-config-api-service/client-config-api-service'; export { generateDeterministicRandomNumber } from './utils/user-segmentation-utils'; 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 1b7d5343b8d..7d3c0833ea9 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 @@ -46,17 +46,3 @@ export type ServiceResponse = { remoteFeatureFlags: FeatureFlags; cacheTimestamp: number | null; }; - -/** - * Describes the shape of the state object for the {@link RemoteFeatureFlagController}. - */ -export type RemoteFeatureFlagControllerState = { - /** - * The collection of feature flags and their respective values, which can be objects. - */ - remoteFeatureFlags: FeatureFlags; - /** - * The timestamp of the last successful feature flag cache. - */ - cacheTimestamp: number; -}; 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..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 @@ -1,20 +1,26 @@ -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 { - RemoteFeatureFlagControllerActions, RemoteFeatureFlagControllerMessenger, RemoteFeatureFlagControllerState, - RemoteFeatureFlagControllerStateChangeEvent, } from './remote-feature-flag-controller'; import type { FeatureFlags } from './remote-feature-flag-controller-types'; +const controllerName = 'RemoteFeatureFlagController'; + const MOCK_FLAGS: FeatureFlags = { feature1: true, feature2: { chrome: '<109' }, @@ -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 d12797ce148..ce846a35927 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,9 +1,9 @@ -import { BaseController } from '@metamask/base-controller'; -import type { - ControllerGetStateAction, - ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; +import { + BaseController, + type ControllerGetStateAction, + 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 { @@ -18,7 +18,7 @@ import { // === GENERAL === -export const controllerName = 'RemoteFeatureFlagController'; +const controllerName = 'RemoteFeatureFlagController'; export const DEFAULT_CACHE_DURATION = 24 * 60 * 60 * 1000; // 1 day // === STATE === @@ -32,13 +32,13 @@ const remoteFeatureFlagControllerMetadata = { remoteFeatureFlags: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, cacheTimestamp: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, }; @@ -63,8 +63,6 @@ export type RemoteFeatureFlagControllerActions = | RemoteFeatureFlagControllerGetStateAction | RemoteFeatureFlagControllerUpdateRemoteFeatureFlagsAction; -export type AllowedActions = never; - export type RemoteFeatureFlagControllerStateChangeEvent = ControllerStateChangeEvent< typeof controllerName, @@ -74,14 +72,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/packages/sample-controllers/CHANGELOG.md b/packages/sample-controllers/CHANGELOG.md index 546c70743a0..9eac7604735 100644 --- a/packages/sample-controllers/CHANGELOG.md +++ b/packages/sample-controllers/CHANGELOG.md @@ -11,6 +11,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/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)) diff --git a/packages/sample-controllers/package.json b/packages/sample-controllers/package.json index c72a9978e13..bf1c0e193e2 100644 --- a/packages/sample-controllers/package.json +++ b/packages/sample-controllers/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.2", + "@metamask/messenger": "^0.3.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..b61edf5c6c4 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,15 @@ -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, + 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 +304,7 @@ describe('SampleGasPricesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -362,8 +365,9 @@ describe('SampleGasPricesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + MockAnyNamespace, + MessengerActions, + MessengerEvents >; /** @@ -389,7 +393,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** @@ -402,14 +406,19 @@ function getRootMessenger(): RootMessenger { function getMessenger( rootMessenger: RootMessenger, ): SampleGasPricesControllerMessenger { - return rootMessenger.getRestricted({ - name: 'SampleGasPricesController', - allowedActions: [ - 'SampleGasPricesService:fetchGasPrices', + const messenger: SampleGasPricesControllerMessenger = new Messenger({ + namespace: 'SampleGasPricesController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: [ 'NetworkController:getNetworkClientById', + 'SampleGasPricesService:fetchGasPrices', ], - allowedEvents: ['NetworkController:stateChange'], + events: ['NetworkController:stateChange'], + messenger, }); + return messenger; } /** 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..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 @@ -1,15 +1,17 @@ -import { Messenger } from '@metamask/base-controller'; import { HttpError } from '@metamask/controller-utils'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, + 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 +295,11 @@ describe('SampleGasPricesService', () => { * required by the service under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + MockAnyNamespace, + MessengerActions, + // TODO: Disable this lint rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments + MessengerEvents >; /** @@ -304,7 +309,7 @@ type RootMessenger = Messenger< * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** @@ -317,10 +322,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..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 @@ -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,12 @@ 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'] + // TODO: Disable this lint rule + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments + SampleGasPricesServiceEvents | AllowedEvents >; // === SERVICE DEFINITION === @@ -76,20 +76,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 +104,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..0d7d67159f7 100644 --- a/packages/sample-controllers/src/sample-petnames-controller.test.ts +++ b/packages/sample-controllers/src/sample-petnames-controller.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 MockAnyNamespace, + 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 +200,7 @@ describe('SamplePetnamesController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(`Object {}`); }); @@ -258,8 +261,9 @@ describe('SamplePetnamesController', () => { * required by the controller under test. */ type RootMessenger = Messenger< - ExtractAvailableAction, - ExtractAvailableEvent + MockAnyNamespace, + MessengerActions, + MessengerEvents >; /** @@ -285,7 +289,7 @@ type WithControllerOptions = { * @returns The root messenger. */ function getRootMessenger(): RootMessenger { - return new Messenger(); + return new Messenger({ namespace: MOCK_ANY_NAMESPACE }); } /** @@ -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/packages/seedless-onboarding-controller/CHANGELOG.md b/packages/seedless-onboarding-controller/CHANGELOG.md index ed65d94e985..3b8667ca1ea 100644 --- a/packages/seedless-onboarding-controller/CHANGELOG.md +++ b/packages/seedless-onboarding-controller/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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`. - Bump `@metamask/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [4.1.0] diff --git a/packages/seedless-onboarding-controller/package.json b/packages/seedless-onboarding-controller/package.json index 69f4f5ab69e..2544a0efae6 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.4.2", + "@metamask/messenger": "^0.3.0", "@metamask/toprf-secure-backup": "^0.7.1", "@metamask/utils": "^11.8.1", "@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 d058088eb61..b56267d9132 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.test.ts @@ -1,8 +1,5 @@ import { keccak256AndHexify } from '@metamask/auth-network-utils'; -import { - deriveStateFromMetadata, - type Messenger, -} from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import type { EncryptionKey } from '@metamask/browser-passworder'; import { encrypt, @@ -54,9 +51,9 @@ import type { VaultEncryptor, } from './types'; import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../base-controller/tests/helpers'; + MockKeyringControllerMessenger, + RootMessenger, +} from '../tests/__fixtures__/mockMessenger'; import { mockSeedlessOnboardingMessenger } from '../tests/__fixtures__/mockMessenger'; import { handleMockSecretDataGet, @@ -122,10 +119,8 @@ type WithControllerCallback = ({ encryptor: VaultEncryptor; initialState: SeedlessOnboardingControllerState; messenger: SeedlessOnboardingControllerMessenger; - baseMessenger: Messenger< - ExtractAvailableAction, - ExtractAvailableEvent - >; + baseMessenger: RootMessenger; + keyringControllerMessenger: MockKeyringControllerMessenger; toprfClient: ToprfSecureBackup; mockRefreshJWTToken: jest.Mock; mockRevokeRefreshToken: jest.Mock; @@ -185,7 +180,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'], @@ -235,6 +231,7 @@ async function withController( initialState: controller.state, messenger, baseMessenger, + keyringControllerMessenger, toprfClient, mockRefreshJWTToken, mockRevokeRefreshToken, @@ -5473,7 +5470,7 @@ describe('SeedlessOnboardingController', () => { deriveStateFromMetadata( controller.state, controller.metadata, - 'anonymous', + 'includeInDebugSnapshot', ), ).toMatchInlineSnapshot(` Object { diff --git a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts index b06b377a956..319f001fde8 100644 --- a/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts +++ b/packages/seedless-onboarding-controller/src/SeedlessOnboardingController.ts @@ -1,6 +1,8 @@ import { keccak256AndHexify } from '@metamask/auth-network-utils'; -import type { StateMetadata } from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; +import { + BaseController, + type StateMetadata, +} from '@metamask/base-controller/next'; import type { KeyPair, RecoverEncryptionKeyResult, @@ -99,86 +101,86 @@ const seedlessOnboardingMetadata: StateMetadata !isNullOrUndefined(nodeAuthTokens), persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, authConnection: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: true, }, authConnectionId: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, groupedAuthConnectionId: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, userId: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, socialLoginEmail: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, vaultEncryptionKey: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, vaultEncryptionSalt: { includeInStateLogs: false, persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, authPubKey: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, passwordOutdatedCache: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, refreshToken: { includeInStateLogs: (refreshToken) => !isNullOrUndefined(refreshToken), persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, revokeToken: { includeInStateLogs: (revokeToken) => !isNullOrUndefined(revokeToken), persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, pendingToBeRevokedTokens: { @@ -186,14 +188,14 @@ const seedlessOnboardingMetadata: StateMetadata 0, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, // stays in vault accessToken: { includeInStateLogs: (accessToken) => !isNullOrUndefined(accessToken), persist: false, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, // stays outside of vault as this token is accessed by the metadata service @@ -202,25 +204,25 @@ const seedlessOnboardingMetadata: StateMetadata !isNullOrUndefined(metadataAccessToken), persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, encryptedSeedlessEncryptionKey: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, encryptedKeyringEncryptionKey: { includeInStateLogs: false, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: false, }, isSeedlessOnboardingUserAuthenticated: { includeInStateLogs: true, persist: true, - anonymous: true, + includeInDebugSnapshot: true, usedInUi: false, }, }; diff --git a/packages/seedless-onboarding-controller/src/types.ts b/packages/seedless-onboarding-controller/src/types.ts index e8e132014ae..7756ebfdc9c 100644 --- a/packages/seedless-onboarding-controller/src/types.ts +++ b/packages/seedless-onboarding-controller/src/types.ts @@ -1,7 +1,9 @@ -import type { RestrictedMessenger } from '@metamask/base-controller'; -import type { ControllerGetStateAction } from '@metamask/base-controller'; -import type { ControllerStateChangeEvent } from '@metamask/base-controller'; +import type { + ControllerGetStateAction, + ControllerStateChangeEvent, +} from '@metamask/base-controller'; import type { ExportableKeyEncryptor } from '@metamask/keyring-controller'; +import type { Messenger } from '@metamask/messenger'; import type { KeyPair, NodeAuthTokens } from '@metamask/toprf-secure-backup'; import type { MutexInterface } from 'async-mutex'; @@ -205,12 +207,10 @@ export type SeedlessOnboardingControllerEvents = type AllowedEvents = never; // 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 acb03986560..07e62860135 100644 --- a/packages/seedless-onboarding-controller/tests/__fixtures__/mockMessenger.ts +++ b/packages/seedless-onboarding-controller/tests/__fixtures__/mockMessenger.ts @@ -1,39 +1,76 @@ -import { Messenger } from '@metamask/base-controller'; - import type { - ExtractAvailableAction, - ExtractAvailableEvent, -} from '../../../base-controller/tests/helpers'; + KeyringControllerLockEvent, + KeyringControllerUnlockEvent, +} from '@metamask/keyring-controller'; +import { + Messenger, + MOCK_ANY_NAMESPACE, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; + +import { controllerName } from '../../src/constants'; import { type SeedlessOnboardingControllerMessenger } from '../../src/types'; +export type AllSeedlessOnboardingControllerActions = + MessengerActions; +export type AllSeedlessOnboardingControllerEvents = + MessengerEvents; + +export type MockKeyringControllerMessenger = Messenger< + 'KeyringController', + never, + KeyringControllerLockEvent | KeyringControllerUnlockEvent +>; + +export type RootMessenger = Messenger< + MockAnyNamespace, + AllSeedlessOnboardingControllerActions, + | AllSeedlessOnboardingControllerEvents + | MessengerEvents +>; + /** * 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< - ExtractAvailableAction, - ExtractAvailableEvent - >(); - const messenger = baseMessenger.getRestricted({ - name: 'SeedlessOnboardingController', - allowedActions: [], - allowedEvents: [], + // Create the root messenger + const baseMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); + + const keyringControllerMessenger = new Messenger< + 'KeyringController', + never, + KeyringControllerLockEvent | KeyringControllerUnlockEvent, + RootMessenger + >({ namespace: 'KeyringController', parent: baseMessenger }); + + // Create the seedless onboarding controller messenger + const messenger = new Messenger< + typeof controllerName, + AllSeedlessOnboardingControllerActions, + AllSeedlessOnboardingControllerEvents, + RootMessenger + >({ + namespace: controllerName, + parent: baseMessenger, }); return { baseMessenger, messenger, + keyringControllerMessenger, }; } type OverrideMessengers = { - baseMessenger: Messenger< - ExtractAvailableAction, - ExtractAvailableEvent - >; + baseMessenger: RootMessenger; messenger: SeedlessOnboardingControllerMessenger; + keyringControllerMessenger: MockKeyringControllerMessenger; }; /** @@ -45,7 +82,7 @@ type OverrideMessengers = { export function mockSeedlessOnboardingMessenger( overrideMessengers?: OverrideMessengers, ) { - const { baseMessenger, messenger } = + const { baseMessenger, messenger, keyringControllerMessenger } = overrideMessengers ?? createCustomSeedlessOnboardingMessenger(); const mockKeyringGetAccounts = jest.fn(); @@ -56,6 +93,7 @@ export function mockSeedlessOnboardingMessenger( return { baseMessenger, messenger, + keyringControllerMessenger, mockKeyringGetAccounts, mockKeyringAddAccounts, mockAccountsListAccounts, diff --git a/packages/selected-network-controller/CHANGELOG.md b/packages/selected-network-controller/CHANGELOG.md index d257bc45259..9d5b1379a10 100644 --- a/packages/selected-network-controller/CHANGELOG.md +++ b/packages/selected-network-controller/CHANGELOG.md @@ -11,6 +11,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/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)) diff --git a/packages/selected-network-controller/package.json b/packages/selected-network-controller/package.json index 962ef749c81..8692978653f 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.2", "@metamask/json-rpc-engine": "^10.1.1", + "@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/packages/shield-controller/CHANGELOG.md b/packages/shield-controller/CHANGELOG.md index 79fb0e147c6..be53f4a96e5 100644 --- a/packages/shield-controller/CHANGELOG.md +++ b/packages/shield-controller/CHANGELOG.md @@ -7,6 +7,15 @@ 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`. + ## [0.4.0] ### Added diff --git a/packages/shield-controller/package.json b/packages/shield-controller/package.json index e41a4d5f436..be895ccc757 100644 --- a/packages/shield-controller/package.json +++ b/packages/shield-controller/package.json @@ -48,6 +48,7 @@ }, "dependencies": { "@metamask/base-controller": "^8.4.2", + "@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 ea6d7553fb8..2e466932a94 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, @@ -40,7 +40,7 @@ function setup({ normalizeSignatureRequest?: NormalizeSignatureRequestFn; } = {}) { const backend = createMockBackend(); - const { messenger, baseMessenger } = createMockMessenger(); + const { messenger, rootMessenger } = createMockMessenger(); const controller = new ShieldController({ backend, @@ -53,7 +53,7 @@ function setup({ return { controller, messenger, - baseMessenger, + rootMessenger, backend, }; } @@ -61,10 +61,10 @@ function setup({ describe('ShieldController', () => { describe('checkCoverage', () => { it('should trigger checkCoverage when a new transaction is added', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, messenger, backend } = setup(); const txMeta = generateMockTxMeta(); - const coverageResultReceived = setupCoverageResultReceived(baseMessenger); - baseMessenger.publish( + const coverageResultReceived = setupCoverageResultReceived(messenger); + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -74,14 +74,14 @@ describe('ShieldController', () => { }); it('should tolerate calling start and stop multiple times', async () => { - const { backend, baseMessenger, controller } = setup(); + const { backend, rootMessenger, messenger, controller } = setup(); controller.stop(); controller.stop(); controller.start(); controller.start(); const txMeta = generateMockTxMeta(); - const coverageResultReceived = setupCoverageResultReceived(baseMessenger); - baseMessenger.publish( + const coverageResultReceived = setupCoverageResultReceived(messenger); + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -91,11 +91,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(), ); @@ -104,7 +104,7 @@ describe('ShieldController', () => { 100, ); }); - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -143,12 +143,12 @@ describe('ShieldController', () => { }); it('should check coverage when a transaction is simulated', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, messenger, backend } = setup(); const txMeta = generateMockTxMeta(); - const coverageResultReceived = setupCoverageResultReceived(baseMessenger); + const coverageResultReceived = setupCoverageResultReceived(messenger); // Add transaction. - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -161,9 +161,8 @@ describe('ShieldController', () => { txMeta2.simulationData = { tokenBalanceChanges: [], }; - const coverageResultReceived2 = - setupCoverageResultReceived(baseMessenger); - baseMessenger.publish( + const coverageResultReceived2 = setupCoverageResultReceived(messenger); + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta2] } as TransactionControllerState, undefined as never, @@ -178,16 +177,15 @@ describe('ShieldController', () => { TX_META_SIMULATION_DATA_MOCKS.forEach( ({ description, previousSimulationData, newSimulationData }) => { it(`should check coverage when ${description}`, async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, backend, messenger } = setup(); const previousTxMeta = { ...generateMockTxMeta(), simulationData: previousSimulationData, }; - const coverageResultReceived = - setupCoverageResultReceived(baseMessenger); + const coverageResultReceived = setupCoverageResultReceived(messenger); // Add transaction. - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [previousTxMeta] } as TransactionControllerState, undefined as never, @@ -201,8 +199,8 @@ describe('ShieldController', () => { const txMeta2 = { ...previousTxMeta }; txMeta2.simulationData = newSimulationData; const coverageResultReceived2 = - setupCoverageResultReceived(baseMessenger); - baseMessenger.publish( + setupCoverageResultReceived(messenger); + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta2] } as TransactionControllerState, undefined as never, @@ -236,14 +234,14 @@ describe('ShieldController', () => { const MOCK_SIGNATURE_REQUEST = generateMockSignatureRequest(); it('should check signature coverage', async () => { - const { baseMessenger, backend } = setup(); + const { rootMessenger, backend } = setup(); const coverageResultReceived = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { @@ -271,16 +269,16 @@ describe('ShieldController', () => { .mockImplementationOnce((_signatureRequest: SignatureRequest) => { return MOCK_NORMALIZED_SIGNATURE_REQUEST; }); - const { baseMessenger, backend } = setup({ + const { rootMessenger, backend } = setup({ normalizeSignatureRequest: normalizeSignatureRequestMock, }); const coverageResultReceived = new Promise((resolve) => { - baseMessenger.subscribe( + rootMessenger.subscribe( 'ShieldController:coverageResultReceived', (_coverageResult) => resolve(), ); }); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { @@ -301,15 +299,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: { @@ -325,12 +323,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: { @@ -361,7 +359,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) => @@ -370,7 +368,7 @@ describe('ShieldController', () => { // Publish a signature request const signatureRequest = generateMockSignatureRequest(); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { [signatureRequest.id]: signatureRequest }, @@ -386,7 +384,7 @@ describe('ShieldController', () => { updatedSignatureRequest.status = SignatureRequestStatus.Signed; updatedSignatureRequest.rawSig = '0x00'; options?.updateSignatureRequest?.(updatedSignatureRequest); - baseMessenger.publish( + rootMessenger.publish( 'SignatureController:stateChange', { signatureRequests: { [signatureRequest.id]: updatedSignatureRequest }, @@ -455,7 +453,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), @@ -463,7 +461,7 @@ describe('ShieldController', () => { // Publish a transaction const txMeta = generateMockTxMeta(); - baseMessenger.publish( + rootMessenger.publish( 'TransactionController:stateChange', { transactions: [txMeta] } as TransactionControllerState, undefined as never, @@ -477,7 +475,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, @@ -536,7 +534,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 6d3443c3cef..8f5e8ae2a96 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, type SignatureRequest, @@ -58,6 +59,11 @@ export function getDefaultShieldControllerState(): ShieldControllerState { }; } +export type ShieldControllerGetStateAction = ControllerGetStateAction< + typeof controllerName, + ShieldControllerState +>; + export type ShieldControllerCheckCoverageAction = { type: `${typeof controllerName}:checkCoverage`; handler: ShieldController['checkCoverage']; @@ -66,7 +72,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`; @@ -85,11 +93,6 @@ export type ShieldControllerEvents = | ShieldControllerCoverageResultReceivedEvent | ShieldControllerStateChangeEvent; -/** - * The external actions available to the ShieldController. - */ -type AllowedActions = never; - /** * The external events available to the ShieldController. */ @@ -100,12 +103,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 >; /** @@ -116,13 +117,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, }, }; @@ -206,13 +207,13 @@ export class ShieldController extends BaseController< } this.#started = true; - 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, @@ -225,12 +226,12 @@ export class ShieldController extends BaseController< } this.#started = false; - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'TransactionController:stateChange', this.#transactionControllerStateChangeHandler, ); - this.messagingSystem.unsubscribe( + this.messenger.unsubscribe( 'SignatureController:stateChange', this.#signatureControllerStateChangeHandler, ); @@ -326,7 +327,7 @@ export class ShieldController extends BaseController< }); // Publish coverage result - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:coverageResultReceived`, coverageResult, ); @@ -361,7 +362,7 @@ export class ShieldController extends BaseController< }); // 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 a5a8c525db3..a738356262c 100644 --- a/packages/shield-controller/src/index.ts +++ b/packages/shield-controller/src/index.ts @@ -9,6 +9,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/tests/utils.ts b/packages/shield-controller/tests/utils.ts index 807b6d6e5cf..e26b6a11e1a 100644 --- a/packages/shield-controller/tests/utils.ts +++ b/packages/shield-controller/tests/utils.ts @@ -89,20 +89,17 @@ export function getRandomCoverageResult() { /** * Setup a coverage result received handler. * - * @param baseMessenger - The base messenger. + * @param messenger - The controller messenger. * @returns A promise that resolves when the coverage result is received. */ export function setupCoverageResultReceived( - baseMessenger: ReturnType['baseMessenger'], + messenger: ReturnType['messenger'], ): Promise { return new Promise((resolve) => { const handler = (_coverageResult: unknown) => { - baseMessenger.unsubscribe( - 'ShieldController:coverageResultReceived', - handler, - ); + messenger.unsubscribe('ShieldController:coverageResultReceived', handler); resolve(); }; - baseMessenger.subscribe('ShieldController:coverageResultReceived', handler); + messenger.subscribe('ShieldController:coverageResultReceived', handler); }); } 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/packages/signature-controller/CHANGELOG.md b/packages/signature-controller/CHANGELOG.md index e891ed1c9ad..54e141b27d4 100644 --- a/packages/signature-controller/CHANGELOG.md +++ b/packages/signature-controller/CHANGELOG.md @@ -11,6 +11,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/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)) diff --git a/packages/signature-controller/package.json b/packages/signature-controller/package.json index 77bd63f61e8..5560143be70 100644 --- a/packages/signature-controller/package.json +++ b/packages/signature-controller/package.json @@ -50,6 +50,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@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/packages/subscription-controller/CHANGELOG.md b/packages/subscription-controller/CHANGELOG.md index 75366355549..528f8236b9c 100644 --- a/packages/subscription-controller/CHANGELOG.md +++ b/packages/subscription-controller/CHANGELOG.md @@ -15,6 +15,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, `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)) diff --git a/packages/subscription-controller/package.json b/packages/subscription-controller/package.json index 772ae273768..8bb3c045df1 100644 --- a/packages/subscription-controller/package.json +++ b/packages/subscription-controller/package.json @@ -49,6 +49,7 @@ "dependencies": { "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", + "@metamask/messenger": "^0.3.0", "@metamask/polling-controller": "^14.0.2", "@metamask/utils": "^11.8.1" }, diff --git a/packages/subscription-controller/src/SubscriptionController.test.ts b/packages/subscription-controller/src/SubscriptionController.test.ts index 03f351c8300..d02ae60a9ce 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 a0642702086..9ac4602b8a5 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/packages/token-search-discovery-controller/CHANGELOG.md b/packages/token-search-discovery-controller/CHANGELOG.md index 0bedcc80fa7..6fd161c8163 100644 --- a/packages/token-search-discovery-controller/CHANGELOG.md +++ b/packages/token-search-discovery-controller/CHANGELOG.md @@ -11,6 +11,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.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) ## [3.5.0] diff --git a/packages/token-search-discovery-controller/package.json b/packages/token-search-discovery-controller/package.json index 0ebbcab2ef8..a49e0474c62 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.2", + "@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/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index ad53ec10f6b..2c13c272f42 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -48,6 +48,11 @@ 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 diff --git a/packages/transaction-controller/package.json b/packages/transaction-controller/package.json index 3b68d910323..7aa0dfc821e 100644 --- a/packages/transaction-controller/package.json +++ b/packages/transaction-controller/package.json @@ -57,6 +57,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@metamask/eth-query": "^4.0.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/packages/transaction-controller/src/TransactionController.test.ts b/packages/transaction-controller/src/TransactionController.test.ts index 44ed9a2ca41..f59aac432ca 100644 --- a/packages/transaction-controller/src/TransactionController.test.ts +++ b/packages/transaction-controller/src/TransactionController.test.ts @@ -4,7 +4,7 @@ import type { AddApprovalRequest, AddResult, } from '@metamask/approval-controller'; -import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller'; +import { deriveStateFromMetadata } from '@metamask/base-controller/next'; import { ChainId, NetworkType, @@ -15,6 +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, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, + MOCK_ANY_NAMESPACE, +} from '@metamask/messenger'; import type { BlockTracker, NetworkClientConfiguration, @@ -50,11 +57,8 @@ 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'; import { TransactionController } from './TransactionController'; @@ -111,9 +115,16 @@ import { buildMockGetNetworkClientById, } from '../../network-controller/tests/helpers'; -type UnrestrictedMessenger = Messenger< - TransactionControllerActions | AllowedActions, - TransactionControllerEvents | AllowedEvents +type AllTransactionControllerActions = + MessengerActions; + +type AllTransactionControllerEvents = + MessengerEvents; + +type RootMessenger = Messenger< + MockAnyNamespace, + AllTransactionControllerActions, + AllTransactionControllerEvents >; const MOCK_V1_UUID = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'; @@ -329,10 +340,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 @@ -662,11 +670,13 @@ describe('TransactionController', () => { listener(networkState); }); }; - const unrestrictedMessenger: UnrestrictedMessenger = new Messenger(); + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); const getNetworkClientById = buildMockGetNetworkClientById( mockNetworkClientConfigurationsByNetworkClientId, ); - unrestrictedMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'NetworkController:getNetworkClientById', getNetworkClientById, ); @@ -674,7 +684,7 @@ describe('TransactionController', () => { const { addTransactionApprovalRequest = { state: 'pending' } } = messengerOptions; const mockTransactionApprovalRequest = mockAddTransactionApprovalRequest( - unrestrictedMessenger, + rootMessenger, addTransactionApprovalRequest, ); @@ -699,28 +709,31 @@ 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 mockGetSelectedAccount = jest.fn().mockReturnValue(selectedAccount); - unrestrictedMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:getSelectedAccount', mockGetSelectedAccount, ); - unrestrictedMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'AccountsController:getState', () => ({}) as never, ); @@ -729,14 +742,14 @@ describe('TransactionController', () => { featureFlags: {}, }); - unrestrictedMessenger.registerActionHandler( + rootMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', remoteFeatureFlagControllerGetStateMock, ); const controller = new TransactionController({ ...otherOptions, - messenger: restrictedMessenger, + messenger: transactionControllerMessenger, } as TransactionControllerOptions); const state = givenOptions?.state; @@ -759,7 +772,8 @@ describe('TransactionController', () => { return { controller, - messenger: unrestrictedMessenger, + messenger: transactionControllerMessenger, + rootMessenger, mockTransactionApprovalRequest, mockGetSelectedAccount, changeNetwork, @@ -788,7 +802,7 @@ describe('TransactionController', () => { * finally the mocked version of the action handler itself. */ function mockAddTransactionApprovalRequest( - messenger: UnrestrictedMessenger, + messenger: RootMessenger, options: | { state: 'approved'; @@ -6044,8 +6058,8 @@ describe('TransactionController', () => { }); it('uses the nonceTracker for the networkClientId matching the chainId', async () => { - const { controller, messenger } = setupController(); - messenger.registerActionHandler( + const { controller, rootMessenger } = setupController(); + rootMessenger.registerActionHandler( 'NetworkController:findNetworkClientIdByChainId', () => 'sepolia', ); @@ -8234,7 +8248,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 705f33f6b48..f30de01f283 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -11,9 +11,9 @@ import type { import type { ControllerGetStateAction, ControllerStateChangeEvent, - RestrictedMessenger, -} from '@metamask/base-controller'; -import { BaseController } from '@metamask/base-controller'; + StateMetadata, +} from '@metamask/base-controller/next'; +import { BaseController } from '@metamask/base-controller/next'; import { query, ApprovalType, @@ -27,6 +27,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, @@ -177,35 +178,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, }, }; @@ -758,12 +759,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 >; /** @@ -955,7 +954,7 @@ export class TransactionController extends BaseController< }, }); - this.messagingSystem = messenger; + this.messenger = messenger; this.#afterAdd = hooks?.afterAdd ?? (() => Promise.resolve({})); this.#afterSign = hooks?.afterSign ?? (() => true); @@ -1003,7 +1002,7 @@ export class TransactionController extends BaseController< this.#transactionHistoryLimit = transactionHistoryLimit; const findNetworkClientIdByChainId = (chainId: Hex) => { - return this.messagingSystem.call( + return this.messenger.call( `NetworkController:findNetworkClientIdByChainId`, chainId, ); @@ -1012,7 +1011,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, ); @@ -1024,10 +1023,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); }, }); @@ -1043,12 +1039,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); }, }); @@ -1083,7 +1076,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, @@ -1095,7 +1088,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, ); @@ -1103,7 +1096,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, @@ -1147,7 +1140,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, ); @@ -1170,7 +1163,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: ( @@ -1196,7 +1189,7 @@ export class TransactionController extends BaseController< return isAtomicBatchSupported({ ...request, getEthQuery: (chainId) => this.#getEthQuery({ chainId }), - messenger: this.messagingSystem, + messenger: this.messenger, publicKeyEIP7702: this.#publicKeyEIP7702, }); } @@ -1384,7 +1377,7 @@ export class TransactionController extends BaseController< { isSwapsDisabled: this.#isSwapsDisabled, cancelTransaction: this.#rejectTransaction.bind(this), - messenger: this.messagingSystem, + messenger: this.messenger, }, ); @@ -1416,7 +1409,7 @@ export class TransactionController extends BaseController< ); } - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:unapprovedTransactionAdded`, addedTransactionMeta, ); @@ -1484,7 +1477,7 @@ export class TransactionController extends BaseController< txParams.value = '0x0'; }, afterSubmit: (newTransactionMeta) => { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, newTransactionMeta, ); @@ -1523,7 +1516,7 @@ export class TransactionController extends BaseController< transactionId, transactionType: TransactionType.retry, afterSubmit: (newTransactionMeta) => { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:speedupTransactionAdded`, newTransactionMeta, ); @@ -1639,12 +1632,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, }); @@ -1680,7 +1673,7 @@ export class TransactionController extends BaseController< ignoreDelegationSignatures, isSimulationEnabled: this.#isSimulationEnabled(), getSimulationConfig: this.#getSimulationConfig, - messenger: this.messagingSystem, + messenger: this.messenger, txParams: transaction, }); @@ -1709,7 +1702,7 @@ export class TransactionController extends BaseController< ethQuery, isSimulationEnabled: this.#isSimulationEnabled(), getSimulationConfig: this.#getSimulationConfig, - messenger: this.messagingSystem, + messenger: this.messenger, txParams: transaction, }); @@ -1856,7 +1849,7 @@ export class TransactionController extends BaseController< throw error; }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionConfirmed`, updatedTransactionMeta, ); @@ -2200,7 +2193,7 @@ export class TransactionController extends BaseController< await updateTransactionLayer1GasFee({ layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, provider, transactionMeta: updatedTransaction, }); @@ -2394,7 +2387,7 @@ export class TransactionController extends BaseController< status as TransactionStatus, ) ) { - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, updatedTransactionMeta, ); @@ -2528,7 +2521,7 @@ export class TransactionController extends BaseController< const gasFeeFlow = getGasFeeFlow( transactionMeta, this.#gasFeeFlows, - this.messagingSystem, + this.messenger, ) as GasFeeFlow; const ethQuery = new EthQuery(provider); @@ -2540,7 +2533,7 @@ export class TransactionController extends BaseController< return gasFeeFlow.getGasFees({ ethQuery, gasFeeControllerData, - messenger: this.messagingSystem, + messenger: this.messenger, transactionMeta, }); } @@ -2570,7 +2563,7 @@ export class TransactionController extends BaseController< return await getTransactionLayer1GasFee({ layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, provider, transactionMeta: { txParams: transactionParams, @@ -2823,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 }, ); @@ -2844,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, }, }; @@ -2864,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) { @@ -2908,7 +2900,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, }), ); @@ -2918,7 +2910,7 @@ export class TransactionController extends BaseController< async () => await updateTransactionLayer1GasFee({ layer1GasFeeFlows: this.#layer1GasFeeFlows, - messenger: this.messagingSystem, + messenger: this.messenger, provider, transactionMeta, }), @@ -3025,13 +3017,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 }; @@ -3163,7 +3152,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, ); @@ -3233,11 +3222,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, ); @@ -3295,7 +3284,7 @@ export class TransactionController extends BaseController< error: normalizeTxError(error ?? providerErrors.userRejectedRequest()), }; - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionFinished`, updatedTransactionMeta, ); @@ -3305,7 +3294,7 @@ export class TransactionController extends BaseController< updatedTransactionMeta, ); - this.messagingSystem.publish(`${controllerName}:transactionRejected`, { + this.messenger.publish(`${controllerName}:transactionRejected`, { transactionMeta: updatedTransactionMeta, actionId, }); @@ -3407,7 +3396,7 @@ export class TransactionController extends BaseController< parentContext: traceContext, }); - return (await this.messagingSystem.call( + return (await this.messenger.call( 'ApprovalController:addRequest', { id, @@ -3543,7 +3532,7 @@ export class TransactionController extends BaseController< ); }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:incomingTransactionsReceived`, finalTransactions, ); @@ -3692,7 +3681,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( @@ -3782,7 +3771,7 @@ export class TransactionController extends BaseController< const signedAuthorizationList = await signAuthorizationList({ authorizationList, - messenger: this.messagingSystem, + messenger: this.messenger, transactionMeta, }); @@ -3874,7 +3863,7 @@ export class TransactionController extends BaseController< } #onTransactionStatusChange(transactionMeta: TransactionMeta) { - this.messagingSystem.publish(`${controllerName}:transactionStatusUpdated`, { + this.messenger.publish(`${controllerName}:transactionStatusUpdated`, { transactionMeta, }); } @@ -3897,7 +3886,7 @@ export class TransactionController extends BaseController< this.#markNonceDuplicatesDropped(transactionMeta.id); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:transactionConfirmed`, transactionMeta, ); @@ -3928,7 +3917,7 @@ export class TransactionController extends BaseController< updateTransaction: this.updateTransaction.bind(this), }); - this.messagingSystem.publish( + this.messenger.publish( `${controllerName}:postTransactionBalanceUpdated`, { transactionMeta: updatedTransactionMeta, @@ -3992,7 +3981,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, @@ -4268,7 +4257,7 @@ export class TransactionController extends BaseController< chainId, getSimulationConfig: this.#getSimulationConfig, isEIP7702GasFeeTokensEnabled: this.#isEIP7702GasFeeTokensEnabled, - messenger: this.messagingSystem, + messenger: this.messenger, publicKeyEIP7702: this.#publicKeyEIP7702, transactionMeta, }); @@ -4367,11 +4356,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') @@ -4431,58 +4420,58 @@ 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}:addTransaction`, this.addTransaction.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:addTransactionBatch`, this.addTransactionBatch.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:confirmExternalTransaction`, this.confirmExternalTransaction.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:estimateGas`, this.estimateGas.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getNonceLock`, this.getNonceLock.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:getTransactions`, this.getTransactions.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateCustodialTransaction`, this.updateCustodialTransaction.bind(this), ); - this.messagingSystem.registerActionHandler( + this.messenger.registerActionHandler( `${controllerName}:updateTransaction`, 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), ); @@ -4556,7 +4545,7 @@ export class TransactionController extends BaseController< }; } - this.messagingSystem.publish(`${controllerName}:transactionFailed`, { + this.messenger.publish(`${controllerName}:transactionFailed`, { actionId, error: error.message, transactionMeta: newTransactionMeta, @@ -4564,7 +4553,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/TransactionControllerIntegration.test.ts b/packages/transaction-controller/src/TransactionControllerIntegration.test.ts index b1713b98523..48e94d4e685 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,13 @@ import { InfuraNetworkType, NetworkType, } from '@metamask/controller-utils'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { NetworkController, NetworkClientType, @@ -30,8 +36,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 +70,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; const uuidV4Mock = jest.mocked(uuidV4); @@ -158,13 +172,21 @@ const setupController = async ( ], }); - const unrestrictedMessenger: UnrestrictedMessenger = new Messenger(); + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); + + 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 +199,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 +233,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: {} }), ); diff --git a/packages/transaction-controller/src/utils/eip7702.test.ts b/packages/transaction-controller/src/utils/eip7702.test.ts index 1b70fa09db1..069b31f3ebc 100644 --- a/packages/transaction-controller/src/utils/eip7702.test.ts +++ b/packages/transaction-controller/src/utils/eip7702.test.ts @@ -1,6 +1,12 @@ import { query } from '@metamask/controller-utils'; import type EthQuery from '@metamask/eth-query'; -import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; +import { + Messenger, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, + MOCK_ANY_NAMESPACE, +} from '@metamask/messenger'; import type { Hex } from '@metamask/utils'; import { remove0x } from '@metamask/utils'; @@ -16,7 +22,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 +78,10 @@ const AUTHORIZATION_LIST_MOCK: AuthorizationList = [ ]; describe('EIP-7702 Utils', () => { - let baseMessenger: Messenger< - | KeyringControllerSignEip7702AuthorizationAction - | RemoteFeatureFlagControllerGetStateAction, - never + let rootMessenger: Messenger< + MockAnyNamespace, + MessengerActions, + MessengerEvents >; const getCodeMock = jest.mocked(query); @@ -95,21 +100,33 @@ describe('EIP-7702 Utils', () => { beforeEach(() => { jest.resetAllMocks(); - baseMessenger = new Messenger(); + rootMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); 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..1d1e8c91b93 100644 --- a/packages/transaction-controller/src/utils/feature-flags.test.ts +++ b/packages/transaction-controller/src/utils/feature-flags.test.ts @@ -1,4 +1,10 @@ -import { Messenger } from '@metamask/base-controller'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; import type { Hex } from '@metamask/utils'; @@ -36,13 +42,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< + MockAnyNamespace, + MessengerActions, + MessengerEvents >; let controllerMessenger: TransactionControllerMessenger; + let remoteFeatureFlagControllerMessenger: Messenger< + 'RemoteFeatureFlagController', + RemoteFeatureFlagControllerGetStateAction, + never, + typeof rootMessenger + >; + let getFeatureFlagsMock: jest.MockedFn< RemoteFeatureFlagControllerGetStateAction['handler'] >; @@ -66,17 +80,25 @@ describe('Feature Flags Utils', () => { getFeatureFlagsMock = jest.fn(); - baseMessenger = new Messenger(); + rootMessenger = new Messenger({ namespace: MOCK_ANY_NAMESPACE }); + + 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..147d9e9174a 100644 --- a/packages/transaction-controller/src/utils/swaps.test.ts +++ b/packages/transaction-controller/src/utils/swaps.test.ts @@ -1,5 +1,11 @@ -import { Messenger } from '@metamask/base-controller'; import { query } from '@metamask/controller-utils'; +import { + MOCK_ANY_NAMESPACE, + Messenger, + type MockAnyNamespace, + type MessengerActions, + type MessengerEvents, +} from '@metamask/messenger'; import { updateSwapsTransaction, @@ -9,13 +15,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 +47,29 @@ describe('updateSwapsTransaction', () => { destinationTokenSymbol: 'DAI', }, }; + const rootMessenger = new Messenger< + MockAnyNamespace, + MessengerActions, + MessengerEvents + >({ + namespace: MOCK_ANY_NAMESPACE, + }); 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/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"] diff --git a/packages/user-operation-controller/CHANGELOG.md b/packages/user-operation-controller/CHANGELOG.md index 773a51aaa49..1b22e08e714 100644 --- a/packages/user-operation-controller/CHANGELOG.md +++ b/packages/user-operation-controller/CHANGELOG.md @@ -11,6 +11,8 @@ 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/base-controller` from `^8.4.1` to `^8.4.2` ([#6917](https://github.com/MetaMask/core/pull/6917)) - Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940)) diff --git a/packages/user-operation-controller/package.json b/packages/user-operation-controller/package.json index 0a76137abff..de6e9389449 100644 --- a/packages/user-operation-controller/package.json +++ b/packages/user-operation-controller/package.json @@ -51,6 +51,7 @@ "@metamask/base-controller": "^8.4.2", "@metamask/controller-utils": "^11.14.1", "@metamask/eth-query": "^4.0.0", + "@metamask/messenger": "^0.3.0", "@metamask/polling-controller": "^14.0.2", "@metamask/rpc-errors": "^7.0.2", "@metamask/superstruct": "^3.1.0", 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 6f0180d5b84..6d76efd0c08 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'; @@ -60,7 +63,7 @@ const stateMetadata = { userOperations: { includeInStateLogs: true, persist: true, - anonymous: false, + includeInDebugSnapshot: false, usedInUi: true, }, }; @@ -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 f67c9bc6cb0..452a9d34169 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2576,6 +2576,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/keyring-api": "npm:^21.0.0" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/multichain-account-service": "npm:^1.6.2" "@metamask/profile-sync-controller": "npm:^25.1.2" "@metamask/providers": "npm:^22.1.0" @@ -2619,6 +2620,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.2.0" "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" @@ -2666,6 +2668,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" @@ -2683,6 +2686,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" @@ -2706,6 +2710,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" @@ -2723,6 +2728,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@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" @@ -2764,6 +2770,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.2.0" "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-snap-client": "npm:^8.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/multichain-account-service": "npm:^1.6.2" "@metamask/network-controller": "npm:^24.3.1" @@ -2905,6 +2912,7 @@ __metadata: "@metamask/eth-json-rpc-provider": "npm:^5.0.1" "@metamask/gas-fee-controller": "npm:^24.1.1" "@metamask/keyring-api": "npm:^21.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/multichain-network-controller": "npm:^1.0.2" "@metamask/network-controller": "npm:^24.3.1" @@ -3031,6 +3039,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/json-rpc-engine": "npm:^10.1.1" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" @@ -3089,9 +3098,9 @@ __metadata: dependencies: "@metamask/accounts-controller": "npm:^33.2.0" "@metamask/auto-changelog": "npm:^3.4.4" - "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/profile-sync-controller": "npm:^25.1.2" "@metamask/utils": "npm:^11.8.1" "@ts-bridge/cli": "npm:^0.6.1" @@ -3203,6 +3212,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@ts-bridge/cli": "npm:^0.6.1" "@types/jest": "npm:^27.4.1" @@ -3247,6 +3257,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/keyring-api": "npm:^21.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/stake-sdk": "npm:^3.2.1" "@metamask/transaction-controller": "npm:^60.10.0" @@ -3336,6 +3347,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -3357,6 +3369,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@metamask/messenger": "npm:^0.3.0" "@sentry/core": "npm:^9.22.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" @@ -3818,6 +3831,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/delegation-core": "npm:^0.2.0" "@metamask/delegation-deployments": "npm:^0.12.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" "@metamask/snaps-utils": "npm:^11.0.0" @@ -3924,6 +3938,7 @@ __metadata: "@metamask/keyring-api": "npm:^21.0.0" "@metamask/keyring-internal-api": "npm:^9.0.0" "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -4003,6 +4018,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" jest: "npm:^27.5.1" @@ -4022,6 +4038,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" "@types/uuid": "npm:^8.3.0" @@ -4077,6 +4094,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.3.0" "@metamask/providers": "npm:^22.1.0" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" @@ -4144,6 +4162,7 @@ __metadata: "@metamask/keyring-api": "npm:^21.0.0" "@metamask/keyring-controller": "npm:^23.2.0" "@metamask/keyring-internal-api": "npm:^9.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.8.1" @@ -4177,6 +4196,7 @@ __metadata: "@metamask/keyring-controller": "npm:^23.2.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.2" "@metamask/snaps-controllers": "npm:^14.0.1" "@metamask/snaps-sdk": "npm:^9.0.0" @@ -4205,6 +4225,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" async-mutex: "npm:^0.5.0" @@ -4232,6 +4253,7 @@ __metadata: "@metamask/eth-json-rpc-provider": "npm:^5.0.1" "@metamask/eth-query": "npm:^4.0.0" "@metamask/json-rpc-engine": "npm:^10.1.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/swappable-obj-proxy": "npm:^2.3.0" "@metamask/utils": "npm:^11.8.1" @@ -4270,6 +4292,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/keyring-api": "npm:^21.0.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/multichain-network-controller": "npm:^1.0.2" "@metamask/network-controller": "npm:^24.3.1" "@metamask/transaction-controller": "npm:^60.10.0" @@ -4313,6 +4336,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/profile-sync-controller": "npm:^25.1.2" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -4367,6 +4391,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/json-rpc-engine": "npm:^10.1.1" + "@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" @@ -4392,6 +4417,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/json-rpc-engine": "npm:^10.1.1" + "@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" @@ -4428,6 +4454,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/transaction-controller": "npm:^60.10.0" "@noble/hashes": "npm:^1.8.0" "@types/jest": "npm:^27.4.1" @@ -4491,6 +4518,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" @@ -4517,6 +4545,7 @@ __metadata: "@metamask/keyring-api": "npm:^21.0.0" "@metamask/keyring-controller": "npm:^23.2.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" @@ -4574,6 +4603,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@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" @@ -4594,6 +4624,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" @@ -4631,6 +4662,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -4668,6 +4700,7 @@ __metadata: "@metamask/base-controller": "npm:^8.4.2" "@metamask/browser-passworder": "npm:^4.3.0" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/toprf-secure-backup": "npm:^0.7.1" "@metamask/utils": "npm:^11.8.1" "@noble/ciphers": "npm:^1.3.0" @@ -4697,6 +4730,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/json-rpc-engine": "npm:^10.1.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/permission-controller": "npm:^11.1.1" "@metamask/swappable-obj-proxy": "npm:^2.3.0" @@ -4727,6 +4761,7 @@ __metadata: "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@metamask/messenger": "npm:^0.3.0" "@metamask/signature-controller": "npm:^34.0.2" "@metamask/transaction-controller": "npm:^60.10.0" "@metamask/utils": "npm:^11.8.1" @@ -4759,6 +4794,7 @@ __metadata: "@metamask/gator-permissions-controller": "npm:^0.2.2" "@metamask/keyring-controller": "npm:^23.2.0" "@metamask/logging-controller": "npm:^6.1.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" @@ -4916,6 +4952,7 @@ __metadata: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/polling-controller": "npm:^14.0.2" "@metamask/profile-sync-controller": "npm:^25.1.2" "@metamask/utils": "npm:^11.8.1" @@ -4952,6 +4989,7 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/base-controller": "npm:^8.4.2" + "@metamask/messenger": "npm:^0.3.0" "@metamask/utils": "npm:^11.8.1" "@types/jest": "npm:^27.4.1" deepmerge: "npm:^4.2.2" @@ -5004,6 +5042,7 @@ __metadata: "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-provider-http": "npm:^0.3.0" "@metamask/gas-fee-controller": "npm:^24.1.1" + "@metamask/messenger": "npm:^0.3.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^24.3.1" "@metamask/nonce-tracker": "npm:^6.0.0" @@ -5052,6 +5091,7 @@ __metadata: "@metamask/eth-query": "npm:^4.0.0" "@metamask/gas-fee-controller": "npm:^24.1.1" "@metamask/keyring-controller": "npm:^23.2.0" + "@metamask/messenger": "npm:^0.3.0" "@metamask/network-controller": "npm:^24.3.1" "@metamask/polling-controller": "npm:^14.0.2" "@metamask/rpc-errors": "npm:^7.0.2"