@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 +0 -31
- package/README.md +22 -62
- package/dist/AccountActivityService.cjs +88 -37
- package/dist/AccountActivityService.cjs.map +1 -1
- package/dist/AccountActivityService.d.cts +7 -3
- package/dist/AccountActivityService.d.cts.map +1 -1
- package/dist/AccountActivityService.d.mts +7 -3
- package/dist/AccountActivityService.d.mts.map +1 -1
- package/dist/AccountActivityService.mjs +88 -37
- package/dist/AccountActivityService.mjs.map +1 -1
- package/dist/BackendWebSocketService.cjs +96 -175
- package/dist/BackendWebSocketService.cjs.map +1 -1
- package/dist/BackendWebSocketService.d.cts +11 -30
- package/dist/BackendWebSocketService.d.cts.map +1 -1
- package/dist/BackendWebSocketService.d.mts +11 -30
- package/dist/BackendWebSocketService.d.mts.map +1 -1
- package/dist/BackendWebSocketService.mjs +96 -175
- package/dist/BackendWebSocketService.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +2 -4
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +2 -4
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +2 -1
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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->>
|
|
293
|
-
|
|
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
|
|
314
|
-
2. **
|
|
315
|
-
3. **
|
|
316
|
-
4. **
|
|
317
|
-
5. **
|
|
318
|
-
6. **
|
|
319
|
-
7. **
|
|
320
|
-
8. **
|
|
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,
|
|
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
|
-
|
|
101
|
-
|
|
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(),
|
|
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(
|
|
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
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,0CAA0C,EAC1C,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AAGrE,OAAO,KAAK,EAAE,mCAAmC,EAAE,yDAAqD;AACxG,OAAO,KAAK,EAEV,kDAAkD,EAEnD,sCAAkC;AAEnC,OAAO,KAAK,EAAE,oCAAoC,EAAE,0DAAsD;AAE1G,OAAO,KAAK,EACV,WAAW,EAEX,aAAa,EACd,oBAAgB;
|
|
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":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,0CAA0C,EAC1C,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AAGrE,OAAO,KAAK,EAAE,mCAAmC,EAAE,yDAAqD;AACxG,OAAO,KAAK,EAEV,kDAAkD,EAEnD,sCAAkC;AAEnC,OAAO,KAAK,EAAE,oCAAoC,EAAE,0DAAsD;AAE1G,OAAO,KAAK,EACV,WAAW,EAEX,aAAa,EACd,oBAAgB;
|
|
1
|
+
{"version":3,"file":"AccountActivityService.d.mts","sourceRoot":"","sources":["../src/AccountActivityService.ts"],"names":[],"mappings":"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"}
|