@metamask-previews/core-backend 1.0.1-preview-e7dac947 → 1.0.1-preview-5cf3ff79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,37 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Changed
11
-
12
- - `BackendWebSocketService` - Added optional `traceFn` parameter to constructor for performance tracing integration (e.g., Sentry)
13
- - Enables tracing of WebSocket operations including connect, disconnect methods
14
- - Trace function receives operation metadata and callback to wrap for performance monitoring
15
- - **BREAKING**: `BackendWebSocketService` - Simplified connection management and added KeyringController event integration ([#6819](https://github.com/MetaMask/core/pull/6819))
16
- - Added `KeyringController:lock` and `KeyringController:unlock` event subscriptions to automatically manage WebSocket connections based on wallet lock state
17
- - Renamed internal method `setupAuthentication()` to `subscribeEvents()` to reflect broader event handling responsibilities
18
- - Simplified reconnection logic: auto-reconnect on any unexpected disconnect, stay disconnected on manual disconnects (tracked via `#manualDisconnect` flag)
19
- - Removed `shouldReconnectOnClose()` method - reconnection decisions now based solely on manual disconnect flag rather than close codes
20
- - Updated `connect()` to reset manual disconnect flag, allowing reconnection after previous manual disconnects
21
- - Updated `disconnect()` to set manual disconnect flag, preventing automatic reconnection
22
- - Improved error handling in `connect()` to properly rethrow errors to callers
23
- - **BREAKING**: `AccountActivityService` - Replaced API-based chain support detection with system notification-driven chain tracking ([#6819](https://github.com/MetaMask/core/pull/6819))
24
- - Removed `getSupportedChains()` public method and all related API fetching logic
25
- - Removed hardcoded `DEFAULT_SUPPORTED_CHAINS` fallback list and cache expiration mechanism
26
- - Added internal `#chainsUp` Set to track chains reported as 'up' via system notifications
27
- - Updated system notification handler to dynamically track chain status (add to set when 'up', remove when 'down')
28
- - Updated WebSocket state change handler to flush all tracked chains as 'down' on disconnect/error (instead of using hardcoded list)
29
- - Chain status is now entirely driven by backend system notifications rather than proactive API calls
30
- - **BREAKING**: Updated `Transaction` type definition - renamed `hash` field to `id` for consistency with backend API ([#6819](https://github.com/MetaMask/core/pull/6819))
31
- - **BREAKING**: Updated `Asset` type definition - added required `decimals` field for proper token amount formatting ([#6819](https://github.com/MetaMask/core/pull/6819))
32
- - Updated documentation (README.md) to reflect new connection management model and chain tracking behavior ([#6819](https://github.com/MetaMask/core/pull/6819))
33
- - Added "WebSocket Connection Management" section explaining connection requirements and behavior
34
- - Updated sequence diagram to show system notification-driven chain status flow
35
- - Updated key flow characteristics to reflect internal chain tracking mechanism
36
-
37
- ### Removed
38
-
39
- - Removed `nock` test dependency - no longer needed after removing API-based chain support fetching ([#6819](https://github.com/MetaMask/core/pull/6819))
40
-
41
10
  ## [1.0.1]
42
11
 
43
12
  ### Changed
package/README.md CHANGED
@@ -16,9 +16,6 @@ Core backend services for MetaMask, serving as the data layer between Backend se
16
16
  - [Data Flow](#data-flow)
17
17
  - [Sequence Diagram: Real-time Account Activity Flow](#sequence-diagram-real-time-account-activity-flow)
18
18
  - [Key Flow Characteristics](#key-flow-characteristics)
19
- - [WebSocket Connection Management](#websocket-connection-management)
20
- - [Connection Requirements](#connection-requirements)
21
- - [Connection Behavior](#connection-behavior)
22
19
  - [API Reference](#api-reference)
23
20
  - [BackendWebSocketService](#backendwebsocketservice)
24
21
  - [Constructor Options](#constructor-options)
@@ -230,17 +227,16 @@ sequenceDiagram
230
227
  Backend->>WS: Connection established
231
228
  WS->>AA: WebSocket connection status notification<br/>(BackendWebSocketService:connectionStateChanged)<br/>{state: 'CONNECTED'}
232
229
 
233
- AA->>AA: call('AccountsController:getSelectedAccount')
234
- AA->>WS: subscribe({channels, callback})
235
- WS->>Backend: {event: 'subscribe', channels: ['account-activity.v1.eip155:0:0x123...']}
236
- Backend->>WS: {event: 'subscribe-response', subscriptionId: 'sub-456'}
237
-
238
- Note over WS,Backend: System notification sent automatically upon subscription
239
- Backend->>WS: {event: 'system-notification', data: {chainIds: ['eip155:1', 'eip155:137', ...], status: 'up'}}
240
- WS->>AA: System notification received
241
- AA->>AA: Track chains as 'up' internally
242
- AA->>TBC: Chain availability notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['0x1', '0x89', ...], status: 'up'}
243
- TBC->>TBC: Increase polling interval from 20s to 10min<br/>(.updateChainPollingConfigs({0x89: 600000}))
230
+ par StatusChanged Event
231
+ AA->>TBC: Chain availability notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['0x1', '0x89', ...], status: 'up'}
232
+ TBC->>TBC: Increase polling interval from 20s to 10min<br/>(.updateChainPollingConfigs({0x89: 600000}))
233
+ and Account Subscription
234
+ AA->>AA: call('AccountsController:getSelectedAccount')
235
+ AA->>WS: subscribe({channels, callback})
236
+ WS->>Backend: {event: 'subscribe', channels: ['account-activity.v1.eip155:0:0x123...']}
237
+ Backend->>WS: {event: 'subscribe-response', subscriptionId: 'sub-456'}
238
+ WS->>AA: Subscription sucessful
239
+ end
244
240
 
245
241
  Note over TBC,Backend: User Account Change
246
242
 
@@ -289,60 +285,24 @@ sequenceDiagram
289
285
  Note over TBC,Backend: Connection Health Management
290
286
 
291
287
  Backend-->>WS: Connection lost
292
- WS->>AA: WebSocket connection status notification<br/>(BackendWebSocketService:connectionStateChanged)<br/>{state: 'DISCONNECTED'}
293
- AA->>AA: Mark all tracked chains as 'down'<br/>(flush internal tracking set)
294
- AA->>TBC: Chain status notification for all tracked chains<br/>(AccountActivityService:statusChanged)<br/>{chainIds: ['0x1', '0x89', ...], status: 'down'}
295
- TBC->>TBC: Decrease polling interval from 10min to 20s<br/>(.updateChainPollingConfigs({0x89: 20000}))
288
+ WS->>TBC: WebSocket connection status notification<br/>(BackendWebSocketService:connectionStateChanged)<br/>{state: 'DISCONNECTED'}
289
+ TBC->>TBC: Decrease polling interval from 10min to 20s(.updateChainPollingConfigs({0x89: 20000}))
296
290
  TBC->>HTTP: Fetch balances immediately
297
291
  WS->>WS: Automatic reconnection<br/>with exponential backoff
298
- WS->>Backend: Reconnection successful
299
-
300
- Note over AA,Backend: Restart initial setup - resubscribe and get fresh chain status
301
- AA->>WS: subscribe (same account, new subscription)
302
- WS->>Backend: {event: 'subscribe', channels: ['account-activity.v1.eip155:0:0x123...']}
303
- Backend->>WS: {event: 'subscribe-response', subscriptionId: 'sub-999'}
304
- Backend->>WS: {event: 'system-notification', data: {chainIds: [...], status: 'up'}}
305
- WS->>AA: System notification received
306
- AA->>AA: Track chains as 'up' again
307
- AA->>TBC: Chain availability notification<br/>(AccountActivityService:statusChanged)<br/>{chainIds: [...], status: 'up'}
308
- TBC->>TBC: Increase polling interval back to 10min
292
+ WS->>Backend: Reconnection successful - Restart initial setup
309
293
  ```
310
294
 
311
295
  #### Key Flow Characteristics
312
296
 
313
- 1. **Initial Setup**: BackendWebSocketService establishes connection, then AccountActivityService subscribes to selected account. Backend automatically sends a system notification with all chains that are currently up. AccountActivityService tracks these chains internally and notifies TokenBalancesController, which increases polling interval to 5 min
314
- 2. **Chain Status Tracking**: AccountActivityService maintains an internal set of chains that are 'up' based on system notifications. On disconnect/error, it marks all tracked chains as 'down' before clearing the set
315
- 3. **System Notifications**: Backend automatically sends chain status updates (up/down) upon subscription and when status changes. AccountActivityService forwards these to TokenBalancesController, which adjusts polling intervals (up: 5min, down: 30s + immediate fetch)
316
- 4. **User Account Changes**: When users switch accounts, AccountActivityService unsubscribes from old account and subscribes to new account. Backend sends fresh system notification with current chain status for the new account
317
- 5. **Connection Resilience**: On reconnection, AccountActivityService resubscribes to selected account and receives fresh chain status via system notification. Automatic reconnection with exponential backoff
318
- 6. **Real-time Updates**: Backend pushes data through: Backend BackendWebSocketService AccountActivityService TokenBalancesController (+ future TransactionController integration)
319
- 7. **Parallel Processing**: Transaction and balance updates processed simultaneously - AccountActivityService publishes both transactionUpdated (future) and balanceUpdated events in parallel
320
- 8. **Direct Balance Processing**: Real-time balance updates bypass HTTP polling and update TokenBalancesController state directly
321
-
322
- ## WebSocket Connection Management
323
-
324
- ### Connection Requirements
325
-
326
- The WebSocket connects when **ALL 3 conditions are true**:
327
-
328
- 1. ✅ **Feature enabled** - `isEnabled()` callback returns `true` (feature flag)
329
- 2. ✅ **User signed in** - `AuthenticationController.isSignedIn = true`
330
- 3. ✅ **Wallet unlocked** - `KeyringController.isUnlocked = true`
331
-
332
- **Plus:** Platform code must call `connect()` when app opens/foregrounds and `disconnect()` when app closes/backgrounds.
333
-
334
- ### Connection Behavior
335
-
336
- **Idempotent `connect()`:**
337
-
338
- - Safe to call multiple times - validates conditions and returns early if already connected
339
- - Multiple rapid calls reuse the same connection promise (no duplicate connections)
340
- - No debouncing needed - handled automatically
341
-
342
- **Auto-Reconnect:**
343
-
344
- - ✅ **Unexpected disconnects** (network issues, server restart) → Auto-reconnect
345
- - ❌ **Manual disconnects** (app backgrounds, wallet locks, user signs out) → Stay disconnected
297
+ 1. **Initial Setup**: BackendWebSocketService establishes connection, then AccountActivityService simultaneously notifies all chains are up AND subscribes to selected account, TokenBalancesController increases polling interval to 10 min, then makes initial HTTP request for current balance state
298
+ 2. **User Account Changes**: When users switch accounts, AccountActivityService unsubscribes from old account, TokenBalancesController makes HTTP calls to fill data gaps, then AccountActivityService subscribes to new account
299
+ 3. **Real-time Updates**: Backend pushes data through: Backend BackendWebSocketService AccountActivityService TokenBalancesController (+ future TransactionController integration)
300
+ 4. **System Notifications**: Backend sends chain status updates (up/down) through WebSocket, AccountActivityService processes and forwards to TokenBalancesController which adjusts polling intervals and fetches balances immediately on chain down (chain down: 10min→20s + immediate fetch, chain up: 20s→10min)
301
+ 5. **Parallel Processing**: Transaction and balance updates processed simultaneously - AccountActivityService publishes both transactionUpdated (future) and balanceUpdated events in parallel
302
+ 6. **Dynamic Polling**: TokenBalancesController adjusts HTTP polling intervals based on WebSocket connection health (10 min when connected, 20s when disconnected)
303
+ 7. **Direct Balance Processing**: Real-time balance updates bypass HTTP polling and update TokenBalancesController state directly
304
+ 8. **Connection Resilience**: Automatic reconnection with resubscription to selected account
305
+ 9. **Ultra-Simple Error Handling**: Any error anywhere → force reconnection (no nested try-catch)
346
306
 
347
307
  ## API Reference
348
308
 
@@ -16,15 +16,52 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
16
16
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
17
17
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
18
18
  };
19
- var _AccountActivityService_instances, _AccountActivityService_messenger, _AccountActivityService_options, _AccountActivityService_chainsUp, _AccountActivityService_handleAccountActivityUpdate, _AccountActivityService_handleSelectedAccountChange, _AccountActivityService_handleSystemNotification, _AccountActivityService_handleWebSocketStateChange, _AccountActivityService_subscribeToSelectedAccount, _AccountActivityService_unsubscribeFromAllAccountActivity, _AccountActivityService_convertToCaip10Address, _AccountActivityService_forceReconnection;
19
+ var _AccountActivityService_instances, _AccountActivityService_messenger, _AccountActivityService_options, _AccountActivityService_supportedChains, _AccountActivityService_supportedChainsExpiresAt, _AccountActivityService_handleAccountActivityUpdate, _AccountActivityService_handleSelectedAccountChange, _AccountActivityService_handleSystemNotification, _AccountActivityService_handleWebSocketStateChange, _AccountActivityService_subscribeToSelectedAccount, _AccountActivityService_unsubscribeFromAllAccountActivity, _AccountActivityService_convertToCaip10Address, _AccountActivityService_forceReconnection;
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.AccountActivityService = exports.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = exports.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = void 0;
22
22
  const BackendWebSocketService_1 = require("./BackendWebSocketService.cjs");
23
23
  const logger_1 = require("./logger.cjs");
24
+ // =============================================================================
25
+ // Utility Functions
26
+ // =============================================================================
27
+ /**
28
+ * Fetches supported networks from the v2 API endpoint.
29
+ * Returns chain IDs already in CAIP-2 format.
30
+ *
31
+ * Note: This directly calls the Account API v2 endpoint. In the future, this should
32
+ * be moved to a dedicated data layer service for better separation of concerns.
33
+ *
34
+ * @returns Array of supported chain IDs in CAIP-2 format (e.g., "eip155:1")
35
+ */
36
+ async function fetchSupportedChainsInCaipFormat() {
37
+ const url = 'https://accounts.api.cx.metamask.io/v2/supportedNetworks';
38
+ const response = await fetch(url);
39
+ if (!response.ok) {
40
+ throw new Error(`Failed to fetch supported networks: ${response.status} ${response.statusText}`);
41
+ }
42
+ const data = await response.json();
43
+ // v2 endpoint already returns data in CAIP-2 format
44
+ return data.fullSupport;
45
+ }
24
46
  const SERVICE_NAME = 'AccountActivityService';
25
47
  const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, SERVICE_NAME);
26
48
  const MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'];
49
+ // Default supported chains used as fallback when API is unavailable
50
+ // This list should match the expected chains from the accounts API v2/supportedNetworks endpoint
51
+ const DEFAULT_SUPPORTED_CHAINS = [
52
+ 'eip155:1',
53
+ 'eip155:137',
54
+ 'eip155:56',
55
+ 'eip155:59144',
56
+ 'eip155:8453',
57
+ 'eip155:10',
58
+ 'eip155:42161',
59
+ 'eip155:534352',
60
+ 'eip155:1329', // Sei
61
+ ];
27
62
  const SUBSCRIPTION_NAMESPACE = 'account-activity.v1';
63
+ // Cache TTL for supported chains (5 hours in milliseconds)
64
+ const SUPPORTED_CHAINS_CACHE_TTL = 5 * 60 * 60 * 1000;
28
65
  // Allowed actions that AccountActivityService can call on other controllers
29
66
  exports.ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = [
30
67
  'AccountsController:getSelectedAccount',
@@ -97,8 +134,8 @@ class AccountActivityService {
97
134
  this.name = SERVICE_NAME;
98
135
  _AccountActivityService_messenger.set(this, void 0);
99
136
  _AccountActivityService_options.set(this, void 0);
100
- // Track chains that are currently up (based on system notifications)
101
- _AccountActivityService_chainsUp.set(this, new Set());
137
+ _AccountActivityService_supportedChains.set(this, null);
138
+ _AccountActivityService_supportedChainsExpiresAt.set(this, 0);
102
139
  __classPrivateFieldSet(this, _AccountActivityService_messenger, options.messenger, "f");
103
140
  // Set configuration with defaults
104
141
  __classPrivateFieldSet(this, _AccountActivityService_options, {
@@ -109,10 +146,36 @@ class AccountActivityService {
109
146
  __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").subscribe('BackendWebSocketService:connectionStateChanged', (connectionInfo) => __classPrivateFieldGet(this, _AccountActivityService_instances, "m", _AccountActivityService_handleWebSocketStateChange).call(this, connectionInfo));
110
147
  __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").call('BackendWebSocketService:addChannelCallback', {
111
148
  channelName: `system-notifications.v1.${__classPrivateFieldGet(this, _AccountActivityService_options, "f").subscriptionNamespace}`,
112
- callback: (notification) => __classPrivateFieldGet(this, _AccountActivityService_instances, "m", _AccountActivityService_handleSystemNotification).call(this, notification),
149
+ callback: (notification) => __classPrivateFieldGet(this, _AccountActivityService_instances, "m", _AccountActivityService_handleSystemNotification).call(this, notification.data),
113
150
  });
114
151
  }
115
152
  // =============================================================================
153
+ // Public Methods - Chain Management
154
+ // =============================================================================
155
+ /**
156
+ * Fetch supported chains from API with fallback to hardcoded list.
157
+ * Uses expiry-based caching with TTL to prevent stale data.
158
+ *
159
+ * @returns Array of supported chain IDs in CAIP-2 format
160
+ */
161
+ async getSupportedChains() {
162
+ // Return cached result if available and not expired
163
+ if (__classPrivateFieldGet(this, _AccountActivityService_supportedChains, "f") !== null &&
164
+ Date.now() < __classPrivateFieldGet(this, _AccountActivityService_supportedChainsExpiresAt, "f")) {
165
+ return __classPrivateFieldGet(this, _AccountActivityService_supportedChains, "f");
166
+ }
167
+ try {
168
+ // Try to fetch from API
169
+ __classPrivateFieldSet(this, _AccountActivityService_supportedChains, await fetchSupportedChainsInCaipFormat(), "f");
170
+ }
171
+ catch {
172
+ // Fallback to hardcoded list and cache it with timestamp
173
+ __classPrivateFieldSet(this, _AccountActivityService_supportedChains, Array.from(DEFAULT_SUPPORTED_CHAINS), "f");
174
+ }
175
+ __classPrivateFieldSet(this, _AccountActivityService_supportedChainsExpiresAt, Date.now() + SUPPORTED_CHAINS_CACHE_TTL, "f");
176
+ return __classPrivateFieldGet(this, _AccountActivityService_supportedChains, "f");
177
+ }
178
+ // =============================================================================
116
179
  // Account Subscription Methods
117
180
  // =============================================================================
118
181
  /**
@@ -133,7 +196,6 @@ class AccountActivityService {
133
196
  // Create subscription using the proper subscribe method (this will be stored in WebSocketService's internal tracking)
134
197
  await __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").call('BackendWebSocketService:subscribe', {
135
198
  channels: [channel],
136
- channelType: __classPrivateFieldGet(this, _AccountActivityService_options, "f").subscriptionNamespace,
137
199
  callback: (notification) => {
138
200
  __classPrivateFieldGet(this, _AccountActivityService_instances, "m", _AccountActivityService_handleAccountActivityUpdate).call(this, notification.data);
139
201
  },
@@ -183,7 +245,7 @@ class AccountActivityService {
183
245
  }
184
246
  }
185
247
  exports.AccountActivityService = AccountActivityService;
186
- _AccountActivityService_messenger = new WeakMap(), _AccountActivityService_options = new WeakMap(), _AccountActivityService_chainsUp = new WeakMap(), _AccountActivityService_instances = new WeakSet(), _AccountActivityService_handleAccountActivityUpdate = function _AccountActivityService_handleAccountActivityUpdate(payload) {
248
+ _AccountActivityService_messenger = new WeakMap(), _AccountActivityService_options = new WeakMap(), _AccountActivityService_supportedChains = new WeakMap(), _AccountActivityService_supportedChainsExpiresAt = new WeakMap(), _AccountActivityService_instances = new WeakSet(), _AccountActivityService_handleAccountActivityUpdate = function _AccountActivityService_handleAccountActivityUpdate(payload) {
187
249
  const { address, tx, updates } = payload;
188
250
  log('Handling account activity update', {
189
251
  address,
@@ -218,29 +280,15 @@ async function _AccountActivityService_handleSelectedAccountChange(newAccount) {
218
280
  catch (error) {
219
281
  log('Account change failed', { error });
220
282
  }
221
- }, _AccountActivityService_handleSystemNotification = function _AccountActivityService_handleSystemNotification(notification) {
222
- const data = notification.data;
223
- const { timestamp } = notification;
283
+ }, _AccountActivityService_handleSystemNotification = function _AccountActivityService_handleSystemNotification(data) {
224
284
  // Validate required fields
225
285
  if (!data.chainIds || !Array.isArray(data.chainIds) || !data.status) {
226
286
  throw new Error('Invalid system notification data: missing chainIds or status');
227
287
  }
228
- // Track chain status
229
- if (data.status === 'up') {
230
- for (const chainId of data.chainIds) {
231
- __classPrivateFieldGet(this, _AccountActivityService_chainsUp, "f").add(chainId);
232
- }
233
- }
234
- else {
235
- for (const chainId of data.chainIds) {
236
- __classPrivateFieldGet(this, _AccountActivityService_chainsUp, "f").delete(chainId);
237
- }
238
- }
239
288
  // Publish status change directly (delta update)
240
289
  __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").publish(`AccountActivityService:statusChanged`, {
241
290
  chainIds: data.chainIds,
242
291
  status: data.status,
243
- timestamp,
244
292
  });
245
293
  }, _AccountActivityService_handleWebSocketStateChange =
246
294
  /**
@@ -250,27 +298,30 @@ async function _AccountActivityService_handleSelectedAccountChange(newAccount) {
250
298
  */
251
299
  async function _AccountActivityService_handleWebSocketStateChange(connectionInfo) {
252
300
  const { state } = connectionInfo;
301
+ const supportedChains = await this.getSupportedChains();
253
302
  if (state === BackendWebSocketService_1.WebSocketState.CONNECTED) {
254
- // WebSocket connected - resubscribe to selected account
255
- // The system notification will automatically provide the list of chains that are up
303
+ // WebSocket connected - resubscribe and set all chains as up
256
304
  await __classPrivateFieldGet(this, _AccountActivityService_instances, "m", _AccountActivityService_subscribeToSelectedAccount).call(this);
305
+ // Publish initial status - all supported chains are up when WebSocket connects
306
+ __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").publish(`AccountActivityService:statusChanged`, {
307
+ chainIds: supportedChains,
308
+ status: 'up',
309
+ });
310
+ log('WebSocket connected - Published all chains as up', {
311
+ count: supportedChains.length,
312
+ chains: supportedChains,
313
+ });
257
314
  }
258
315
  else if (state === BackendWebSocketService_1.WebSocketState.DISCONNECTED ||
259
316
  state === BackendWebSocketService_1.WebSocketState.ERROR) {
260
- // On disconnect/error, flush all tracked chains as down
261
- const chainsToMarkDown = Array.from(__classPrivateFieldGet(this, _AccountActivityService_chainsUp, "f"));
262
- if (chainsToMarkDown.length > 0) {
263
- __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").publish(`AccountActivityService:statusChanged`, {
264
- chainIds: chainsToMarkDown,
265
- status: 'down',
266
- });
267
- log('WebSocket error/disconnection - Published tracked chains as down', {
268
- count: chainsToMarkDown.length,
269
- chains: chainsToMarkDown,
270
- });
271
- // Clear the tracking set since all chains are now down
272
- __classPrivateFieldGet(this, _AccountActivityService_chainsUp, "f").clear();
273
- }
317
+ __classPrivateFieldGet(this, _AccountActivityService_messenger, "f").publish(`AccountActivityService:statusChanged`, {
318
+ chainIds: supportedChains,
319
+ status: 'down',
320
+ });
321
+ log('WebSocket error/disconnection - Published all chains as down', {
322
+ count: supportedChains.length,
323
+ chains: supportedChains,
324
+ });
274
325
  }
275
326
  }, _AccountActivityService_subscribeToSelectedAccount =
276
327
  // =============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"AccountActivityService.cjs","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Account Activity Service for monitoring account transactions and balance changes\n *\n * This service subscribes to account activity and receives all transactions\n * and balance updates for those accounts via the comprehensive AccountActivityMessage format.\n */\n\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types';\nimport type {\n WebSocketConnectionInfo,\n BackendWebSocketServiceConnectionStateChangedEvent,\n ServerNotificationMessage,\n} from './BackendWebSocketService';\nimport { WebSocketState } from './BackendWebSocketService';\nimport type { BackendWebSocketServiceMethodActions } from './BackendWebSocketService-method-action-types';\nimport { projectLogger, createModuleLogger } from './logger';\nimport type {\n Transaction,\n AccountActivityMessage,\n BalanceUpdate,\n} from './types';\n\n// =============================================================================\n// Types and Constants\n// =============================================================================\n\n/**\n * System notification data for chain status updates\n */\nexport type SystemNotificationData = {\n /** Array of chain IDs affected (e.g., ['eip155:137', 'eip155:1']) */\n chainIds: string[];\n /** Status of the chains: 'down' or 'up' */\n status: 'down' | 'up';\n /** Timestamp of the notification */\n timestamp?: number;\n};\n\nconst SERVICE_NAME = 'AccountActivityService';\n\nconst log = createModuleLogger(projectLogger, SERVICE_NAME);\n\nconst MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'] as const;\n\nconst SUBSCRIPTION_NAMESPACE = 'account-activity.v1';\n\n/**\n * Account subscription options\n */\nexport type SubscriptionOptions = {\n address: string; // Should be in CAIP-10 format, e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\"\n};\n\n/**\n * Configuration options for the account activity service\n */\nexport type AccountActivityServiceOptions = {\n /** Custom subscription namespace (default: 'account-activity.v1') */\n subscriptionNamespace?: string;\n};\n\n// =============================================================================\n// Action and Event Types\n// =============================================================================\n\n// Action types for the messaging system - using generated method actions\nexport type AccountActivityServiceActions = AccountActivityServiceMethodActions;\n\n// Allowed actions that AccountActivityService can call on other controllers\nexport const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = [\n 'AccountsController:getSelectedAccount',\n 'BackendWebSocketService:connect',\n 'BackendWebSocketService:disconnect',\n 'BackendWebSocketService:subscribe',\n 'BackendWebSocketService:getConnectionInfo',\n 'BackendWebSocketService:channelHasSubscription',\n 'BackendWebSocketService:getSubscriptionsByChannel',\n 'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n 'BackendWebSocketService:addChannelCallback',\n 'BackendWebSocketService:removeChannelCallback',\n] as const;\n\n// Allowed events that AccountActivityService can listen to\nexport const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [\n 'AccountsController:selectedAccountChange',\n 'BackendWebSocketService:connectionStateChanged',\n] as const;\n\nexport type AccountActivityServiceAllowedActions =\n | AccountsControllerGetSelectedAccountAction\n | BackendWebSocketServiceMethodActions;\n\n// Event types for the messaging system\n\nexport type AccountActivityServiceTransactionUpdatedEvent = {\n type: `AccountActivityService:transactionUpdated`;\n payload: [Transaction];\n};\n\nexport type AccountActivityServiceBalanceUpdatedEvent = {\n type: `AccountActivityService:balanceUpdated`;\n payload: [{ address: string; chain: string; updates: BalanceUpdate[] }];\n};\n\nexport type AccountActivityServiceSubscriptionErrorEvent = {\n type: `AccountActivityService:subscriptionError`;\n payload: [{ addresses: string[]; error: string; operation: string }];\n};\n\nexport type AccountActivityServiceStatusChangedEvent = {\n type: `AccountActivityService:statusChanged`;\n payload: [\n {\n chainIds: string[];\n status: 'up' | 'down';\n timestamp?: number;\n },\n ];\n};\n\nexport type AccountActivityServiceEvents =\n | AccountActivityServiceTransactionUpdatedEvent\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceSubscriptionErrorEvent\n | AccountActivityServiceStatusChangedEvent;\n\nexport type AccountActivityServiceAllowedEvents =\n | AccountsControllerSelectedAccountChangeEvent\n | BackendWebSocketServiceConnectionStateChangedEvent;\n\nexport type AccountActivityServiceMessenger = RestrictedMessenger<\n typeof SERVICE_NAME,\n AccountActivityServiceActions | AccountActivityServiceAllowedActions,\n AccountActivityServiceEvents | AccountActivityServiceAllowedEvents,\n AccountActivityServiceAllowedActions['type'],\n AccountActivityServiceAllowedEvents['type']\n>;\n\n// =============================================================================\n// Main Service Class\n// =============================================================================\n\n/**\n * High-performance service for real-time account activity monitoring using optimized\n * WebSocket subscriptions with direct callback routing. Automatically subscribes to\n * the currently selected account and switches subscriptions when the selected account changes.\n * Receives transactions and balance updates using the comprehensive AccountActivityMessage format.\n *\n * Performance Features:\n * - Direct callback routing (no EventEmitter overhead)\n * - Minimal subscription tracking (no duplication with BackendWebSocketService)\n * - Optimized cleanup for mobile environments\n * - Single-account subscription (only selected account)\n * - Comprehensive balance updates with transfer tracking\n *\n * Architecture:\n * - Uses messenger pattern to communicate with BackendWebSocketService\n * - AccountActivityService tracks channel-to-subscriptionId mappings via messenger calls\n * - Automatically subscribes to selected account on initialization\n * - Switches subscriptions when selected account changes\n * - No direct dependency on BackendWebSocketService (uses messenger instead)\n *\n * @example\n * ```typescript\n * const service = new AccountActivityService({\n * messenger: activityMessenger,\n * });\n *\n * // Service automatically subscribes to the currently selected account\n * // When user switches accounts, service automatically resubscribes\n *\n * // All transactions and balance updates are received via optimized\n * // WebSocket callbacks and processed with zero-allocation routing\n * // Balance updates include comprehensive transfer details and post-transaction balances\n * ```\n */\nexport class AccountActivityService {\n /**\n * The name of the service.\n */\n readonly name = SERVICE_NAME;\n\n readonly #messenger: AccountActivityServiceMessenger;\n\n readonly #options: Required<AccountActivityServiceOptions>;\n\n // Track chains that are currently up (based on system notifications)\n readonly #chainsUp: Set<string> = new Set();\n\n // =============================================================================\n // Constructor and Initialization\n // =============================================================================\n\n /**\n * Creates a new Account Activity service instance\n *\n * @param options - Configuration options including messenger\n */\n constructor(\n options: AccountActivityServiceOptions & {\n messenger: AccountActivityServiceMessenger;\n },\n ) {\n this.#messenger = options.messenger;\n\n // Set configuration with defaults\n this.#options = {\n subscriptionNamespace:\n options.subscriptionNamespace ?? SUBSCRIPTION_NAMESPACE,\n };\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n this.#messenger.subscribe(\n 'AccountsController:selectedAccountChange',\n async (account: InternalAccount) =>\n await this.#handleSelectedAccountChange(account),\n );\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n (connectionInfo: WebSocketConnectionInfo) =>\n this.#handleWebSocketStateChange(connectionInfo),\n );\n this.#messenger.call('BackendWebSocketService:addChannelCallback', {\n channelName: `system-notifications.v1.${this.#options.subscriptionNamespace}`,\n callback: (notification: ServerNotificationMessage) =>\n this.#handleSystemNotification(notification),\n });\n }\n\n // =============================================================================\n // Account Subscription Methods\n // =============================================================================\n\n /**\n * Subscribe to account activity (transactions and balance updates)\n * Address should be in CAIP-10 format (e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\")\n *\n * @param subscription - Account subscription configuration with address\n */\n async subscribe(subscription: SubscriptionOptions): Promise<void> {\n try {\n await this.#messenger.call('BackendWebSocketService:connect');\n\n // Create channel name from address\n const channel = `${this.#options.subscriptionNamespace}.${subscription.address}`;\n\n // Check if already subscribed\n if (\n this.#messenger.call(\n 'BackendWebSocketService:channelHasSubscription',\n channel,\n )\n ) {\n return;\n }\n\n // Create subscription using the proper subscribe method (this will be stored in WebSocketService's internal tracking)\n await this.#messenger.call('BackendWebSocketService:subscribe', {\n channels: [channel],\n channelType: this.#options.subscriptionNamespace, // e.g., 'account-activity.v1'\n callback: (notification: ServerNotificationMessage) => {\n this.#handleAccountActivityUpdate(\n notification.data as AccountActivityMessage,\n );\n },\n });\n } catch (error) {\n log('Subscription failed, forcing reconnection', { error });\n await this.#forceReconnection();\n }\n }\n\n /**\n * Unsubscribe from account activity for specified address\n * Address should be in CAIP-10 format (e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\")\n *\n * @param subscription - Account subscription configuration with address to unsubscribe\n */\n async unsubscribe(subscription: SubscriptionOptions): Promise<void> {\n const { address } = subscription;\n try {\n // Find channel for the specified address\n const channel = `${this.#options.subscriptionNamespace}.${address}`;\n const subscriptions = this.#messenger.call(\n 'BackendWebSocketService:getSubscriptionsByChannel',\n channel,\n );\n\n if (subscriptions.length === 0) {\n return;\n }\n\n // Fast path: Direct unsubscribe using stored unsubscribe function\n // Unsubscribe from all matching subscriptions\n for (const subscriptionInfo of subscriptions) {\n await subscriptionInfo.unsubscribe();\n }\n } catch (error) {\n log('Unsubscription failed, forcing reconnection', { error });\n await this.#forceReconnection();\n }\n }\n\n // =============================================================================\n // Private Methods - Event Handlers\n // =============================================================================\n\n /**\n * Handle account activity updates (transactions + balance changes)\n * Processes the comprehensive AccountActivityMessage format with detailed balance updates and transfers\n *\n * @param payload - The account activity message containing transaction and balance updates\n * @example AccountActivityMessage format handling:\n * Input: {\n * address: \"0xd14b52362b5b777ffa754c666ddec6722aaeee08\",\n * tx: { id: \"0x1cde...\", chain: \"eip155:8453\", status: \"confirmed\", timestamp: 1760099871, ... },\n * updates: [{\n * asset: { fungible: true, type: \"eip155:8453/erc20:0x833...\", unit: \"USDC\", decimals: 6 },\n * postBalance: { amount: \"0xc350\" },\n * transfers: [{ from: \"0x7b07...\", to: \"0xd14b...\", amount: \"0x2710\" }]\n * }]\n * }\n * Output: Transaction and balance updates published separately\n */\n #handleAccountActivityUpdate(payload: AccountActivityMessage): void {\n const { address, tx, updates } = payload;\n\n log('Handling account activity update', {\n address,\n updateCount: updates.length,\n });\n\n // Process transaction update\n this.#messenger.publish(`AccountActivityService:transactionUpdated`, tx);\n\n // Publish comprehensive balance updates with transfer details\n this.#messenger.publish(`AccountActivityService:balanceUpdated`, {\n address,\n chain: tx.chain,\n updates,\n });\n }\n\n /**\n * Handle selected account change event\n *\n * @param newAccount - The newly selected account\n */\n async #handleSelectedAccountChange(\n newAccount: InternalAccount | null,\n ): Promise<void> {\n if (!newAccount?.address) {\n return;\n }\n\n try {\n // Convert new account to CAIP-10 format\n const newAddress = this.#convertToCaip10Address(newAccount);\n\n // First, unsubscribe from all current account activity subscriptions to avoid multiple subscriptions\n await this.#unsubscribeFromAllAccountActivity();\n\n // Then, subscribe to the new selected account\n await this.subscribe({ address: newAddress });\n } catch (error) {\n log('Account change failed', { error });\n }\n }\n\n /**\n * Handle system notification for chain status changes\n * Publishes only the status change (delta) for affected chains\n *\n * @param notification - Server notification message containing chain status updates and timestamp\n */\n #handleSystemNotification(notification: ServerNotificationMessage): void {\n const data = notification.data as SystemNotificationData;\n const { timestamp } = notification;\n\n // Validate required fields\n if (!data.chainIds || !Array.isArray(data.chainIds) || !data.status) {\n throw new Error(\n 'Invalid system notification data: missing chainIds or status',\n );\n }\n\n // Track chain status\n if (data.status === 'up') {\n for (const chainId of data.chainIds) {\n this.#chainsUp.add(chainId);\n }\n } else {\n for (const chainId of data.chainIds) {\n this.#chainsUp.delete(chainId);\n }\n }\n\n // Publish status change directly (delta update)\n this.#messenger.publish(`AccountActivityService:statusChanged`, {\n chainIds: data.chainIds,\n status: data.status,\n timestamp,\n });\n }\n\n /**\n * Handle WebSocket connection state changes for fallback polling and resubscription\n *\n * @param connectionInfo - WebSocket connection state information\n */\n async #handleWebSocketStateChange(\n connectionInfo: WebSocketConnectionInfo,\n ): Promise<void> {\n const { state } = connectionInfo;\n\n if (state === WebSocketState.CONNECTED) {\n // WebSocket connected - resubscribe to selected account\n // The system notification will automatically provide the list of chains that are up\n await this.#subscribeToSelectedAccount();\n } else if (\n state === WebSocketState.DISCONNECTED ||\n state === WebSocketState.ERROR\n ) {\n // On disconnect/error, flush all tracked chains as down\n const chainsToMarkDown = Array.from(this.#chainsUp);\n\n if (chainsToMarkDown.length > 0) {\n this.#messenger.publish(`AccountActivityService:statusChanged`, {\n chainIds: chainsToMarkDown,\n status: 'down',\n });\n\n log(\n 'WebSocket error/disconnection - Published tracked chains as down',\n {\n count: chainsToMarkDown.length,\n chains: chainsToMarkDown,\n },\n );\n\n // Clear the tracking set since all chains are now down\n this.#chainsUp.clear();\n }\n }\n }\n\n // =============================================================================\n // Private Methods - Subscription Management\n // =============================================================================\n\n /**\n * Subscribe to the currently selected account only\n */\n async #subscribeToSelectedAccount(): Promise<void> {\n const selectedAccount = this.#messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n\n if (!selectedAccount || !selectedAccount.address) {\n return;\n }\n\n // Convert to CAIP-10 format and subscribe\n const address = this.#convertToCaip10Address(selectedAccount);\n await this.subscribe({ address });\n }\n\n /**\n * Unsubscribe from all account activity subscriptions for this service\n * Finds all channels matching the service's namespace and unsubscribes from them\n */\n async #unsubscribeFromAllAccountActivity(): Promise<void> {\n const accountActivitySubscriptions = this.#messenger.call(\n 'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n this.#options.subscriptionNamespace,\n );\n\n // Unsubscribe from all matching subscriptions\n for (const subscription of accountActivitySubscriptions) {\n await subscription.unsubscribe();\n }\n }\n\n // =============================================================================\n // Private Methods - Utility Functions\n // =============================================================================\n\n /**\n * Convert an InternalAccount address to CAIP-10 format or raw address\n *\n * @param account - The internal account to convert\n * @returns The CAIP-10 formatted address or raw address\n */\n #convertToCaip10Address(account: InternalAccount): string {\n // Check if account has EVM scopes\n if (account.scopes.some((scope) => scope.startsWith('eip155:'))) {\n // CAIP-10 format: eip155:0:address (subscribe to all EVM chains)\n return `eip155:0:${account.address}`;\n }\n\n // Check if account has Solana scopes\n if (account.scopes.some((scope) => scope.startsWith('solana:'))) {\n // CAIP-10 format: solana:0:address (subscribe to all Solana chains)\n return `solana:0:${account.address}`;\n }\n\n // For other chains or unknown scopes, return raw address\n return account.address;\n }\n\n /**\n * Force WebSocket reconnection to clean up subscription state\n */\n async #forceReconnection(): Promise<void> {\n try {\n log('Forcing WebSocket reconnection to clean up subscription state');\n\n // All subscriptions will be cleaned up automatically on WebSocket disconnect\n\n await this.#messenger.call('BackendWebSocketService:disconnect');\n await this.#messenger.call('BackendWebSocketService:connect');\n } catch (error) {\n log('Failed to force WebSocket reconnection', { error });\n }\n }\n\n // =============================================================================\n // Public Methods - Cleanup\n // =============================================================================\n\n /**\n * Destroy the service and clean up all resources\n * Optimized for fast cleanup during service destruction or mobile app termination\n */\n destroy(): void {\n // Clean up system notification callback\n this.#messenger.call(\n 'BackendWebSocketService:removeChannelCallback',\n `system-notifications.v1.${this.#options.subscriptionNamespace}`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"AccountActivityService.cjs","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Account Activity Service for monitoring account transactions and balance changes\n *\n * This service subscribes to account activity and receives all transactions\n * and balance updates for those accounts via the comprehensive AccountActivityMessage format.\n */\n\nimport type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type { RestrictedMessenger } from '@metamask/base-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport type { AccountActivityServiceMethodActions } from './AccountActivityService-method-action-types';\nimport type {\n WebSocketConnectionInfo,\n BackendWebSocketServiceConnectionStateChangedEvent,\n ServerNotificationMessage,\n} from './BackendWebSocketService';\nimport { WebSocketState } from './BackendWebSocketService';\nimport type { BackendWebSocketServiceMethodActions } from './BackendWebSocketService-method-action-types';\nimport { projectLogger, createModuleLogger } from './logger';\nimport type {\n Transaction,\n AccountActivityMessage,\n BalanceUpdate,\n} from './types';\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Fetches supported networks from the v2 API endpoint.\n * Returns chain IDs already in CAIP-2 format.\n *\n * Note: This directly calls the Account API v2 endpoint. In the future, this should\n * be moved to a dedicated data layer service for better separation of concerns.\n *\n * @returns Array of supported chain IDs in CAIP-2 format (e.g., \"eip155:1\")\n */\nasync function fetchSupportedChainsInCaipFormat(): Promise<string[]> {\n const url = 'https://accounts.api.cx.metamask.io/v2/supportedNetworks';\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch supported networks: ${response.status} ${response.statusText}`,\n );\n }\n\n const data: {\n fullSupport: string[];\n partialSupport: { balances: string[] };\n } = await response.json();\n\n // v2 endpoint already returns data in CAIP-2 format\n return data.fullSupport;\n}\n\n// =============================================================================\n// Types and Constants\n// =============================================================================\n\n/**\n * System notification data for chain status updates\n */\nexport type SystemNotificationData = {\n /** Array of chain IDs affected (e.g., ['eip155:137', 'eip155:1']) */\n chainIds: string[];\n /** Status of the chains: 'down' or 'up' */\n status: 'down' | 'up';\n};\n\nconst SERVICE_NAME = 'AccountActivityService';\n\nconst log = createModuleLogger(projectLogger, SERVICE_NAME);\n\nconst MESSENGER_EXPOSED_METHODS = ['subscribe', 'unsubscribe'] as const;\n\n// Default supported chains used as fallback when API is unavailable\n// This list should match the expected chains from the accounts API v2/supportedNetworks endpoint\nconst DEFAULT_SUPPORTED_CHAINS = [\n 'eip155:1', // Ethereum Mainnet\n 'eip155:137', // Polygon\n 'eip155:56', // BSC\n 'eip155:59144', // Linea\n 'eip155:8453', // Base\n 'eip155:10', // Optimism\n 'eip155:42161', // Arbitrum One\n 'eip155:534352', // Scroll\n 'eip155:1329', // Sei\n];\nconst SUBSCRIPTION_NAMESPACE = 'account-activity.v1';\n\n// Cache TTL for supported chains (5 hours in milliseconds)\nconst SUPPORTED_CHAINS_CACHE_TTL = 5 * 60 * 60 * 1000;\n\n/**\n * Account subscription options\n */\nexport type SubscriptionOptions = {\n address: string; // Should be in CAIP-10 format, e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\"\n};\n\n/**\n * Configuration options for the account activity service\n */\nexport type AccountActivityServiceOptions = {\n /** Custom subscription namespace (default: 'account-activity.v1') */\n subscriptionNamespace?: string;\n};\n\n// =============================================================================\n// Action and Event Types\n// =============================================================================\n\n// Action types for the messaging system - using generated method actions\nexport type AccountActivityServiceActions = AccountActivityServiceMethodActions;\n\n// Allowed actions that AccountActivityService can call on other controllers\nexport const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS = [\n 'AccountsController:getSelectedAccount',\n 'BackendWebSocketService:connect',\n 'BackendWebSocketService:disconnect',\n 'BackendWebSocketService:subscribe',\n 'BackendWebSocketService:getConnectionInfo',\n 'BackendWebSocketService:channelHasSubscription',\n 'BackendWebSocketService:getSubscriptionsByChannel',\n 'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n 'BackendWebSocketService:addChannelCallback',\n 'BackendWebSocketService:removeChannelCallback',\n] as const;\n\n// Allowed events that AccountActivityService can listen to\nexport const ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS = [\n 'AccountsController:selectedAccountChange',\n 'BackendWebSocketService:connectionStateChanged',\n] as const;\n\nexport type AccountActivityServiceAllowedActions =\n | AccountsControllerGetSelectedAccountAction\n | BackendWebSocketServiceMethodActions;\n\n// Event types for the messaging system\n\nexport type AccountActivityServiceTransactionUpdatedEvent = {\n type: `AccountActivityService:transactionUpdated`;\n payload: [Transaction];\n};\n\nexport type AccountActivityServiceBalanceUpdatedEvent = {\n type: `AccountActivityService:balanceUpdated`;\n payload: [{ address: string; chain: string; updates: BalanceUpdate[] }];\n};\n\nexport type AccountActivityServiceSubscriptionErrorEvent = {\n type: `AccountActivityService:subscriptionError`;\n payload: [{ addresses: string[]; error: string; operation: string }];\n};\n\nexport type AccountActivityServiceStatusChangedEvent = {\n type: `AccountActivityService:statusChanged`;\n payload: [\n {\n chainIds: string[];\n status: 'up' | 'down';\n },\n ];\n};\n\nexport type AccountActivityServiceEvents =\n | AccountActivityServiceTransactionUpdatedEvent\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceSubscriptionErrorEvent\n | AccountActivityServiceStatusChangedEvent;\n\nexport type AccountActivityServiceAllowedEvents =\n | AccountsControllerSelectedAccountChangeEvent\n | BackendWebSocketServiceConnectionStateChangedEvent;\n\nexport type AccountActivityServiceMessenger = RestrictedMessenger<\n typeof SERVICE_NAME,\n AccountActivityServiceActions | AccountActivityServiceAllowedActions,\n AccountActivityServiceEvents | AccountActivityServiceAllowedEvents,\n AccountActivityServiceAllowedActions['type'],\n AccountActivityServiceAllowedEvents['type']\n>;\n\n// =============================================================================\n// Main Service Class\n// =============================================================================\n\n/**\n * High-performance service for real-time account activity monitoring using optimized\n * WebSocket subscriptions with direct callback routing. Automatically subscribes to\n * the currently selected account and switches subscriptions when the selected account changes.\n * Receives transactions and balance updates using the comprehensive AccountActivityMessage format.\n *\n * Performance Features:\n * - Direct callback routing (no EventEmitter overhead)\n * - Minimal subscription tracking (no duplication with BackendWebSocketService)\n * - Optimized cleanup for mobile environments\n * - Single-account subscription (only selected account)\n * - Comprehensive balance updates with transfer tracking\n *\n * Architecture:\n * - Uses messenger pattern to communicate with BackendWebSocketService\n * - AccountActivityService tracks channel-to-subscriptionId mappings via messenger calls\n * - Automatically subscribes to selected account on initialization\n * - Switches subscriptions when selected account changes\n * - No direct dependency on BackendWebSocketService (uses messenger instead)\n *\n * @example\n * ```typescript\n * const service = new AccountActivityService({\n * messenger: activityMessenger,\n * });\n *\n * // Service automatically subscribes to the currently selected account\n * // When user switches accounts, service automatically resubscribes\n *\n * // All transactions and balance updates are received via optimized\n * // WebSocket callbacks and processed with zero-allocation routing\n * // Balance updates include comprehensive transfer details and post-transaction balances\n * ```\n */\nexport class AccountActivityService {\n /**\n * The name of the service.\n */\n readonly name = SERVICE_NAME;\n\n readonly #messenger: AccountActivityServiceMessenger;\n\n readonly #options: Required<AccountActivityServiceOptions>;\n\n #supportedChains: string[] | null = null;\n\n #supportedChainsExpiresAt: number = 0;\n\n // =============================================================================\n // Constructor and Initialization\n // =============================================================================\n\n /**\n * Creates a new Account Activity service instance\n *\n * @param options - Configuration options including messenger\n */\n constructor(\n options: AccountActivityServiceOptions & {\n messenger: AccountActivityServiceMessenger;\n },\n ) {\n this.#messenger = options.messenger;\n\n // Set configuration with defaults\n this.#options = {\n subscriptionNamespace:\n options.subscriptionNamespace ?? SUBSCRIPTION_NAMESPACE,\n };\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n this.#messenger.subscribe(\n 'AccountsController:selectedAccountChange',\n async (account: InternalAccount) =>\n await this.#handleSelectedAccountChange(account),\n );\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n (connectionInfo: WebSocketConnectionInfo) =>\n this.#handleWebSocketStateChange(connectionInfo),\n );\n this.#messenger.call('BackendWebSocketService:addChannelCallback', {\n channelName: `system-notifications.v1.${this.#options.subscriptionNamespace}`,\n callback: (notification: ServerNotificationMessage) =>\n this.#handleSystemNotification(\n notification.data as SystemNotificationData,\n ),\n });\n }\n\n // =============================================================================\n // Public Methods - Chain Management\n // =============================================================================\n\n /**\n * Fetch supported chains from API with fallback to hardcoded list.\n * Uses expiry-based caching with TTL to prevent stale data.\n *\n * @returns Array of supported chain IDs in CAIP-2 format\n */\n async getSupportedChains(): Promise<string[]> {\n // Return cached result if available and not expired\n if (\n this.#supportedChains !== null &&\n Date.now() < this.#supportedChainsExpiresAt\n ) {\n return this.#supportedChains;\n }\n\n try {\n // Try to fetch from API\n this.#supportedChains = await fetchSupportedChainsInCaipFormat();\n } catch {\n // Fallback to hardcoded list and cache it with timestamp\n this.#supportedChains = Array.from(DEFAULT_SUPPORTED_CHAINS);\n }\n\n this.#supportedChainsExpiresAt = Date.now() + SUPPORTED_CHAINS_CACHE_TTL;\n\n return this.#supportedChains;\n }\n\n // =============================================================================\n // Account Subscription Methods\n // =============================================================================\n\n /**\n * Subscribe to account activity (transactions and balance updates)\n * Address should be in CAIP-10 format (e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\")\n *\n * @param subscription - Account subscription configuration with address\n */\n async subscribe(subscription: SubscriptionOptions): Promise<void> {\n try {\n await this.#messenger.call('BackendWebSocketService:connect');\n\n // Create channel name from address\n const channel = `${this.#options.subscriptionNamespace}.${subscription.address}`;\n\n // Check if already subscribed\n if (\n this.#messenger.call(\n 'BackendWebSocketService:channelHasSubscription',\n channel,\n )\n ) {\n return;\n }\n\n // Create subscription using the proper subscribe method (this will be stored in WebSocketService's internal tracking)\n await this.#messenger.call('BackendWebSocketService:subscribe', {\n channels: [channel],\n callback: (notification: ServerNotificationMessage) => {\n this.#handleAccountActivityUpdate(\n notification.data as AccountActivityMessage,\n );\n },\n });\n } catch (error) {\n log('Subscription failed, forcing reconnection', { error });\n await this.#forceReconnection();\n }\n }\n\n /**\n * Unsubscribe from account activity for specified address\n * Address should be in CAIP-10 format (e.g., \"eip155:0:0x1234...\" or \"solana:0:ABC123...\")\n *\n * @param subscription - Account subscription configuration with address to unsubscribe\n */\n async unsubscribe(subscription: SubscriptionOptions): Promise<void> {\n const { address } = subscription;\n try {\n // Find channel for the specified address\n const channel = `${this.#options.subscriptionNamespace}.${address}`;\n const subscriptions = this.#messenger.call(\n 'BackendWebSocketService:getSubscriptionsByChannel',\n channel,\n );\n\n if (subscriptions.length === 0) {\n return;\n }\n\n // Fast path: Direct unsubscribe using stored unsubscribe function\n // Unsubscribe from all matching subscriptions\n for (const subscriptionInfo of subscriptions) {\n await subscriptionInfo.unsubscribe();\n }\n } catch (error) {\n log('Unsubscription failed, forcing reconnection', { error });\n await this.#forceReconnection();\n }\n }\n\n // =============================================================================\n // Private Methods - Event Handlers\n // =============================================================================\n\n /**\n * Handle account activity updates (transactions + balance changes)\n * Processes the comprehensive AccountActivityMessage format with detailed balance updates and transfers\n *\n * @param payload - The account activity message containing transaction and balance updates\n * @example AccountActivityMessage format handling:\n * Input: {\n * address: \"0x123\",\n * tx: { hash: \"0x...\", chain: \"eip155:1\", status: \"completed\", ... },\n * updates: [{\n * asset: { fungible: true, type: \"eip155:1/erc20:0x...\", unit: \"USDT\" },\n * postBalance: { amount: \"1254.75\" },\n * transfers: [{ from: \"0x...\", to: \"0x...\", amount: \"500.00\" }]\n * }]\n * }\n * Output: Transaction and balance updates published separately\n */\n #handleAccountActivityUpdate(payload: AccountActivityMessage): void {\n const { address, tx, updates } = payload;\n\n log('Handling account activity update', {\n address,\n updateCount: updates.length,\n });\n\n // Process transaction update\n this.#messenger.publish(`AccountActivityService:transactionUpdated`, tx);\n\n // Publish comprehensive balance updates with transfer details\n this.#messenger.publish(`AccountActivityService:balanceUpdated`, {\n address,\n chain: tx.chain,\n updates,\n });\n }\n\n /**\n * Handle selected account change event\n *\n * @param newAccount - The newly selected account\n */\n async #handleSelectedAccountChange(\n newAccount: InternalAccount | null,\n ): Promise<void> {\n if (!newAccount?.address) {\n return;\n }\n\n try {\n // Convert new account to CAIP-10 format\n const newAddress = this.#convertToCaip10Address(newAccount);\n\n // First, unsubscribe from all current account activity subscriptions to avoid multiple subscriptions\n await this.#unsubscribeFromAllAccountActivity();\n\n // Then, subscribe to the new selected account\n await this.subscribe({ address: newAddress });\n } catch (error) {\n log('Account change failed', { error });\n }\n }\n\n /**\n * Handle system notification for chain status changes\n * Publishes only the status change (delta) for affected chains\n *\n * @param data - System notification data containing chain status updates\n */\n #handleSystemNotification(data: SystemNotificationData): void {\n // Validate required fields\n if (!data.chainIds || !Array.isArray(data.chainIds) || !data.status) {\n throw new Error(\n 'Invalid system notification data: missing chainIds or status',\n );\n }\n\n // Publish status change directly (delta update)\n this.#messenger.publish(`AccountActivityService:statusChanged`, {\n chainIds: data.chainIds,\n status: data.status,\n });\n }\n\n /**\n * Handle WebSocket connection state changes for fallback polling and resubscription\n *\n * @param connectionInfo - WebSocket connection state information\n */\n async #handleWebSocketStateChange(\n connectionInfo: WebSocketConnectionInfo,\n ): Promise<void> {\n const { state } = connectionInfo;\n const supportedChains = await this.getSupportedChains();\n\n if (state === WebSocketState.CONNECTED) {\n // WebSocket connected - resubscribe and set all chains as up\n await this.#subscribeToSelectedAccount();\n\n // Publish initial status - all supported chains are up when WebSocket connects\n this.#messenger.publish(`AccountActivityService:statusChanged`, {\n chainIds: supportedChains,\n status: 'up',\n });\n\n log('WebSocket connected - Published all chains as up', {\n count: supportedChains.length,\n chains: supportedChains,\n });\n } else if (\n state === WebSocketState.DISCONNECTED ||\n state === WebSocketState.ERROR\n ) {\n this.#messenger.publish(`AccountActivityService:statusChanged`, {\n chainIds: supportedChains,\n status: 'down',\n });\n\n log('WebSocket error/disconnection - Published all chains as down', {\n count: supportedChains.length,\n chains: supportedChains,\n });\n }\n }\n\n // =============================================================================\n // Private Methods - Subscription Management\n // =============================================================================\n\n /**\n * Subscribe to the currently selected account only\n */\n async #subscribeToSelectedAccount(): Promise<void> {\n const selectedAccount = this.#messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n\n if (!selectedAccount || !selectedAccount.address) {\n return;\n }\n\n // Convert to CAIP-10 format and subscribe\n const address = this.#convertToCaip10Address(selectedAccount);\n await this.subscribe({ address });\n }\n\n /**\n * Unsubscribe from all account activity subscriptions for this service\n * Finds all channels matching the service's namespace and unsubscribes from them\n */\n async #unsubscribeFromAllAccountActivity(): Promise<void> {\n const accountActivitySubscriptions = this.#messenger.call(\n 'BackendWebSocketService:findSubscriptionsByChannelPrefix',\n this.#options.subscriptionNamespace,\n );\n\n // Unsubscribe from all matching subscriptions\n for (const subscription of accountActivitySubscriptions) {\n await subscription.unsubscribe();\n }\n }\n\n // =============================================================================\n // Private Methods - Utility Functions\n // =============================================================================\n\n /**\n * Convert an InternalAccount address to CAIP-10 format or raw address\n *\n * @param account - The internal account to convert\n * @returns The CAIP-10 formatted address or raw address\n */\n #convertToCaip10Address(account: InternalAccount): string {\n // Check if account has EVM scopes\n if (account.scopes.some((scope) => scope.startsWith('eip155:'))) {\n // CAIP-10 format: eip155:0:address (subscribe to all EVM chains)\n return `eip155:0:${account.address}`;\n }\n\n // Check if account has Solana scopes\n if (account.scopes.some((scope) => scope.startsWith('solana:'))) {\n // CAIP-10 format: solana:0:address (subscribe to all Solana chains)\n return `solana:0:${account.address}`;\n }\n\n // For other chains or unknown scopes, return raw address\n return account.address;\n }\n\n /**\n * Force WebSocket reconnection to clean up subscription state\n */\n async #forceReconnection(): Promise<void> {\n try {\n log('Forcing WebSocket reconnection to clean up subscription state');\n\n // All subscriptions will be cleaned up automatically on WebSocket disconnect\n\n await this.#messenger.call('BackendWebSocketService:disconnect');\n await this.#messenger.call('BackendWebSocketService:connect');\n } catch (error) {\n log('Failed to force WebSocket reconnection', { error });\n }\n }\n\n // =============================================================================\n // Public Methods - Cleanup\n // =============================================================================\n\n /**\n * Destroy the service and clean up all resources\n * Optimized for fast cleanup during service destruction or mobile app termination\n */\n destroy(): void {\n // Clean up system notification callback\n this.#messenger.call(\n 'BackendWebSocketService:removeChannelCallback',\n `system-notifications.v1.${this.#options.subscriptionNamespace}`,\n );\n }\n}\n"]}
@@ -18,8 +18,6 @@ export type SystemNotificationData = {
18
18
  chainIds: string[];
19
19
  /** Status of the chains: 'down' or 'up' */
20
20
  status: 'down' | 'up';
21
- /** Timestamp of the notification */
22
- timestamp?: number;
23
21
  };
24
22
  declare const SERVICE_NAME = "AccountActivityService";
25
23
  /**
@@ -65,7 +63,6 @@ export type AccountActivityServiceStatusChangedEvent = {
65
63
  {
66
64
  chainIds: string[];
67
65
  status: 'up' | 'down';
68
- timestamp?: number;
69
66
  }
70
67
  ];
71
68
  };
@@ -120,6 +117,13 @@ export declare class AccountActivityService {
120
117
  constructor(options: AccountActivityServiceOptions & {
121
118
  messenger: AccountActivityServiceMessenger;
122
119
  });
120
+ /**
121
+ * Fetch supported chains from API with fallback to hardcoded list.
122
+ * Uses expiry-based caching with TTL to prevent stale data.
123
+ *
124
+ * @returns Array of supported chain IDs in CAIP-2 format
125
+ */
126
+ getSupportedChains(): Promise<string[]>;
123
127
  /**
124
128
  * Subscribe to account activity (transactions and balance updates)
125
129
  * Address should be in CAIP-10 format (e.g., "eip155:0:0x1234..." or "solana:0:ABC123...")
@@ -1 +1 @@
1
- {"version":3,"file":"AccountActivityService.d.cts","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AccountActivityService.d.cts","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,6 @@ export type SystemNotificationData = {
18
18
  chainIds: string[];
19
19
  /** Status of the chains: 'down' or 'up' */
20
20
  status: 'down' | 'up';
21
- /** Timestamp of the notification */
22
- timestamp?: number;
23
21
  };
24
22
  declare const SERVICE_NAME = "AccountActivityService";
25
23
  /**
@@ -65,7 +63,6 @@ export type AccountActivityServiceStatusChangedEvent = {
65
63
  {
66
64
  chainIds: string[];
67
65
  status: 'up' | 'down';
68
- timestamp?: number;
69
66
  }
70
67
  ];
71
68
  };
@@ -120,6 +117,13 @@ export declare class AccountActivityService {
120
117
  constructor(options: AccountActivityServiceOptions & {
121
118
  messenger: AccountActivityServiceMessenger;
122
119
  });
120
+ /**
121
+ * Fetch supported chains from API with fallback to hardcoded list.
122
+ * Uses expiry-based caching with TTL to prevent stale data.
123
+ *
124
+ * @returns Array of supported chain IDs in CAIP-2 format
125
+ */
126
+ getSupportedChains(): Promise<string[]>;
123
127
  /**
124
128
  * Subscribe to account activity (transactions and balance updates)
125
129
  * Address should be in CAIP-10 format (e.g., "eip155:0:0x1234..." or "solana:0:ABC123...")
@@ -1 +1 @@
1
- {"version":3,"file":"AccountActivityService.d.mts","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AccountActivityService.d.mts","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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"}