Skip to content

Commit ed168cf

Browse files
refactor: migrate {AccountActivity,BackendWebSocket}Service to @metamask/messenger (#6823)
## Explanation <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> This PR migrates `AccountActivityService` and `BackendWebSocketService` to the new `@metamask/messenger` message bus, as opposed to the one exported from `@metamask/base-controller`. ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs), highlighting breaking changes as necessary - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Replaces base-controller messenger with @metamask/messenger across core-backend services, updates types/exports, tests, dependencies, and docs with breaking changes noted. > > - **Core Backend Services**: > - Migrate `AccountActivityService` and `BackendWebSocketService` to `@metamask/messenger` (`Messenger`) replacing `RestrictedMessenger`. > - Update messenger wiring: new root/child messengers, `delegate`, `publish/subscribe/call` usage; adjust connection, subscription, and event handling accordingly. > - Refactor tests to use new messenger APIs and types. > - **API/Types (BREAKING)**: > - Remove controller-messenger-specific exported aliases/constants: `BackendWebSocketServiceAllowedActions/Events`, `AccountActivityServiceAllowedActions/Events`, `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS/ EVENTS`. > - Rename metadata property `anonymous` to `includeInDebugSnapshot`. > - **Dependencies/Build**: > - Remove `@metamask/base-controller`; add `@metamask/messenger` and reference in `tsconfig`. > - Bump `@metamask/profile-sync-controller` to `^25.1.2`. > - **Docs**: > - Update README dependency graph: `core_backend --> messenger`; add `eip_7702_internal_rpc_middleware --> controller_utils`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 1eef1b3. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Michele Esposito <michele@esposito.codes>
1 parent ce4c907 commit ed168cf

File tree

11 files changed

+163
-86
lines changed

11 files changed

+163
-86
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ linkStyle default opacity:0.5
210210
composable_controller --> base_controller;
211211
composable_controller --> messenger;
212212
composable_controller --> json_rpc_engine;
213-
core_backend --> base_controller;
214213
core_backend --> controller_utils;
214+
core_backend --> messenger;
215215
core_backend --> profile_sync_controller;
216216
core_backend --> accounts_controller;
217217
core_backend --> keyring_controller;

packages/core-backend/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12+
- **BREAKING:** Use new `Messenger` from `@metamask/messenger` ([#6823](https://github.com/MetaMask/core/pull/6823))
13+
- Previously, `AccountActivityService` and `BackendWebSocketService` accepted a `RestrictedMessenger` instance from `@metamask/base-controller`.
14+
- **BREAKING:** Metadata property `anonymous` renamed to `includeInDebugSnapshot` ([#6823](https://github.com/MetaMask/core/pull/6823))
15+
16+
### Removed
17+
18+
- **BREAKING:** Remove exported type aliases and constants that were specific to controller messenger integration ([#6823](https://github.com/MetaMask/core/pull/6823))
19+
- Removed type exports: `BackendWebSocketServiceAllowedActions`, `BackendWebSocketServiceAllowedEvents`, `AccountActivityServiceAllowedActions`, `AccountActivityServiceAllowedEvents`
20+
- Removed constant exports: `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS`, `ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS`
21+
- These types and constants were internal implementation details that should not have been exposed. Consumers should use the service-specific messenger types directly.
1222
- Bump `@metamask/profile-sync-controller` from `^25.1.1` to `^25.1.2` ([#6940](https://github.com/MetaMask/core/pull/6940))
1323

1424
## [3.0.0]

packages/core-backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@
4747
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
4848
},
4949
"dependencies": {
50-
"@metamask/base-controller": "^8.4.2",
5150
"@metamask/controller-utils": "^11.14.1",
51+
"@metamask/messenger": "^0.3.0",
5252
"@metamask/profile-sync-controller": "^25.1.2",
5353
"@metamask/utils": "^11.8.1",
5454
"uuid": "^8.3.2"

packages/core-backend/src/AccountActivityService.test.ts

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
1-
import { Messenger } from '@metamask/base-controller';
21
import type { InternalAccount } from '@metamask/keyring-internal-api';
2+
import {
3+
Messenger,
4+
MOCK_ANY_NAMESPACE,
5+
type MessengerActions,
6+
type MessengerEvents,
7+
type MockAnyNamespace,
8+
} from '@metamask/messenger';
39
import type { Hex } from '@metamask/utils';
410

5-
import type {
6-
AccountActivityServiceAllowedEvents,
7-
AccountActivityServiceAllowedActions,
8-
} from './AccountActivityService';
911
import {
1012
AccountActivityService,
1113
type AccountActivityServiceMessenger,
1214
type SubscriptionOptions,
13-
ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS,
14-
ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS,
1515
} from './AccountActivityService';
1616
import type { ServerNotificationMessage } from './BackendWebSocketService';
1717
import { WebSocketState } from './BackendWebSocketService';
1818
import type { Transaction, BalanceUpdate } from './types';
1919
import type { AccountActivityMessage } from './types';
2020
import { flushPromises } from '../../../tests/helpers';
2121

22+
type AllAccountActivityServiceActions =
23+
MessengerActions<AccountActivityServiceMessenger>;
24+
25+
type AllAccountActivityServiceEvents =
26+
MessengerEvents<AccountActivityServiceMessenger>;
27+
28+
type RootMessenger = Messenger<
29+
MockAnyNamespace,
30+
AllAccountActivityServiceActions,
31+
AllAccountActivityServiceEvents
32+
>;
33+
2234
// Helper function for completing async operations
2335
const completeAsyncOperations = async (timeoutMs = 0) => {
2436
await flushPromises();
@@ -48,25 +60,70 @@ const createMockInternalAccount = (options: {
4860
scopes: ['eip155:1'], // Required scopes property
4961
});
5062

63+
/**
64+
* Creates and returns a root messenger for testing
65+
*
66+
* @returns A messenger instance
67+
*/
68+
function getRootMessenger(): RootMessenger {
69+
return new Messenger({
70+
namespace: MOCK_ANY_NAMESPACE,
71+
});
72+
}
73+
5174
/**
5275
* Creates a real messenger with registered mock actions for testing
5376
* Each call creates a completely independent messenger to ensure test isolation
5477
*
5578
* @returns Object containing the messenger and mock action functions
5679
*/
57-
const getMessenger = () => {
80+
const getMessenger = (): {
81+
rootMessenger: RootMessenger;
82+
messenger: AccountActivityServiceMessenger;
83+
mocks: {
84+
getSelectedAccount: jest.Mock;
85+
connect: jest.Mock;
86+
subscribe: jest.Mock;
87+
channelHasSubscription: jest.Mock;
88+
getSubscriptionsByChannel: jest.Mock;
89+
findSubscriptionsByChannelPrefix: jest.Mock;
90+
forceReconnection: jest.Mock;
91+
addChannelCallback: jest.Mock;
92+
removeChannelCallback: jest.Mock;
93+
};
94+
} => {
5895
// Use any types for the root messenger to avoid complex type constraints in tests
5996
// Create a unique root messenger for each test
60-
const rootMessenger = new Messenger<
61-
AccountActivityServiceAllowedActions,
62-
AccountActivityServiceAllowedEvents
63-
>();
64-
const messenger: AccountActivityServiceMessenger =
65-
rootMessenger.getRestricted({
66-
name: 'AccountActivityService',
67-
allowedActions: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS],
68-
allowedEvents: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS],
69-
});
97+
const rootMessenger = getRootMessenger();
98+
const messenger: AccountActivityServiceMessenger = new Messenger<
99+
'AccountActivityService',
100+
AllAccountActivityServiceActions,
101+
AllAccountActivityServiceEvents,
102+
RootMessenger
103+
>({
104+
namespace: 'AccountActivityService',
105+
parent: rootMessenger,
106+
});
107+
108+
rootMessenger.delegate({
109+
actions: [
110+
'AccountsController:getSelectedAccount',
111+
'BackendWebSocketService:connect',
112+
'BackendWebSocketService:forceReconnection',
113+
'BackendWebSocketService:subscribe',
114+
'BackendWebSocketService:getConnectionInfo',
115+
'BackendWebSocketService:channelHasSubscription',
116+
'BackendWebSocketService:getSubscriptionsByChannel',
117+
'BackendWebSocketService:findSubscriptionsByChannelPrefix',
118+
'BackendWebSocketService:addChannelCallback',
119+
'BackendWebSocketService:removeChannelCallback',
120+
],
121+
events: [
122+
'AccountsController:selectedAccountChange',
123+
'BackendWebSocketService:connectionStateChanged',
124+
],
125+
messenger,
126+
});
70127

71128
// Create mock action handlers
72129
const mockGetSelectedAccount = jest.fn();
@@ -215,10 +272,7 @@ type WithServiceOptions = {
215272
type WithServiceCallback<ReturnValue> = (payload: {
216273
service: AccountActivityService;
217274
messenger: AccountActivityServiceMessenger;
218-
rootMessenger: Messenger<
219-
AccountActivityServiceAllowedActions,
220-
AccountActivityServiceAllowedEvents
221-
>;
275+
rootMessenger: RootMessenger;
222276
mocks: {
223277
getSelectedAccount: jest.Mock;
224278
connect: jest.Mock;
@@ -633,7 +687,7 @@ describe('AccountActivityService', () => {
633687
});
634688

635689
// Publish WebSocket ERROR state event - should flush tracked chains as down
636-
await rootMessenger.publish(
690+
rootMessenger.publish(
637691
'BackendWebSocketService:connectionStateChanged',
638692
{
639693
state: WebSocketState.ERROR,
@@ -667,7 +721,7 @@ describe('AccountActivityService', () => {
667721
mocks.getSelectedAccount.mockReturnValue(null);
668722

669723
// Publish WebSocket ERROR state event without any tracked chains
670-
await rootMessenger.publish(
724+
rootMessenger.publish(
671725
'BackendWebSocketService:connectionStateChanged',
672726
{
673727
state: WebSocketState.ERROR,
@@ -718,7 +772,7 @@ describe('AccountActivityService', () => {
718772
});
719773

720774
// Publish account change event - will be picked up by controller subscription
721-
await rootMessenger.publish(
775+
rootMessenger.publish(
722776
'AccountsController:selectedAccountChange',
723777
solanaAccount,
724778
);
@@ -748,7 +802,7 @@ describe('AccountActivityService', () => {
748802
});
749803

750804
// Publish account change event - will be picked up by controller subscription
751-
await rootMessenger.publish(
805+
rootMessenger.publish(
752806
'AccountsController:selectedAccountChange',
753807
unknownAccount,
754808
);
@@ -770,7 +824,7 @@ describe('AccountActivityService', () => {
770824
mocks.getSelectedAccount.mockReturnValue(null);
771825

772826
// Publish WebSocket connection event - will be picked up by controller subscription
773-
await rootMessenger.publish(
827+
rootMessenger.publish(
774828
'BackendWebSocketService:connectionStateChanged',
775829
{
776830
state: WebSocketState.CONNECTED,
@@ -810,7 +864,7 @@ describe('AccountActivityService', () => {
810864
});
811865

812866
// Publish account change event on root messenger
813-
await rootMessenger.publish(
867+
rootMessenger.publish(
814868
'AccountsController:selectedAccountChange',
815869
newAccount,
816870
);
@@ -848,7 +902,7 @@ describe('AccountActivityService', () => {
848902
});
849903

850904
// Publish account change event on root messenger
851-
await rootMessenger.publish(
905+
rootMessenger.publish(
852906
'AccountsController:selectedAccountChange',
853907
newAccount,
854908
);

packages/core-backend/src/AccountActivityService.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import type {
99
AccountsControllerGetSelectedAccountAction,
1010
AccountsControllerSelectedAccountChangeEvent,
1111
} from '@metamask/accounts-controller';
12-
import type { RestrictedMessenger } from '@metamask/base-controller';
1312
import type { TraceCallback } from '@metamask/controller-utils';
1413
import type { InternalAccount } from '@metamask/keyring-internal-api';
14+
import type { Messenger } from '@metamask/messenger';
1515

1616
import type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types';
1717
import type {
@@ -96,7 +96,7 @@ export const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [
9696
'BackendWebSocketService:connectionStateChanged',
9797
] as const;
9898

99-
export type AccountActivityServiceAllowedActions =
99+
export type AllowedActions =
100100
| AccountsControllerGetSelectedAccountAction
101101
| BackendWebSocketServiceMethodActions;
102102

@@ -134,16 +134,14 @@ export type AccountActivityServiceEvents =
134134
| AccountActivityServiceSubscriptionErrorEvent
135135
| AccountActivityServiceStatusChangedEvent;
136136

137-
export type AccountActivityServiceAllowedEvents =
137+
export type AllowedEvents =
138138
| AccountsControllerSelectedAccountChangeEvent
139139
| BackendWebSocketServiceConnectionStateChangedEvent;
140140

141-
export type AccountActivityServiceMessenger = RestrictedMessenger<
141+
export type AccountActivityServiceMessenger = Messenger<
142142
typeof SERVICE_NAME,
143-
AccountActivityServiceActions | AccountActivityServiceAllowedActions,
144-
AccountActivityServiceEvents | AccountActivityServiceAllowedEvents,
145-
AccountActivityServiceAllowedActions['type'],
146-
AccountActivityServiceAllowedEvents['type']
143+
AccountActivityServiceActions | AllowedActions,
144+
AccountActivityServiceEvents | AllowedEvents
147145
>;
148146

149147
// =============================================================================

packages/core-backend/src/BackendWebSocketService.test.ts

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import { Messenger } from '@metamask/base-controller';
1+
import {
2+
Messenger,
3+
MOCK_ANY_NAMESPACE,
4+
type MessengerActions,
5+
type MessengerEvents,
6+
type MockAnyNamespace,
7+
} from '@metamask/messenger';
28

39
import {
410
BackendWebSocketService,
511
getCloseReason,
612
WebSocketState,
713
type BackendWebSocketServiceOptions,
814
type BackendWebSocketServiceMessenger,
9-
type BackendWebSocketServiceActions,
10-
type BackendWebSocketServiceAllowedActions,
11-
type BackendWebSocketServiceEvents,
12-
type BackendWebSocketServiceAllowedEvents,
1315
} from './BackendWebSocketService';
1416
import { flushPromises } from '../../../tests/helpers';
1517

@@ -20,6 +22,18 @@ import { flushPromises } from '../../../tests/helpers';
2022
// Type for global object with WebSocket mock
2123
type GlobalWithWebSocket = typeof global & { lastWebSocket: MockWebSocket };
2224

25+
type AllBackendWebSocketServiceActions =
26+
MessengerActions<BackendWebSocketServiceMessenger>;
27+
28+
type AllBackendWebSocketServiceEvents =
29+
MessengerEvents<BackendWebSocketServiceMessenger>;
30+
31+
type RootMessenger = Messenger<
32+
MockAnyNamespace,
33+
AllBackendWebSocketServiceActions,
34+
AllBackendWebSocketServiceEvents
35+
>;
36+
2337
// =====================================================
2438
// MOCK WEBSOCKET CLASS
2539
// =====================================================
@@ -163,6 +177,17 @@ class MockWebSocket extends EventTarget {
163177
// TEST UTILITIES & MOCKS
164178
// =====================================================
165179

180+
/**
181+
* Creates and returns a root messenger for testing
182+
*
183+
* @returns A messenger instance
184+
*/
185+
function getRootMessenger(): RootMessenger {
186+
return new Messenger({
187+
namespace: MOCK_ANY_NAMESPACE,
188+
});
189+
}
190+
166191
/**
167192
* Creates a real messenger with registered mock actions for testing
168193
* Each call creates a completely independent messenger to ensure test isolation
@@ -171,19 +196,25 @@ class MockWebSocket extends EventTarget {
171196
*/
172197
const getMessenger = () => {
173198
// Create a unique root messenger for each test
174-
const rootMessenger = new Messenger<
175-
BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions,
176-
BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents
177-
>();
178-
const messenger = rootMessenger.getRestricted({
179-
name: 'BackendWebSocketService',
180-
allowedActions: ['AuthenticationController:getBearerToken'],
181-
allowedEvents: [
199+
const rootMessenger = getRootMessenger();
200+
const messenger = new Messenger<
201+
'BackendWebSocketService',
202+
AllBackendWebSocketServiceActions,
203+
AllBackendWebSocketServiceEvents,
204+
RootMessenger
205+
>({
206+
namespace: 'BackendWebSocketService',
207+
parent: rootMessenger,
208+
});
209+
rootMessenger.delegate({
210+
actions: ['AuthenticationController:getBearerToken'],
211+
events: [
182212
'AuthenticationController:stateChange',
183213
'KeyringController:lock',
184214
'KeyringController:unlock',
185215
],
186-
}) as unknown as BackendWebSocketServiceMessenger;
216+
messenger,
217+
});
187218

188219
// Create mock action handlers
189220
const mockGetBearerToken = jest.fn().mockResolvedValue('valid-default-token');
@@ -252,10 +283,7 @@ type TestSetupOptions = {
252283
type TestSetup = {
253284
service: BackendWebSocketService;
254285
messenger: BackendWebSocketServiceMessenger;
255-
rootMessenger: Messenger<
256-
BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions,
257-
BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents
258-
>;
286+
rootMessenger: RootMessenger;
259287
mocks: {
260288
getBearerToken: jest.Mock;
261289
};
@@ -270,10 +298,7 @@ type TestSetup = {
270298
type WithServiceCallback<ReturnValue> = (payload: {
271299
service: BackendWebSocketService;
272300
messenger: BackendWebSocketServiceMessenger;
273-
rootMessenger: Messenger<
274-
BackendWebSocketServiceActions | BackendWebSocketServiceAllowedActions,
275-
BackendWebSocketServiceEvents | BackendWebSocketServiceAllowedEvents
276-
>;
301+
rootMessenger: RootMessenger;
277302
mocks: {
278303
getBearerToken: jest.Mock;
279304
};

0 commit comments

Comments
 (0)