@metamask-previews/assets-controller 0.2.0-preview-c3cd77f → 0.2.0-preview-d01b2f93d

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.
Files changed (116) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/AssetsController-method-action-types.cjs +7 -0
  3. package/dist/AssetsController-method-action-types.cjs.map +1 -0
  4. package/dist/AssetsController-method-action-types.d.cts +78 -0
  5. package/dist/AssetsController-method-action-types.d.cts.map +1 -0
  6. package/dist/AssetsController-method-action-types.d.mts +78 -0
  7. package/dist/AssetsController-method-action-types.d.mts.map +1 -0
  8. package/dist/AssetsController-method-action-types.mjs +6 -0
  9. package/dist/AssetsController-method-action-types.mjs.map +1 -0
  10. package/dist/AssetsController.cjs +161 -116
  11. package/dist/AssetsController.cjs.map +1 -1
  12. package/dist/AssetsController.d.cts +23 -80
  13. package/dist/AssetsController.d.cts.map +1 -1
  14. package/dist/AssetsController.d.mts +23 -80
  15. package/dist/AssetsController.d.mts.map +1 -1
  16. package/dist/AssetsController.mjs +161 -116
  17. package/dist/AssetsController.mjs.map +1 -1
  18. package/dist/data-sources/AbstractDataSource.cjs.map +1 -1
  19. package/dist/data-sources/AbstractDataSource.d.cts +10 -1
  20. package/dist/data-sources/AbstractDataSource.d.cts.map +1 -1
  21. package/dist/data-sources/AbstractDataSource.d.mts +10 -1
  22. package/dist/data-sources/AbstractDataSource.d.mts.map +1 -1
  23. package/dist/data-sources/AbstractDataSource.mjs.map +1 -1
  24. package/dist/data-sources/AccountsApiDataSource.cjs +23 -99
  25. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  26. package/dist/data-sources/AccountsApiDataSource.d.cts +5 -67
  27. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  28. package/dist/data-sources/AccountsApiDataSource.d.mts +5 -67
  29. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  30. package/dist/data-sources/AccountsApiDataSource.mjs +22 -97
  31. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  32. package/dist/data-sources/BackendWebsocketDataSource.cjs +135 -45
  33. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  34. package/dist/data-sources/BackendWebsocketDataSource.d.cts +19 -66
  35. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  36. package/dist/data-sources/BackendWebsocketDataSource.d.mts +19 -66
  37. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  38. package/dist/data-sources/BackendWebsocketDataSource.mjs +135 -45
  39. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  40. package/dist/data-sources/PriceDataSource.cjs +22 -44
  41. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  42. package/dist/data-sources/PriceDataSource.d.cts +6 -89
  43. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  44. package/dist/data-sources/PriceDataSource.d.mts +6 -89
  45. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  46. package/dist/data-sources/PriceDataSource.mjs +22 -44
  47. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  48. package/dist/data-sources/RpcDataSource.cjs +57 -98
  49. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  50. package/dist/data-sources/RpcDataSource.d.cts +16 -55
  51. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  52. package/dist/data-sources/RpcDataSource.d.mts +16 -55
  53. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  54. package/dist/data-sources/RpcDataSource.mjs +57 -98
  55. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  56. package/dist/data-sources/SnapDataSource.cjs +30 -30
  57. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  58. package/dist/data-sources/SnapDataSource.d.cts +7 -44
  59. package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
  60. package/dist/data-sources/SnapDataSource.d.mts +7 -44
  61. package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
  62. package/dist/data-sources/SnapDataSource.mjs +30 -30
  63. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  64. package/dist/data-sources/TokenDataSource.cjs +3 -16
  65. package/dist/data-sources/TokenDataSource.cjs.map +1 -1
  66. package/dist/data-sources/TokenDataSource.d.cts +2 -25
  67. package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
  68. package/dist/data-sources/TokenDataSource.d.mts +2 -25
  69. package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
  70. package/dist/data-sources/TokenDataSource.mjs +3 -16
  71. package/dist/data-sources/TokenDataSource.mjs.map +1 -1
  72. package/dist/data-sources/index.cjs +1 -6
  73. package/dist/data-sources/index.cjs.map +1 -1
  74. package/dist/data-sources/index.d.cts +6 -7
  75. package/dist/data-sources/index.d.cts.map +1 -1
  76. package/dist/data-sources/index.d.mts +6 -7
  77. package/dist/data-sources/index.d.mts.map +1 -1
  78. package/dist/data-sources/index.mjs +1 -3
  79. package/dist/data-sources/index.mjs.map +1 -1
  80. package/dist/index.cjs +1 -6
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +9 -11
  83. package/dist/index.d.cts.map +1 -1
  84. package/dist/index.d.mts +9 -11
  85. package/dist/index.d.mts.map +1 -1
  86. package/dist/index.mjs +1 -3
  87. package/dist/index.mjs.map +1 -1
  88. package/dist/middlewares/DetectionMiddleware.cjs +4 -27
  89. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
  90. package/dist/middlewares/DetectionMiddleware.d.cts +3 -26
  91. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
  92. package/dist/middlewares/DetectionMiddleware.d.mts +3 -26
  93. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
  94. package/dist/middlewares/DetectionMiddleware.mjs +4 -27
  95. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
  96. package/dist/middlewares/index.cjs.map +1 -1
  97. package/dist/middlewares/index.d.cts +1 -1
  98. package/dist/middlewares/index.d.cts.map +1 -1
  99. package/dist/middlewares/index.d.mts +1 -1
  100. package/dist/middlewares/index.d.mts.map +1 -1
  101. package/dist/middlewares/index.mjs.map +1 -1
  102. package/dist/types.cjs.map +1 -1
  103. package/dist/types.d.cts +52 -3
  104. package/dist/types.d.cts.map +1 -1
  105. package/dist/types.d.mts +52 -3
  106. package/dist/types.d.mts.map +1 -1
  107. package/dist/types.mjs.map +1 -1
  108. package/package.json +1 -1
  109. package/dist/data-sources/initDataSources.cjs +0 -215
  110. package/dist/data-sources/initDataSources.cjs.map +0 -1
  111. package/dist/data-sources/initDataSources.d.cts +0 -140
  112. package/dist/data-sources/initDataSources.d.cts.map +0 -1
  113. package/dist/data-sources/initDataSources.d.mts +0 -140
  114. package/dist/data-sources/initDataSources.d.mts.map +0 -1
  115. package/dist/data-sources/initDataSources.mjs +0 -210
  116. package/dist/data-sources/initDataSources.mjs.map +0 -1
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  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");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _BackendWebsocketDataSource_instances, _BackendWebsocketDataSource_messenger, _BackendWebsocketDataSource_wsSubscriptions, _BackendWebsocketDataSource_pendingSubscriptions, _BackendWebsocketDataSource_subscriptionRequests, _BackendWebsocketDataSource_registerActionHandlers, _BackendWebsocketDataSource_subscribeToEvents, _BackendWebsocketDataSource_handleDisconnect, _BackendWebsocketDataSource_processPendingSubscriptions, _BackendWebsocketDataSource_handleNotification, _BackendWebsocketDataSource_processBalanceUpdates;
13
+ var _BackendWebsocketDataSource_instances, _BackendWebsocketDataSource_messenger, _BackendWebsocketDataSource_apiClient, _BackendWebsocketDataSource_onActiveChainsUpdated, _BackendWebsocketDataSource_chainsRefreshTimer, _BackendWebsocketDataSource_wsSubscriptions, _BackendWebsocketDataSource_pendingSubscriptions, _BackendWebsocketDataSource_subscriptionRequests, _BackendWebsocketDataSource_initializeActiveChains, _BackendWebsocketDataSource_refreshActiveChains, _BackendWebsocketDataSource_fetchActiveChains, _BackendWebsocketDataSource_subscribeToEvents, _BackendWebsocketDataSource_handleDisconnect, _BackendWebsocketDataSource_processPendingSubscriptions, _BackendWebsocketDataSource_handleNotification, _BackendWebsocketDataSource_processBalanceUpdates;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.createBackendWebsocketDataSource = exports.BackendWebsocketDataSource = void 0;
16
16
  const AbstractDataSource_1 = require("./AbstractDataSource.cjs");
@@ -38,31 +38,74 @@ function extractNamespace(chainId) {
38
38
  const [namespace] = chainId.split(':');
39
39
  return namespace;
40
40
  }
41
+ /** Namespaces we always subscribe to for account activity (EVM + Solana). */
42
+ const ACCOUNT_ACTIVITY_NAMESPACES = ['eip155', 'solana'];
41
43
  /**
42
- * Get unique namespaces from chain IDs.
44
+ * Get unique namespaces for account-activity subscriptions.
45
+ * Always includes eip155 and solana so we subscribe to both EVM and Solana account activity,
46
+ * plus any additional namespaces from the requested chain IDs.
43
47
  *
44
- * @param chainIds - Array of CAIP-2 chain IDs.
45
- * @returns Array of unique namespaces.
48
+ * @param chainIds - Array of CAIP-2 chain IDs (from the subscription request).
49
+ * @returns Array of unique namespaces (at least eip155 and solana).
46
50
  */
47
- function getUniqueNamespaces(chainIds) {
48
- const namespaces = new Set();
51
+ function getNamespacesForAccountActivity(chainIds) {
52
+ const namespaces = new Set(ACCOUNT_ACTIVITY_NAMESPACES);
49
53
  for (const chainId of chainIds) {
50
54
  namespaces.add(extractNamespace(chainId));
51
55
  }
52
56
  return Array.from(namespaces);
53
57
  }
58
+ /**
59
+ * Returns the address to use for account-activity subscription in the given namespace.
60
+ * EIP-155 accounts use hex (0x...) address; Solana accounts use base58.
61
+ * Returns null if this account type does not have an address in that namespace.
62
+ *
63
+ * @param account - Internal account (type + address).
64
+ * @param account.type - Account type (e.g. "eip155:eoa", "solana:data-account").
65
+ * @param account.address - Account address (hex for eip155, base58 for solana).
66
+ * @param namespace - The chain namespace (e.g., "eip155", "solana").
67
+ * @returns The address for that namespace, or null if the account does not support the namespace.
68
+ */
69
+ function getAddressForAccountActivity(account, namespace) {
70
+ if (namespace === 'eip155') {
71
+ return account.type.startsWith('eip155') ? account.address : null;
72
+ }
73
+ if (namespace === 'solana') {
74
+ return account.type.startsWith('solana') ? account.address : null;
75
+ }
76
+ // Other namespaces (e.g. from chainIds): use address if account type matches namespace
77
+ const typePrefix = `${namespace}:`;
78
+ return account.type.startsWith(typePrefix) ? account.address : null;
79
+ }
54
80
  /**
55
81
  * Build WebSocket channel name for account activity using CAIP-10 wildcard format.
56
82
  * Uses 0 as the chain reference to subscribe to all chains in the namespace.
57
- * Format: account-activity.v1.eip155:0:0x1234... (all EVM chains)
58
- * Format: account-activity.v1.solana:0:ABC123... (all Solana chains)
83
+ * EIP-155 addresses are lowercased (hex); Solana addresses are left as-is (base58).
59
84
  *
60
85
  * @param namespace - The chain namespace (e.g., "eip155", "solana").
61
- * @param address - The account address.
86
+ * @param address - The account address (hex for eip155, base58 for solana).
62
87
  * @returns The WebSocket channel name.
63
88
  */
64
89
  function buildAccountActivityChannel(namespace, address) {
65
- return `${CHANNEL_TYPE}.${namespace}:0:${address.toLowerCase()}`;
90
+ const formatted = namespace === 'eip155' ? address.toLowerCase() : address;
91
+ return `${CHANNEL_TYPE}.${namespace}:0:${formatted}`;
92
+ }
93
+ /**
94
+ * Normalize API chain identifier to CAIP-2 ChainId.
95
+ * Passes through strings already in namespace:reference form (e.g. eip155:1, solana:5eykt...).
96
+ * Converts bare decimals to eip155:decimal.
97
+ *
98
+ * @param chainIdOrDecimal - Chain ID string (CAIP-2 or decimal) or decimal number.
99
+ * @returns CAIP-2 ChainId.
100
+ */
101
+ function toChainId(chainIdOrDecimal) {
102
+ if (typeof chainIdOrDecimal === 'string') {
103
+ if (chainIdOrDecimal.includes(':')) {
104
+ return chainIdOrDecimal;
105
+ }
106
+ return `eip155:${chainIdOrDecimal}`;
107
+ }
108
+ return `eip155:${chainIdOrDecimal}`;
66
109
  }
67
110
  // Note: AccountActivityMessage and BalanceUpdate types are imported from @metamask/core-backend
68
111
  // ============================================================================
@@ -94,6 +137,7 @@ function buildAccountActivityChannel(namespace, address) {
94
137
  * - BackendWebSocketService:getConnectionInfo
95
138
  * - BackendWebSocketService:findSubscriptionsByChannelPrefix
96
139
  */
140
+ const DEFAULT_CHAINS_REFRESH_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes
97
141
  class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource {
98
142
  constructor(options) {
99
143
  super(CONTROLLER_NAME, {
@@ -102,6 +146,10 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
102
146
  });
103
147
  _BackendWebsocketDataSource_instances.add(this);
104
148
  _BackendWebsocketDataSource_messenger.set(this, void 0);
149
+ _BackendWebsocketDataSource_apiClient.set(this, void 0);
150
+ _BackendWebsocketDataSource_onActiveChainsUpdated.set(this, void 0);
151
+ /** Chains refresh timer */
152
+ _BackendWebsocketDataSource_chainsRefreshTimer.set(this, null);
105
153
  /** WebSocket subscriptions by our internal subscription ID */
106
154
  _BackendWebsocketDataSource_wsSubscriptions.set(this, new Map());
107
155
  /** Pending subscription requests to process when WebSocket connects */
@@ -109,8 +157,20 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
109
157
  /** Store original subscription requests for reconnection */
110
158
  _BackendWebsocketDataSource_subscriptionRequests.set(this, new Map());
111
159
  __classPrivateFieldSet(this, _BackendWebsocketDataSource_messenger, options.messenger, "f");
112
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_registerActionHandlers).call(this);
160
+ __classPrivateFieldSet(this, _BackendWebsocketDataSource_apiClient, options.queryApiClient, "f");
161
+ __classPrivateFieldSet(this, _BackendWebsocketDataSource_onActiveChainsUpdated, options.onActiveChainsUpdated, "f");
113
162
  __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_subscribeToEvents).call(this);
163
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_initializeActiveChains).call(this).catch(console.error);
164
+ }
165
+ /**
166
+ * Sync active chains from AccountsApiDataSource.
167
+ * Called by AssetsController.handleActiveChainsUpdate when the callback is
168
+ * invoked for BackendWebsocketDataSource (no messenger call; controller already updated).
169
+ *
170
+ * @param chains - Updated active chain IDs from AccountsApiDataSource.
171
+ */
172
+ setActiveChainsFromAccountsApi(chains) {
173
+ this.updateActiveChains(chains, () => undefined);
114
174
  }
115
175
  // ============================================================================
116
176
  // ACTIVE CHAINS
@@ -121,7 +181,7 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
121
181
  * @param chains - Array of supported chain IDs.
122
182
  */
123
183
  updateSupportedChains(chains) {
124
- this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").call('AssetsController:activeChainsUpdate', CONTROLLER_NAME, updatedChains));
184
+ this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_onActiveChainsUpdated, "f").call(this, updatedChains));
125
185
  }
126
186
  // ============================================================================
127
187
  // SUBSCRIBE
@@ -130,8 +190,8 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
130
190
  const { request, subscriptionId, isUpdate } = subscriptionRequest;
131
191
  // Filter to active chains only
132
192
  const chainsToSubscribe = request.chainIds.filter((chainId) => this.state.activeChains.includes(chainId));
133
- const addresses = request.accounts.map((a) => a.address);
134
- if (chainsToSubscribe.length === 0) {
193
+ const addresses = request.accountsWithSupportedChains.map((a) => a.account.address);
194
+ if (addresses.length === 0) {
135
195
  return;
136
196
  }
137
197
  // Check WebSocket connection status
@@ -168,13 +228,16 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
168
228
  }
169
229
  // Clean up existing subscription if any
170
230
  await this.unsubscribe(subscriptionId);
171
- // Extract unique namespaces from chains (e.g., eip155, solana)
172
- const namespaces = getUniqueNamespaces(chainsToSubscribe);
173
- // Build channel names using CAIP-10 wildcard format
231
+ // Always subscribe to eip155 and solana account activity, plus any namespaces from requested chains
232
+ const namespaces = getNamespacesForAccountActivity(chainsToSubscribe);
233
+ // Build channel names: use namespace-appropriate address per account (eip155 = hex, solana = base58)
174
234
  const channels = [];
175
235
  for (const namespace of namespaces) {
176
- for (const address of addresses) {
177
- channels.push(buildAccountActivityChannel(namespace, address));
236
+ for (const { account } of request.accountsWithSupportedChains) {
237
+ const address = getAddressForAccountActivity(account, namespace);
238
+ if (address) {
239
+ channels.push(buildAccountActivityChannel(namespace, address));
240
+ }
178
241
  }
179
242
  }
180
243
  try {
@@ -183,7 +246,7 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
183
246
  channels,
184
247
  channelType: CHANNEL_TYPE,
185
248
  callback: (notification) => {
186
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_handleNotification).call(this, notification, request);
249
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_handleNotification).call(this, notification, subscriptionId);
187
250
  },
188
251
  });
189
252
  // Store WebSocket subscription
@@ -203,6 +266,7 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
203
266
  },
204
267
  chains: chainsToSubscribe,
205
268
  addresses,
269
+ onAssetsUpdate: subscriptionRequest.onAssetsUpdate,
206
270
  });
207
271
  // Store original request for reconnection
208
272
  __classPrivateFieldGet(this, _BackendWebsocketDataSource_subscriptionRequests, "f").set(subscriptionId, subscriptionRequest);
@@ -219,6 +283,10 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
219
283
  // CLEANUP
220
284
  // ============================================================================
221
285
  destroy() {
286
+ if (__classPrivateFieldGet(this, _BackendWebsocketDataSource_chainsRefreshTimer, "f")) {
287
+ clearInterval(__classPrivateFieldGet(this, _BackendWebsocketDataSource_chainsRefreshTimer, "f"));
288
+ __classPrivateFieldSet(this, _BackendWebsocketDataSource_chainsRefreshTimer, null, "f");
289
+ }
222
290
  // Clean up WebSocket subscriptions
223
291
  // Convert to array first to avoid modifying map during iteration
224
292
  const subscriptions = [...__classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").values()];
@@ -239,32 +307,48 @@ class BackendWebsocketDataSource extends AbstractDataSource_1.AbstractDataSource
239
307
  }
240
308
  }
241
309
  exports.BackendWebsocketDataSource = BackendWebsocketDataSource;
242
- _BackendWebsocketDataSource_messenger = new WeakMap(), _BackendWebsocketDataSource_wsSubscriptions = new WeakMap(), _BackendWebsocketDataSource_pendingSubscriptions = new WeakMap(), _BackendWebsocketDataSource_subscriptionRequests = new WeakMap(), _BackendWebsocketDataSource_instances = new WeakSet(), _BackendWebsocketDataSource_registerActionHandlers = function _BackendWebsocketDataSource_registerActionHandlers() {
243
- const getActiveChainsHandler = async () => this.getActiveChains();
244
- const subscribeHandler = async (request) => this.subscribe(request);
245
- const unsubscribeHandler = async (subscriptionId) => this.unsubscribe(subscriptionId);
246
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").registerActionHandler('BackendWebsocketDataSource:getActiveChains', getActiveChainsHandler);
247
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").registerActionHandler('BackendWebsocketDataSource:subscribe', subscribeHandler);
248
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").registerActionHandler('BackendWebsocketDataSource:unsubscribe', unsubscribeHandler);
310
+ _BackendWebsocketDataSource_messenger = new WeakMap(), _BackendWebsocketDataSource_apiClient = new WeakMap(), _BackendWebsocketDataSource_onActiveChainsUpdated = new WeakMap(), _BackendWebsocketDataSource_chainsRefreshTimer = new WeakMap(), _BackendWebsocketDataSource_wsSubscriptions = new WeakMap(), _BackendWebsocketDataSource_pendingSubscriptions = new WeakMap(), _BackendWebsocketDataSource_subscriptionRequests = new WeakMap(), _BackendWebsocketDataSource_instances = new WeakSet(), _BackendWebsocketDataSource_initializeActiveChains =
311
+ // ============================================================================
312
+ // INITIALIZATION
313
+ // ============================================================================
314
+ async function _BackendWebsocketDataSource_initializeActiveChains() {
315
+ try {
316
+ const chains = await __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_fetchActiveChains).call(this);
317
+ this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_onActiveChainsUpdated, "f").call(this, updatedChains));
318
+ __classPrivateFieldSet(this, _BackendWebsocketDataSource_chainsRefreshTimer, setInterval(() => {
319
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_refreshActiveChains).call(this).catch(console.error);
320
+ }, DEFAULT_CHAINS_REFRESH_INTERVAL_MS), "f");
321
+ }
322
+ catch (error) {
323
+ log('Failed to fetch active chains', error);
324
+ }
325
+ }, _BackendWebsocketDataSource_refreshActiveChains = async function _BackendWebsocketDataSource_refreshActiveChains() {
326
+ try {
327
+ const chains = await __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_fetchActiveChains).call(this);
328
+ const previousChains = new Set(this.state.activeChains);
329
+ const newChains = new Set(chains);
330
+ const added = chains.filter((chain) => !previousChains.has(chain));
331
+ const removed = Array.from(previousChains).filter((chain) => !newChains.has(chain));
332
+ if (added.length > 0 || removed.length > 0) {
333
+ this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_onActiveChainsUpdated, "f").call(this, updatedChains));
334
+ }
335
+ }
336
+ catch (error) {
337
+ log('Failed to refresh active chains', error);
338
+ }
339
+ }, _BackendWebsocketDataSource_fetchActiveChains = async function _BackendWebsocketDataSource_fetchActiveChains() {
340
+ const response = await __classPrivateFieldGet(this, _BackendWebsocketDataSource_apiClient, "f").accounts.fetchV2SupportedNetworks();
341
+ return response.fullSupport.map(toChainId);
249
342
  }, _BackendWebsocketDataSource_subscribeToEvents = function _BackendWebsocketDataSource_subscribeToEvents() {
250
- // Listen for WebSocket connection state changes
343
+ // Listen for WebSocket connection state changes (event not in AssetsControllerEvents).
251
344
  __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").subscribe('BackendWebSocketService:connectionStateChanged', (connectionInfo) => {
252
345
  if (connectionInfo.state === 'connected') {
253
- // WebSocket connected - process any pending subscriptions
254
346
  __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_processPendingSubscriptions).call(this).catch(console.error);
255
347
  }
256
348
  else if (connectionInfo.state === 'disconnected') {
257
- // When disconnected, all subscriptions are cleared server-side
258
- // Move active subscriptions to pending for re-subscription on reconnect
259
349
  __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_handleDisconnect).call(this);
260
350
  }
261
351
  });
262
- // Listen for AccountsApiDataSource active chains changes
263
- // This keeps BackendWebsocketDataSource in sync with the supported chains
264
- // since both use the same backend infrastructure
265
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").subscribe('AccountsApiDataSource:activeChainsUpdated', (chains) => {
266
- this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").call('AssetsController:activeChainsUpdate', CONTROLLER_NAME, updatedChains));
267
- });
268
352
  }, _BackendWebsocketDataSource_handleDisconnect = function _BackendWebsocketDataSource_handleDisconnect() {
269
353
  log('WebSocket disconnected, preserving subscriptions for reconnect', {
270
354
  activeSubscriptionCount: this.activeSubscriptions.size,
@@ -308,8 +392,13 @@ async function _BackendWebsocketDataSource_processPendingSubscriptions() {
308
392
  });
309
393
  }
310
394
  }
311
- }, _BackendWebsocketDataSource_handleNotification = function _BackendWebsocketDataSource_handleNotification(notification, request) {
395
+ }, _BackendWebsocketDataSource_handleNotification = function _BackendWebsocketDataSource_handleNotification(notification, subscriptionId) {
312
396
  try {
397
+ const subscription = this.activeSubscriptions.get(subscriptionId);
398
+ const request = __classPrivateFieldGet(this, _BackendWebsocketDataSource_subscriptionRequests, "f").get(subscriptionId)?.request;
399
+ if (!request) {
400
+ return;
401
+ }
313
402
  const activityMessage = notification.data;
314
403
  const { address, tx, updates } = activityMessage;
315
404
  if (!address || !tx || !updates) {
@@ -317,19 +406,20 @@ async function _BackendWebsocketDataSource_processPendingSubscriptions() {
317
406
  }
318
407
  // Extract chain ID from transaction (CAIP-2 format, e.g., "eip155:8453")
319
408
  const chainId = tx.chain;
320
- // Find matching account in request
321
- const account = request.accounts.find((a) => a.address.toLowerCase() === address.toLowerCase());
409
+ // Find matching account in request (eip155: case-insensitive hex; solana: exact base58)
410
+ const account = request.accountsWithSupportedChains
411
+ .map((entry) => entry.account)
412
+ .find((a) => a.address.startsWith('0x')
413
+ ? a.address.toLowerCase() === address.toLowerCase()
414
+ : a.address === address);
322
415
  if (!account) {
323
416
  return;
324
417
  }
325
418
  const accountId = account.id;
326
419
  // Process all balance updates from the activity message
327
420
  const response = __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_processBalanceUpdates).call(this, updates, chainId, accountId);
328
- if (Object.keys(response).length > 0) {
329
- // Report update to AssetsController
330
- __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f")
331
- .call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)
332
- .catch(console.error);
421
+ if (Object.keys(response).length > 0 && subscription) {
422
+ Promise.resolve(subscription.onAssetsUpdate(response)).catch(console.error);
333
423
  }
334
424
  }
335
425
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"BackendWebsocketDataSource.cjs","sourceRoot":"","sources":["../../src/data-sources/BackendWebsocketDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BackendWebSocketServiceActions,\n BackendWebSocketServiceEvents,\n ServerNotificationMessage,\n WebSocketSubscription,\n WebSocketState,\n AccountActivityMessage,\n BalanceUpdate,\n} from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { AbstractDataSource } from './AbstractDataSource';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetMetadata,\n AssetBalance,\n DataRequest,\n DataResponse,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'BackendWebsocketDataSource';\nconst CHANNEL_TYPE = 'account-activity.v1';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Action types that BackendWebsocketDataSource exposes\nexport type BackendWebsocketDataSourceGetActiveChainsAction = {\n type: 'BackendWebsocketDataSource:getActiveChains';\n handler: () => Promise<ChainId[]>;\n};\n\nexport type BackendWebsocketDataSourceSubscribeAction = {\n type: 'BackendWebsocketDataSource:subscribe';\n handler: (request: SubscriptionRequest) => Promise<void>;\n};\n\nexport type BackendWebsocketDataSourceUnsubscribeAction = {\n type: 'BackendWebsocketDataSource:unsubscribe';\n handler: (subscriptionId: string) => Promise<void>;\n};\n\nexport type BackendWebsocketDataSourceActions =\n | BackendWebsocketDataSourceGetActiveChainsAction\n | BackendWebsocketDataSourceSubscribeAction\n | BackendWebsocketDataSourceUnsubscribeAction;\n\n// Event types that BackendWebsocketDataSource publishes\nexport type BackendWebsocketDataSourceActiveChainsChangedEvent = {\n type: 'BackendWebsocketDataSource:activeChainsUpdated';\n payload: [ChainId[]];\n};\n\nexport type BackendWebsocketDataSourceAssetsUpdatedEvent = {\n type: 'BackendWebsocketDataSource:assetsUpdated';\n payload: [DataResponse, string | undefined];\n};\n\nexport type BackendWebsocketDataSourceEvents =\n | BackendWebsocketDataSourceActiveChainsChangedEvent\n | BackendWebsocketDataSourceAssetsUpdatedEvent;\n\n// Actions to report to AssetsController\ntype AssetsControllerActiveChainsUpdateAction = {\n type: 'AssetsController:activeChainsUpdate';\n handler: (dataSourceId: string, activeChains: ChainId[]) => void;\n};\n\ntype AssetsControllerAssetsUpdateAction = {\n type: 'AssetsController:assetsUpdate';\n handler: (response: DataResponse, sourceId: string) => Promise<void>;\n};\n\n// Allowed actions that BackendWebsocketDataSource can call\nexport type BackendWebsocketDataSourceAllowedActions =\n | BackendWebSocketServiceActions\n | AssetsControllerActiveChainsUpdateAction\n | AssetsControllerAssetsUpdateAction;\n\n// Event type from AccountsApiDataSource that we subscribe to\nexport type AccountsApiDataSourceActiveChainsChangedEvent = {\n type: 'AccountsApiDataSource:activeChainsUpdated';\n payload: [ChainId[]];\n};\n\n// Allowed events that BackendWebsocketDataSource can subscribe to\nexport type BackendWebsocketDataSourceAllowedEvents =\n | BackendWebSocketServiceEvents\n | AccountsApiDataSourceActiveChainsChangedEvent;\n\nexport type BackendWebsocketDataSourceMessenger = Messenger<\n typeof CONTROLLER_NAME,\n BackendWebsocketDataSourceActions | BackendWebsocketDataSourceAllowedActions,\n BackendWebsocketDataSourceEvents | BackendWebsocketDataSourceAllowedEvents\n>;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type BackendWebsocketDataSourceState = DataSourceState;\n\nconst defaultState: BackendWebsocketDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type BackendWebsocketDataSourceOptions = {\n messenger: BackendWebsocketDataSourceMessenger;\n state?: Partial<BackendWebsocketDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Extract namespace from a CAIP-2 chain ID.\n * E.g., \"eip155:1\" -> \"eip155\", \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\" -> \"solana\"\n *\n * @param chainId - The CAIP-2 chain ID to extract namespace from.\n * @returns The namespace portion of the chain ID.\n */\nfunction extractNamespace(chainId: ChainId): string {\n const [namespace] = chainId.split(':');\n return namespace;\n}\n\n/**\n * Get unique namespaces from chain IDs.\n *\n * @param chainIds - Array of CAIP-2 chain IDs.\n * @returns Array of unique namespaces.\n */\nfunction getUniqueNamespaces(chainIds: ChainId[]): string[] {\n const namespaces = new Set<string>();\n for (const chainId of chainIds) {\n namespaces.add(extractNamespace(chainId));\n }\n return Array.from(namespaces);\n}\n\n/**\n * Build WebSocket channel name for account activity using CAIP-10 wildcard format.\n * Uses 0 as the chain reference to subscribe to all chains in the namespace.\n * Format: account-activity.v1.eip155:0:0x1234... (all EVM chains)\n * Format: account-activity.v1.solana:0:ABC123... (all Solana chains)\n *\n * @param namespace - The chain namespace (e.g., \"eip155\", \"solana\").\n * @param address - The account address.\n * @returns The WebSocket channel name.\n */\nfunction buildAccountActivityChannel(\n namespace: string,\n address: string,\n): string {\n return `${CHANNEL_TYPE}.${namespace}:0:${address.toLowerCase()}`;\n}\n\n// Note: AccountActivityMessage and BalanceUpdate types are imported from @metamask/core-backend\n\n// ============================================================================\n// BACKEND WEBSOCKET DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for receiving real-time balance updates via WebSocket.\n *\n * This data source connects directly to BackendWebSocketService to receive\n * push notifications for account balance changes. Unlike AccountsApiDataSource\n * which polls for data, this provides instant updates.\n *\n * Uses Messenger pattern for all interactions:\n * - Calls BackendWebSocketService methods via messenger actions\n * - Exposes its own actions for AssetsController to call\n * - Publishes events for AssetsController to subscribe to\n *\n * Actions exposed:\n * - BackendWebsocketDataSource:getActiveChains\n * - BackendWebsocketDataSource:subscribe\n * - BackendWebsocketDataSource:unsubscribe\n *\n * Events published:\n * - BackendWebsocketDataSource:activeChainsUpdated\n * - BackendWebsocketDataSource:assetsUpdated\n *\n * Actions called (from BackendWebSocketService):\n * - BackendWebSocketService:subscribe\n * - BackendWebSocketService:getConnectionInfo\n * - BackendWebSocketService:findSubscriptionsByChannelPrefix\n */\nexport class BackendWebsocketDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n BackendWebsocketDataSourceState\n> {\n readonly #messenger: BackendWebsocketDataSourceMessenger;\n\n /** WebSocket subscriptions by our internal subscription ID */\n readonly #wsSubscriptions: Map<string, WebSocketSubscription> = new Map();\n\n /** Pending subscription requests to process when WebSocket connects */\n readonly #pendingSubscriptions: Map<string, SubscriptionRequest> = new Map();\n\n /** Store original subscription requests for reconnection */\n readonly #subscriptionRequests: Map<string, SubscriptionRequest> = new Map();\n\n constructor(options: BackendWebsocketDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n\n this.#registerActionHandlers();\n this.#subscribeToEvents();\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n #registerActionHandlers(): void {\n const getActiveChainsHandler: BackendWebsocketDataSourceGetActiveChainsAction['handler'] =\n async () => this.getActiveChains();\n\n const subscribeHandler: BackendWebsocketDataSourceSubscribeAction['handler'] =\n async (request) => this.subscribe(request);\n\n const unsubscribeHandler: BackendWebsocketDataSourceUnsubscribeAction['handler'] =\n async (subscriptionId) => this.unsubscribe(subscriptionId);\n\n this.#messenger.registerActionHandler(\n 'BackendWebsocketDataSource:getActiveChains',\n getActiveChainsHandler,\n );\n\n this.#messenger.registerActionHandler(\n 'BackendWebsocketDataSource:subscribe',\n subscribeHandler,\n );\n\n this.#messenger.registerActionHandler(\n 'BackendWebsocketDataSource:unsubscribe',\n unsubscribeHandler,\n );\n }\n\n #subscribeToEvents(): void {\n // Listen for WebSocket connection state changes\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n (connectionInfo) => {\n if (connectionInfo.state === ('connected' as WebSocketState)) {\n // WebSocket connected - process any pending subscriptions\n this.#processPendingSubscriptions().catch(console.error);\n } else if (\n connectionInfo.state === ('disconnected' as WebSocketState)\n ) {\n // When disconnected, all subscriptions are cleared server-side\n // Move active subscriptions to pending for re-subscription on reconnect\n this.#handleDisconnect();\n }\n },\n );\n\n // Listen for AccountsApiDataSource active chains changes\n // This keeps BackendWebsocketDataSource in sync with the supported chains\n // since both use the same backend infrastructure\n this.#messenger.subscribe(\n 'AccountsApiDataSource:activeChainsUpdated',\n (chains: ChainId[]) => {\n this.updateActiveChains(chains, (updatedChains) =>\n this.#messenger.call(\n 'AssetsController:activeChainsUpdate',\n CONTROLLER_NAME,\n updatedChains,\n ),\n );\n },\n );\n }\n\n /**\n * Handle WebSocket disconnection.\n * Moves all active subscriptions to pending for re-subscription on reconnect.\n */\n #handleDisconnect(): void {\n log('WebSocket disconnected, preserving subscriptions for reconnect', {\n activeSubscriptionCount: this.activeSubscriptions.size,\n wsSubscriptionCount: this.#wsSubscriptions.size,\n });\n\n // Move active subscriptions to pending for re-subscription\n for (const [subscriptionId] of this.activeSubscriptions) {\n const originalRequest = this.#subscriptionRequests.get(subscriptionId);\n if (originalRequest) {\n // Mark as update since it was previously active\n this.#pendingSubscriptions.set(subscriptionId, {\n ...originalRequest,\n isUpdate: false, // Treat as new subscription since server cleared it\n });\n }\n }\n\n // Clear WebSocket subscriptions (server-side already cleared)\n this.#wsSubscriptions.clear();\n\n // Clear active subscriptions (they're no longer valid)\n this.activeSubscriptions.clear();\n }\n\n /**\n * Process any pending subscriptions that were queued while WebSocket was disconnected.\n */\n async #processPendingSubscriptions(): Promise<void> {\n if (this.#pendingSubscriptions.size === 0) {\n return;\n }\n\n // Process all pending subscriptions\n const pendingEntries = Array.from(this.#pendingSubscriptions.entries());\n\n for (const [subscriptionId, request] of pendingEntries) {\n try {\n // Remove from pending before processing to avoid infinite loop\n this.#pendingSubscriptions.delete(subscriptionId);\n await this.subscribe(request);\n } catch (error) {\n log('Failed to process pending subscription', {\n subscriptionId,\n error,\n });\n }\n }\n }\n\n // ============================================================================\n // ACTIVE CHAINS\n // ============================================================================\n\n /**\n * Update active chains when AccountsApiDataSource reports new supported chains.\n *\n * @param chains - Array of supported chain IDs.\n */\n updateSupportedChains(chains: ChainId[]): void {\n this.updateActiveChains(chains, (updatedChains) =>\n this.#messenger.call(\n 'AssetsController:activeChainsUpdate',\n CONTROLLER_NAME,\n updatedChains,\n ),\n );\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Filter to active chains only\n const chainsToSubscribe = request.chainIds.filter((chainId) =>\n this.state.activeChains.includes(chainId),\n );\n\n const addresses = request.accounts.map((a) => a.address);\n\n if (chainsToSubscribe.length === 0) {\n return;\n }\n\n // Check WebSocket connection status\n try {\n const connectionInfo = this.#messenger.call(\n 'BackendWebSocketService:getConnectionInfo',\n );\n if (connectionInfo.state !== ('connected' as WebSocketState)) {\n // Store the subscription request to process when WebSocket connects\n this.#pendingSubscriptions.set(subscriptionId, subscriptionRequest);\n return;\n }\n } catch {\n // Store anyway - will be processed when we can connect\n this.#pendingSubscriptions.set(subscriptionId, subscriptionRequest);\n return;\n }\n\n // Remove from pending if it was there (we're processing it now)\n this.#pendingSubscriptions.delete(subscriptionId);\n\n // Handle subscription update\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n // Check if accounts changed - if so, we need to re-subscribe to different channels\n const existingAddresses = existing.addresses ?? [];\n const addressesChanged =\n addresses.length !== existingAddresses.length ||\n addresses.some((addr) => !existingAddresses.includes(addr));\n\n if (!addressesChanged) {\n // Only chains changed - just update chains and return\n existing.chains = chainsToSubscribe;\n return;\n }\n // Accounts changed - fall through to re-subscribe with new channels\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n // Extract unique namespaces from chains (e.g., eip155, solana)\n const namespaces = getUniqueNamespaces(chainsToSubscribe);\n\n // Build channel names using CAIP-10 wildcard format\n const channels: string[] = [];\n for (const namespace of namespaces) {\n for (const address of addresses) {\n channels.push(buildAccountActivityChannel(namespace, address));\n }\n }\n\n try {\n // Create WebSocket subscription\n const wsSubscription = await this.#messenger.call(\n 'BackendWebSocketService:subscribe',\n {\n channels,\n channelType: CHANNEL_TYPE,\n callback: (notification: ServerNotificationMessage) => {\n this.#handleNotification(notification, request);\n },\n },\n );\n\n // Store WebSocket subscription\n this.#wsSubscriptions.set(subscriptionId, wsSubscription);\n\n // Store in abstract class tracking\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n const wsSub = this.#wsSubscriptions.get(subscriptionId);\n if (wsSub) {\n wsSub.unsubscribe().catch((unsubErr) => {\n log('Error unsubscribing', { subscriptionId, error: unsubErr });\n });\n this.#wsSubscriptions.delete(subscriptionId);\n }\n // Also clean up the stored request\n this.#subscriptionRequests.delete(subscriptionId);\n },\n chains: chainsToSubscribe,\n addresses,\n });\n\n // Store original request for reconnection\n this.#subscriptionRequests.set(subscriptionId, subscriptionRequest);\n } catch (error) {\n log('WebSocket subscription FAILED', {\n subscriptionId,\n error,\n chains: chainsToSubscribe,\n });\n }\n }\n\n // ============================================================================\n // NOTIFICATION HANDLING\n // ============================================================================\n\n #handleNotification(\n notification: ServerNotificationMessage,\n request: DataRequest,\n ): void {\n try {\n const activityMessage =\n notification.data as unknown as AccountActivityMessage;\n const { address, tx, updates } = activityMessage;\n\n if (!address || !tx || !updates) {\n return;\n }\n\n // Extract chain ID from transaction (CAIP-2 format, e.g., \"eip155:8453\")\n const chainId = tx.chain as ChainId;\n\n // Find matching account in request\n const account = request.accounts.find(\n (a) => a.address.toLowerCase() === address.toLowerCase(),\n );\n if (!account) {\n return;\n }\n const accountId = account.id;\n\n // Process all balance updates from the activity message\n const response = this.#processBalanceUpdates(updates, chainId, accountId);\n\n if (Object.keys(response).length > 0) {\n // Report update to AssetsController\n this.#messenger\n .call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)\n .catch(console.error);\n }\n } catch (error) {\n log('Error handling notification', error);\n }\n }\n\n /**\n * Process balance updates from AccountActivityMessage.\n * Each update contains asset info, post-transaction balance, and transfer details.\n *\n * @param updates - Array of balance updates from the activity message.\n * @param _chainId - The chain ID (unused but kept for context).\n * @param accountId - The account ID to process updates for.\n * @returns DataResponse containing processed balance and metadata.\n */\n #processBalanceUpdates(\n updates: BalanceUpdate[],\n _chainId: ChainId,\n accountId: string,\n ): DataResponse {\n const assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>> = {\n [accountId]: {},\n };\n const assetsMetadata: Record<Caip19AssetId, AssetMetadata> = {};\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n if (!asset || !postBalance) {\n continue;\n }\n\n // Asset type is in CAIP format: \"eip155:1/erc20:0x...\" or \"eip155:1/slip44:60\"\n // We can use it directly as the asset ID\n const assetId = asset.type as Caip19AssetId;\n\n // Determine token type from asset type string\n const isNative = asset.type.includes('/slip44:');\n const tokenType = isNative ? 'native' : 'erc20';\n\n // Parse balance amount (already in hex format like \"0xc350\")\n const balanceAmount = postBalance.amount.startsWith('0x')\n ? BigInt(postBalance.amount).toString()\n : postBalance.amount;\n\n assetsBalance[accountId][assetId] = {\n amount: balanceAmount,\n };\n\n assetsMetadata[assetId] = {\n type: tokenType,\n symbol: asset.unit,\n name: asset.unit, // Use unit as name (actual name may not be in the message)\n decimals: asset.decimals,\n };\n }\n\n const response: DataResponse = {};\n if (Object.keys(assetsBalance[accountId]).length > 0) {\n response.assetsBalance = assetsBalance;\n response.assetsMetadata = assetsMetadata;\n }\n\n return response;\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // Clean up WebSocket subscriptions\n // Convert to array first to avoid modifying map during iteration\n const subscriptions = [...this.#wsSubscriptions.values()];\n for (const wsSub of subscriptions) {\n try {\n // Fire and forget - don't await in destroy\n wsSub.unsubscribe().catch(() => {\n // Ignore errors during cleanup\n });\n } catch {\n // Ignore errors during cleanup\n }\n }\n this.#wsSubscriptions.clear();\n\n // Clean up base class subscriptions\n super.destroy();\n }\n}\n\n// ============================================================================\n// FACTORY FUNCTION\n// ============================================================================\n\n/**\n * Creates a BackendWebsocketDataSource instance.\n *\n * @param options - Configuration options for the data source.\n * @returns A new BackendWebsocketDataSource instance.\n */\nexport function createBackendWebsocketDataSource(\n options: BackendWebsocketDataSourceOptions,\n): BackendWebsocketDataSource {\n return new BackendWebsocketDataSource(options);\n}\n"]}
1
+ {"version":3,"file":"BackendWebsocketDataSource.cjs","sourceRoot":"","sources":["../../src/data-sources/BackendWebsocketDataSource.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAWA,iEAA0D;AAM1D,0CAA8D;AAS9D,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,eAAe,GAAG,4BAA4B,CAAC;AACrD,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAE3C,MAAM,GAAG,GAAG,IAAA,2BAAkB,EAAC,sBAAa,EAAE,eAAe,CAAC,CAAC;AAoB/D,MAAM,YAAY,GAAoC;IACpD,YAAY,EAAE,EAAE;CACjB,CAAC;AAgBF,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,OAAgB;IACxC,MAAM,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,6EAA6E;AAC7E,MAAM,2BAA2B,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAElE;;;;;;;GAOG;AACH,SAAS,+BAA+B,CAAC,QAAmB;IAC1D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAS,2BAA2B,CAAC,CAAC;IAChE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,4BAA4B,CACnC,OAA0C,EAC1C,SAAiB;IAEjB,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,CAAC;IACD,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,CAAC;IACD,uFAAuF;IACvF,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,CAAC;IACnC,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,2BAA2B,CAClC,SAAiB,EACjB,OAAe;IAEf,MAAM,SAAS,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAC3E,OAAO,GAAG,YAAY,IAAI,SAAS,MAAM,SAAS,EAAE,CAAC;AACvD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,SAAS,CAAC,gBAAiC;IAClD,IAAI,OAAO,gBAAgB,KAAK,QAAQ,EAAE,CAAC;QACzC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,OAAO,gBAA2B,CAAC;QACrC,CAAC;QACD,OAAO,UAAU,gBAAgB,EAAa,CAAC;IACjD,CAAC;IACD,OAAO,UAAU,gBAAgB,EAAa,CAAC;AACjD,CAAC;AAED,gGAAgG;AAEhG,+EAA+E;AAC/E,gCAAgC;AAChC,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,kCAAkC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AAExE,MAAa,0BAA2B,SAAQ,uCAG/C;IAmBC,YAAY,OAA0C;QACpD,KAAK,CAAC,eAAe,EAAE;YACrB,GAAG,YAAY;YACf,GAAG,OAAO,CAAC,KAAK;SACjB,CAAC,CAAC;;QAtBI,wDAAsC;QAEtC,wDAA8B;QAE9B,oEAAoD;QAE7D,2BAA2B;QAC3B,yDAA6D,IAAI,EAAC;QAElE,8DAA8D;QACrD,sDAAuD,IAAI,GAAG,EAAE,EAAC;QAE1E,uEAAuE;QAC9D,2DAA0D,IAAI,GAAG,EAAE,EAAC;QAE7E,4DAA4D;QACnD,2DAA0D,IAAI,GAAG,EAAE,EAAC;QAQ3E,uBAAA,IAAI,yCAAc,OAAO,CAAC,SAAS,MAAA,CAAC;QACpC,uBAAA,IAAI,yCAAc,OAAO,CAAC,cAAc,MAAA,CAAC;QACzC,uBAAA,IAAI,qDAA0B,OAAO,CAAC,qBAAqB,MAAA,CAAC;QAE5D,uBAAA,IAAI,4FAAmB,MAAvB,IAAI,CAAqB,CAAC;QAC1B,uBAAA,IAAI,iGAAwB,MAA5B,IAAI,CAA0B,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IAuED;;;;;;OAMG;IACH,8BAA8B,CAAC,MAAiB;QAC9C,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;IAwDD,+EAA+E;IAC/E,gBAAgB;IAChB,+EAA+E;IAE/E;;;;OAIG;IACH,qBAAqB,CAAC,MAAiB;QACrC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,EAAE,CAChD,uBAAA,IAAI,yDAAuB,MAA3B,IAAI,EAAwB,aAAa,CAAC,CAC3C,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,YAAY;IACZ,+EAA+E;IAE/E,KAAK,CAAC,SAAS,CAAC,mBAAwC;QACtD,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CAAC;QAElE,+BAA+B;QAC/B,MAAM,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5D,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC1C,CAAC;QAEF,MAAM,SAAS,GAAG,OAAO,CAAC,2BAA2B,CAAC,GAAG,CACvD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACzB,CAAC;QAEF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,oCAAoC;QACpC,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,uBAAA,IAAI,6CAAW,CAAC,IAAI,CACzC,2CAA2C,CAC5C,CAAC;YACF,IAAI,cAAc,CAAC,KAAK,KAAM,WAA8B,EAAE,CAAC;gBAC7D,oEAAoE;gBACpE,uBAAA,IAAI,wDAAsB,CAAC,GAAG,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;gBACpE,OAAO;YACT,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uDAAuD;YACvD,uBAAA,IAAI,wDAAsB,CAAC,GAAG,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QAED,gEAAgE;QAChE,uBAAA,IAAI,wDAAsB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAElD,6BAA6B;QAC7B,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC9D,IAAI,QAAQ,EAAE,CAAC;gBACb,mFAAmF;gBACnF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;gBACnD,MAAM,gBAAgB,GACpB,SAAS,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM;oBAC7C,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBAE9D,IAAI,CAAC,gBAAgB,EAAE,CAAC;oBACtB,sDAAsD;oBACtD,QAAQ,CAAC,MAAM,GAAG,iBAAiB,CAAC;oBACpC,OAAO;gBACT,CAAC;gBACD,oEAAoE;YACtE,CAAC;QACH,CAAC;QAED,wCAAwC;QACxC,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QAEvC,oGAAoG;QACpG,MAAM,UAAU,GAAG,+BAA+B,CAAC,iBAAiB,CAAC,CAAC;QAEtE,qGAAqG;QACrG,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,2BAA2B,EAAE,CAAC;gBAC9D,MAAM,OAAO,GAAG,4BAA4B,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;gBACjE,IAAI,OAAO,EAAE,CAAC;oBACZ,QAAQ,CAAC,IAAI,CAAC,2BAA2B,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;gBACjE,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,gCAAgC;YAChC,MAAM,cAAc,GAAG,MAAM,uBAAA,IAAI,6CAAW,CAAC,IAAI,CAC/C,mCAAmC,EACnC;gBACE,QAAQ;gBACR,WAAW,EAAE,YAAY;gBACzB,QAAQ,EAAE,CAAC,YAAuC,EAAE,EAAE;oBACpD,uBAAA,IAAI,6FAAoB,MAAxB,IAAI,EAAqB,YAAY,EAAE,cAAc,CAAC,CAAC;gBACzD,CAAC;aACF,CACF,CAAC;YAEF,+BAA+B;YAC/B,uBAAA,IAAI,mDAAiB,CAAC,GAAG,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;YAE1D,mCAAmC;YACnC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,EAAE;gBAC3C,OAAO,EAAE,GAAG,EAAE;oBACZ,MAAM,KAAK,GAAG,uBAAA,IAAI,mDAAiB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;oBACxD,IAAI,KAAK,EAAE,CAAC;wBACV,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,QAAiB,EAAE,EAAE;4BAC9C,GAAG,CAAC,qBAAqB,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;wBAClE,CAAC,CAAC,CAAC;wBACH,uBAAA,IAAI,mDAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;oBAC/C,CAAC;oBACD,mCAAmC;oBACnC,uBAAA,IAAI,wDAAsB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;gBACpD,CAAC;gBACD,MAAM,EAAE,iBAAiB;gBACzB,SAAS;gBACT,cAAc,EAAE,mBAAmB,CAAC,cAAc;aACnD,CAAC,CAAC;YAEH,0CAA0C;YAC1C,uBAAA,IAAI,wDAAsB,CAAC,GAAG,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,+BAA+B,EAAE;gBACnC,cAAc;gBACd,KAAK;gBACL,MAAM,EAAE,iBAAiB;aAC1B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAkHD,+EAA+E;IAC/E,UAAU;IACV,+EAA+E;IAE/E,OAAO;QACL,IAAI,uBAAA,IAAI,sDAAoB,EAAE,CAAC;YAC7B,aAAa,CAAC,uBAAA,IAAI,sDAAoB,CAAC,CAAC;YACxC,uBAAA,IAAI,kDAAuB,IAAI,MAAA,CAAC;QAClC,CAAC;QAED,mCAAmC;QACnC,iEAAiE;QACjE,MAAM,aAAa,GAAG,CAAC,GAAG,uBAAA,IAAI,mDAAiB,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1D,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,2CAA2C;gBAC3C,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;oBAC7B,+BAA+B;gBACjC,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,+BAA+B;YACjC,CAAC;QACH,CAAC;QACD,uBAAA,IAAI,mDAAiB,CAAC,KAAK,EAAE,CAAC;QAE9B,oCAAoC;QACpC,KAAK,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC;CACF;AA7bD,gEA6bC;;AAzZC,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,KAAK;IACH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,4FAAmB,MAAvB,IAAI,CAAqB,CAAC;QAC/C,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,EAAE,CAChD,uBAAA,IAAI,yDAAuB,MAA3B,IAAI,EAAwB,aAAa,CAAC,CAC3C,CAAC;QAEF,uBAAA,IAAI,kDAAuB,WAAW,CAAC,GAAG,EAAE;YAC1C,uBAAA,IAAI,8FAAqB,MAAzB,IAAI,CAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACnD,CAAC,EAAE,kCAAkC,CAAC,MAAA,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC,oDAED,KAAK;IACH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,4FAAmB,MAAvB,IAAI,CAAqB,CAAC;QAC/C,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QACxD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,CAC/C,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CACjC,CAAC;QAEF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,EAAE,CAChD,uBAAA,IAAI,yDAAuB,MAA3B,IAAI,EAAwB,aAAa,CAAC,CAC3C,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;AACH,CAAC,kDAED,KAAK;IACH,MAAM,QAAQ,GAAG,MAAM,uBAAA,IAAI,6CAAW,CAAC,QAAQ,CAAC,wBAAwB,EAAE,CAAC;IAC3E,OAAO,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAC7C,CAAC;IAOC,uFAAuF;IAErF,uBAAA,IAAI,6CAGL,CAAC,SAAS,CACT,gDAAgD,EAChD,CAAC,cAAsC,EAAE,EAAE;QACzC,IAAI,cAAc,CAAC,KAAK,KAAM,WAA8B,EAAE,CAAC;YAC7D,uBAAA,IAAI,sGAA6B,MAAjC,IAAI,CAA+B,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3D,CAAC;aAAM,IACL,cAAc,CAAC,KAAK,KAAM,cAAiC,EAC3D,CAAC;YACD,uBAAA,IAAI,2FAAkB,MAAtB,IAAI,CAAoB,CAAC;QAC3B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC;IAkBC,GAAG,CAAC,gEAAgE,EAAE;QACpE,uBAAuB,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI;QACtD,mBAAmB,EAAE,uBAAA,IAAI,mDAAiB,CAAC,IAAI;KAChD,CAAC,CAAC;IAEH,2DAA2D;IAC3D,KAAK,MAAM,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACxD,MAAM,eAAe,GAAG,uBAAA,IAAI,wDAAsB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACvE,IAAI,eAAe,EAAE,CAAC;YACpB,gDAAgD;YAChD,uBAAA,IAAI,wDAAsB,CAAC,GAAG,CAAC,cAAc,EAAE;gBAC7C,GAAG,eAAe;gBAClB,QAAQ,EAAE,KAAK,EAAE,oDAAoD;aACtE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,uBAAA,IAAI,mDAAiB,CAAC,KAAK,EAAE,CAAC;IAE9B,uDAAuD;IACvD,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,KAAK;IACH,IAAI,uBAAA,IAAI,wDAAsB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO;IACT,CAAC;IAED,oCAAoC;IACpC,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,wDAAsB,CAAC,OAAO,EAAE,CAAC,CAAC;IAExE,KAAK,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,cAAc,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,+DAA+D;YAC/D,uBAAA,IAAI,wDAAsB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;YAClD,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,wCAAwC,EAAE;gBAC5C,cAAc;gBACd,KAAK;aACN,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC,2GA8IC,YAAuC,EACvC,cAAsB;IAEtB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,uBAAA,IAAI,wDAAsB,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;QACxE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,eAAe,GACnB,YAAY,CAAC,IAAyC,CAAC;QACzD,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;QAEjD,IAAI,CAAC,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QAED,yEAAyE;QACzE,MAAM,OAAO,GAAG,EAAE,CAAC,KAAgB,CAAC;QAEpC,wFAAwF;QACxF,MAAM,OAAO,GAAG,OAAO,CAAC,2BAA2B;aAChD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;aAC7B,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACV,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YACxB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE;YACnD,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAC1B,CAAC;QACJ,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC;QAE7B,wDAAwD;QACxD,MAAM,QAAQ,GAAG,uBAAA,IAAI,gGAAuB,MAA3B,IAAI,EAAwB,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QAE1E,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC;YACrD,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAC1D,OAAO,CAAC,KAAK,CACd,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC,iHAYC,OAAwB,EACxB,QAAiB,EACjB,SAAiB;IAEjB,MAAM,aAAa,GAAwD;QACzE,CAAC,SAAS,CAAC,EAAE,EAAE;KAChB,CAAC;IACF,MAAM,cAAc,GAAyC,EAAE,CAAC;IAEhE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAEtC,IAAI,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3B,SAAS;QACX,CAAC;QAED,+EAA+E;QAC/E,yCAAyC;QACzC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAqB,CAAC;QAE5C,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;QAEhD,6DAA6D;QAC7D,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;YACvD,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;YACvC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;QAEvB,aAAa,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,GAAG;YAClC,MAAM,EAAE,aAAa;SACtB,CAAC;QAEF,cAAc,CAAC,OAAO,CAAC,GAAG;YACxB,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,KAAK,CAAC,IAAI;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,2DAA2D;YAC7E,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,QAAQ,CAAC,aAAa,GAAG,aAAa,CAAC;QACvC,QAAQ,CAAC,cAAc,GAAG,cAAc,CAAC;IAC3C,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAgCH,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;GAKG;AACH,SAAgB,gCAAgC,CAC9C,OAA0C;IAE1C,OAAO,IAAI,0BAA0B,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAJD,4EAIC","sourcesContent":["import type {\n BackendWebSocketServiceActions,\n BackendWebSocketServiceEvents,\n ServerNotificationMessage,\n WebSocketSubscription,\n WebSocketState,\n AccountActivityMessage,\n BalanceUpdate,\n} from '@metamask/core-backend';\nimport type { ApiPlatformClient } from '@metamask/core-backend';\n\nimport { AbstractDataSource } from './AbstractDataSource';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\nimport type { AssetsControllerMessenger } from '../AssetsController';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetMetadata,\n AssetBalance,\n DataResponse,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'BackendWebsocketDataSource';\nconst CHANNEL_TYPE = 'account-activity.v1';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Allowed actions that BackendWebsocketDataSource can call\nexport type BackendWebsocketDataSourceAllowedActions =\n BackendWebSocketServiceActions;\n\n// Allowed events that BackendWebsocketDataSource can subscribe to\nexport type BackendWebsocketDataSourceAllowedEvents =\n BackendWebSocketServiceEvents;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type BackendWebsocketDataSourceState = DataSourceState;\n\nconst defaultState: BackendWebsocketDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type BackendWebsocketDataSourceOptions = {\n /** The AssetsController messenger (shared by all data sources). */\n messenger: AssetsControllerMessenger;\n /** ApiPlatformClient for fetching supported networks at init (same as AccountsApiDataSource). */\n queryApiClient: ApiPlatformClient;\n /** Called when active chains are updated (e.g. to notify AssetsController). */\n onActiveChainsUpdated: (chains: ChainId[]) => void;\n state?: Partial<BackendWebsocketDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Extract namespace from a CAIP-2 chain ID.\n * E.g., \"eip155:1\" -> \"eip155\", \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\" -> \"solana\"\n *\n * @param chainId - The CAIP-2 chain ID to extract namespace from.\n * @returns The namespace portion of the chain ID.\n */\nfunction extractNamespace(chainId: ChainId): string {\n const [namespace] = chainId.split(':');\n return namespace;\n}\n\n/** Namespaces we always subscribe to for account activity (EVM + Solana). */\nconst ACCOUNT_ACTIVITY_NAMESPACES = ['eip155', 'solana'] as const;\n\n/**\n * Get unique namespaces for account-activity subscriptions.\n * Always includes eip155 and solana so we subscribe to both EVM and Solana account activity,\n * plus any additional namespaces from the requested chain IDs.\n *\n * @param chainIds - Array of CAIP-2 chain IDs (from the subscription request).\n * @returns Array of unique namespaces (at least eip155 and solana).\n */\nfunction getNamespacesForAccountActivity(chainIds: ChainId[]): string[] {\n const namespaces = new Set<string>(ACCOUNT_ACTIVITY_NAMESPACES);\n for (const chainId of chainIds) {\n namespaces.add(extractNamespace(chainId));\n }\n return Array.from(namespaces);\n}\n\n/**\n * Returns the address to use for account-activity subscription in the given namespace.\n * EIP-155 accounts use hex (0x...) address; Solana accounts use base58.\n * Returns null if this account type does not have an address in that namespace.\n *\n * @param account - Internal account (type + address).\n * @param account.type - Account type (e.g. \"eip155:eoa\", \"solana:data-account\").\n * @param account.address - Account address (hex for eip155, base58 for solana).\n * @param namespace - The chain namespace (e.g., \"eip155\", \"solana\").\n * @returns The address for that namespace, or null if the account does not support the namespace.\n */\nfunction getAddressForAccountActivity(\n account: { type: string; address: string },\n namespace: string,\n): string | null {\n if (namespace === 'eip155') {\n return account.type.startsWith('eip155') ? account.address : null;\n }\n if (namespace === 'solana') {\n return account.type.startsWith('solana') ? account.address : null;\n }\n // Other namespaces (e.g. from chainIds): use address if account type matches namespace\n const typePrefix = `${namespace}:`;\n return account.type.startsWith(typePrefix) ? account.address : null;\n}\n\n/**\n * Build WebSocket channel name for account activity using CAIP-10 wildcard format.\n * Uses 0 as the chain reference to subscribe to all chains in the namespace.\n * EIP-155 addresses are lowercased (hex); Solana addresses are left as-is (base58).\n *\n * @param namespace - The chain namespace (e.g., \"eip155\", \"solana\").\n * @param address - The account address (hex for eip155, base58 for solana).\n * @returns The WebSocket channel name.\n */\nfunction buildAccountActivityChannel(\n namespace: string,\n address: string,\n): string {\n const formatted = namespace === 'eip155' ? address.toLowerCase() : address;\n return `${CHANNEL_TYPE}.${namespace}:0:${formatted}`;\n}\n\n/**\n * Normalize API chain identifier to CAIP-2 ChainId.\n * Passes through strings already in namespace:reference form (e.g. eip155:1, solana:5eykt...).\n * Converts bare decimals to eip155:decimal.\n *\n * @param chainIdOrDecimal - Chain ID string (CAIP-2 or decimal) or decimal number.\n * @returns CAIP-2 ChainId.\n */\nfunction toChainId(chainIdOrDecimal: number | string): ChainId {\n if (typeof chainIdOrDecimal === 'string') {\n if (chainIdOrDecimal.includes(':')) {\n return chainIdOrDecimal as ChainId;\n }\n return `eip155:${chainIdOrDecimal}` as ChainId;\n }\n return `eip155:${chainIdOrDecimal}` as ChainId;\n}\n\n// Note: AccountActivityMessage and BalanceUpdate types are imported from @metamask/core-backend\n\n// ============================================================================\n// BACKEND WEBSOCKET DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for receiving real-time balance updates via WebSocket.\n *\n * This data source connects directly to BackendWebSocketService to receive\n * push notifications for account balance changes. Unlike AccountsApiDataSource\n * which polls for data, this provides instant updates.\n *\n * Uses Messenger pattern for all interactions:\n * - Calls BackendWebSocketService methods via messenger actions\n * - Exposes its own actions for AssetsController to call\n * - Publishes events for AssetsController to subscribe to\n *\n * Actions exposed:\n * - BackendWebsocketDataSource:getActiveChains\n * - BackendWebsocketDataSource:subscribe\n * - BackendWebsocketDataSource:unsubscribe\n *\n * Events published:\n * - BackendWebsocketDataSource:activeChainsUpdated\n * - BackendWebsocketDataSource:assetsUpdated\n *\n * Actions called (from BackendWebSocketService):\n * - BackendWebSocketService:subscribe\n * - BackendWebSocketService:getConnectionInfo\n * - BackendWebSocketService:findSubscriptionsByChannelPrefix\n */\nconst DEFAULT_CHAINS_REFRESH_INTERVAL_MS = 20 * 60 * 1000; // 20 minutes\n\nexport class BackendWebsocketDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n BackendWebsocketDataSourceState\n> {\n readonly #messenger: AssetsControllerMessenger;\n\n readonly #apiClient: ApiPlatformClient;\n\n readonly #onActiveChainsUpdated: (chains: ChainId[]) => void;\n\n /** Chains refresh timer */\n #chainsRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n /** WebSocket subscriptions by our internal subscription ID */\n readonly #wsSubscriptions: Map<string, WebSocketSubscription> = new Map();\n\n /** Pending subscription requests to process when WebSocket connects */\n readonly #pendingSubscriptions: Map<string, SubscriptionRequest> = new Map();\n\n /** Store original subscription requests for reconnection */\n readonly #subscriptionRequests: Map<string, SubscriptionRequest> = new Map();\n\n constructor(options: BackendWebsocketDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n this.#apiClient = options.queryApiClient;\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n\n this.#subscribeToEvents();\n this.#initializeActiveChains().catch(console.error);\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n async #initializeActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(updatedChains),\n );\n\n this.#chainsRefreshTimer = setInterval(() => {\n this.#refreshActiveChains().catch(console.error);\n }, DEFAULT_CHAINS_REFRESH_INTERVAL_MS);\n } catch (error) {\n log('Failed to fetch active chains', error);\n }\n }\n\n async #refreshActiveChains(): Promise<void> {\n try {\n const chains = await this.#fetchActiveChains();\n const previousChains = new Set(this.state.activeChains);\n const newChains = new Set(chains);\n\n const added = chains.filter((chain) => !previousChains.has(chain));\n const removed = Array.from(previousChains).filter(\n (chain) => !newChains.has(chain),\n );\n\n if (added.length > 0 || removed.length > 0) {\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(updatedChains),\n );\n }\n } catch (error) {\n log('Failed to refresh active chains', error);\n }\n }\n\n async #fetchActiveChains(): Promise<ChainId[]> {\n const response = await this.#apiClient.accounts.fetchV2SupportedNetworks();\n return response.fullSupport.map(toChainId);\n }\n\n #subscribeToEvents(): void {\n type ConnectionStatePayload = {\n state: WebSocketState;\n [key: string]: unknown;\n };\n // Listen for WebSocket connection state changes (event not in AssetsControllerEvents).\n (\n this.#messenger as unknown as {\n subscribe: (e: string, h: (p: ConnectionStatePayload) => void) => void;\n }\n ).subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n (connectionInfo: ConnectionStatePayload) => {\n if (connectionInfo.state === ('connected' as WebSocketState)) {\n this.#processPendingSubscriptions().catch(console.error);\n } else if (\n connectionInfo.state === ('disconnected' as WebSocketState)\n ) {\n this.#handleDisconnect();\n }\n },\n );\n }\n\n /**\n * Sync active chains from AccountsApiDataSource.\n * Called by AssetsController.handleActiveChainsUpdate when the callback is\n * invoked for BackendWebsocketDataSource (no messenger call; controller already updated).\n *\n * @param chains - Updated active chain IDs from AccountsApiDataSource.\n */\n setActiveChainsFromAccountsApi(chains: ChainId[]): void {\n this.updateActiveChains(chains, () => undefined);\n }\n\n /**\n * Handle WebSocket disconnection.\n * Moves all active subscriptions to pending for re-subscription on reconnect.\n */\n #handleDisconnect(): void {\n log('WebSocket disconnected, preserving subscriptions for reconnect', {\n activeSubscriptionCount: this.activeSubscriptions.size,\n wsSubscriptionCount: this.#wsSubscriptions.size,\n });\n\n // Move active subscriptions to pending for re-subscription\n for (const [subscriptionId] of this.activeSubscriptions) {\n const originalRequest = this.#subscriptionRequests.get(subscriptionId);\n if (originalRequest) {\n // Mark as update since it was previously active\n this.#pendingSubscriptions.set(subscriptionId, {\n ...originalRequest,\n isUpdate: false, // Treat as new subscription since server cleared it\n });\n }\n }\n\n // Clear WebSocket subscriptions (server-side already cleared)\n this.#wsSubscriptions.clear();\n\n // Clear active subscriptions (they're no longer valid)\n this.activeSubscriptions.clear();\n }\n\n /**\n * Process any pending subscriptions that were queued while WebSocket was disconnected.\n */\n async #processPendingSubscriptions(): Promise<void> {\n if (this.#pendingSubscriptions.size === 0) {\n return;\n }\n\n // Process all pending subscriptions\n const pendingEntries = Array.from(this.#pendingSubscriptions.entries());\n\n for (const [subscriptionId, request] of pendingEntries) {\n try {\n // Remove from pending before processing to avoid infinite loop\n this.#pendingSubscriptions.delete(subscriptionId);\n await this.subscribe(request);\n } catch (error) {\n log('Failed to process pending subscription', {\n subscriptionId,\n error,\n });\n }\n }\n }\n\n // ============================================================================\n // ACTIVE CHAINS\n // ============================================================================\n\n /**\n * Update active chains when AccountsApiDataSource reports new supported chains.\n *\n * @param chains - Array of supported chain IDs.\n */\n updateSupportedChains(chains: ChainId[]): void {\n this.updateActiveChains(chains, (updatedChains) =>\n this.#onActiveChainsUpdated(updatedChains),\n );\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Filter to active chains only\n const chainsToSubscribe = request.chainIds.filter((chainId) =>\n this.state.activeChains.includes(chainId),\n );\n\n const addresses = request.accountsWithSupportedChains.map(\n (a) => a.account.address,\n );\n\n if (addresses.length === 0) {\n return;\n }\n\n // Check WebSocket connection status\n try {\n const connectionInfo = this.#messenger.call(\n 'BackendWebSocketService:getConnectionInfo',\n );\n if (connectionInfo.state !== ('connected' as WebSocketState)) {\n // Store the subscription request to process when WebSocket connects\n this.#pendingSubscriptions.set(subscriptionId, subscriptionRequest);\n return;\n }\n } catch {\n // Store anyway - will be processed when we can connect\n this.#pendingSubscriptions.set(subscriptionId, subscriptionRequest);\n return;\n }\n\n // Remove from pending if it was there (we're processing it now)\n this.#pendingSubscriptions.delete(subscriptionId);\n\n // Handle subscription update\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n // Check if accounts changed - if so, we need to re-subscribe to different channels\n const existingAddresses = existing.addresses ?? [];\n const addressesChanged =\n addresses.length !== existingAddresses.length ||\n addresses.some((addr) => !existingAddresses.includes(addr));\n\n if (!addressesChanged) {\n // Only chains changed - just update chains and return\n existing.chains = chainsToSubscribe;\n return;\n }\n // Accounts changed - fall through to re-subscribe with new channels\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n // Always subscribe to eip155 and solana account activity, plus any namespaces from requested chains\n const namespaces = getNamespacesForAccountActivity(chainsToSubscribe);\n\n // Build channel names: use namespace-appropriate address per account (eip155 = hex, solana = base58)\n const channels: string[] = [];\n for (const namespace of namespaces) {\n for (const { account } of request.accountsWithSupportedChains) {\n const address = getAddressForAccountActivity(account, namespace);\n if (address) {\n channels.push(buildAccountActivityChannel(namespace, address));\n }\n }\n }\n\n try {\n // Create WebSocket subscription\n const wsSubscription = await this.#messenger.call(\n 'BackendWebSocketService:subscribe',\n {\n channels,\n channelType: CHANNEL_TYPE,\n callback: (notification: ServerNotificationMessage) => {\n this.#handleNotification(notification, subscriptionId);\n },\n },\n );\n\n // Store WebSocket subscription\n this.#wsSubscriptions.set(subscriptionId, wsSubscription);\n\n // Store in abstract class tracking\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n const wsSub = this.#wsSubscriptions.get(subscriptionId);\n if (wsSub) {\n wsSub.unsubscribe().catch((unsubErr: unknown) => {\n log('Error unsubscribing', { subscriptionId, error: unsubErr });\n });\n this.#wsSubscriptions.delete(subscriptionId);\n }\n // Also clean up the stored request\n this.#subscriptionRequests.delete(subscriptionId);\n },\n chains: chainsToSubscribe,\n addresses,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Store original request for reconnection\n this.#subscriptionRequests.set(subscriptionId, subscriptionRequest);\n } catch (error) {\n log('WebSocket subscription FAILED', {\n subscriptionId,\n error,\n chains: chainsToSubscribe,\n });\n }\n }\n\n // ============================================================================\n // NOTIFICATION HANDLING\n // ============================================================================\n\n #handleNotification(\n notification: ServerNotificationMessage,\n subscriptionId: string,\n ): void {\n try {\n const subscription = this.activeSubscriptions.get(subscriptionId);\n const request = this.#subscriptionRequests.get(subscriptionId)?.request;\n if (!request) {\n return;\n }\n\n const activityMessage =\n notification.data as unknown as AccountActivityMessage;\n const { address, tx, updates } = activityMessage;\n\n if (!address || !tx || !updates) {\n return;\n }\n\n // Extract chain ID from transaction (CAIP-2 format, e.g., \"eip155:8453\")\n const chainId = tx.chain as ChainId;\n\n // Find matching account in request (eip155: case-insensitive hex; solana: exact base58)\n const account = request.accountsWithSupportedChains\n .map((entry) => entry.account)\n .find((a) =>\n a.address.startsWith('0x')\n ? a.address.toLowerCase() === address.toLowerCase()\n : a.address === address,\n );\n if (!account) {\n return;\n }\n const accountId = account.id;\n\n // Process all balance updates from the activity message\n const response = this.#processBalanceUpdates(updates, chainId, accountId);\n\n if (Object.keys(response).length > 0 && subscription) {\n Promise.resolve(subscription.onAssetsUpdate(response)).catch(\n console.error,\n );\n }\n } catch (error) {\n log('Error handling notification', error);\n }\n }\n\n /**\n * Process balance updates from AccountActivityMessage.\n * Each update contains asset info, post-transaction balance, and transfer details.\n *\n * @param updates - Array of balance updates from the activity message.\n * @param _chainId - The chain ID (unused but kept for context).\n * @param accountId - The account ID to process updates for.\n * @returns DataResponse containing processed balance and metadata.\n */\n #processBalanceUpdates(\n updates: BalanceUpdate[],\n _chainId: ChainId,\n accountId: string,\n ): DataResponse {\n const assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>> = {\n [accountId]: {},\n };\n const assetsMetadata: Record<Caip19AssetId, AssetMetadata> = {};\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n if (!asset || !postBalance) {\n continue;\n }\n\n // Asset type is in CAIP format: \"eip155:1/erc20:0x...\" or \"eip155:1/slip44:60\"\n // We can use it directly as the asset ID\n const assetId = asset.type as Caip19AssetId;\n\n // Determine token type from asset type string\n const isNative = asset.type.includes('/slip44:');\n const tokenType = isNative ? 'native' : 'erc20';\n\n // Parse balance amount (already in hex format like \"0xc350\")\n const balanceAmount = postBalance.amount.startsWith('0x')\n ? BigInt(postBalance.amount).toString()\n : postBalance.amount;\n\n assetsBalance[accountId][assetId] = {\n amount: balanceAmount,\n };\n\n assetsMetadata[assetId] = {\n type: tokenType,\n symbol: asset.unit,\n name: asset.unit, // Use unit as name (actual name may not be in the message)\n decimals: asset.decimals,\n };\n }\n\n const response: DataResponse = {};\n if (Object.keys(assetsBalance[accountId]).length > 0) {\n response.assetsBalance = assetsBalance;\n response.assetsMetadata = assetsMetadata;\n }\n\n return response;\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n if (this.#chainsRefreshTimer) {\n clearInterval(this.#chainsRefreshTimer);\n this.#chainsRefreshTimer = null;\n }\n\n // Clean up WebSocket subscriptions\n // Convert to array first to avoid modifying map during iteration\n const subscriptions = [...this.#wsSubscriptions.values()];\n for (const wsSub of subscriptions) {\n try {\n // Fire and forget - don't await in destroy\n wsSub.unsubscribe().catch(() => {\n // Ignore errors during cleanup\n });\n } catch {\n // Ignore errors during cleanup\n }\n }\n this.#wsSubscriptions.clear();\n\n // Clean up base class subscriptions\n super.destroy();\n }\n}\n\n// ============================================================================\n// FACTORY FUNCTION\n// ============================================================================\n\n/**\n * Creates a BackendWebsocketDataSource instance.\n *\n * @param options - Configuration options for the data source.\n * @returns A new BackendWebsocketDataSource instance.\n */\nexport function createBackendWebsocketDataSource(\n options: BackendWebsocketDataSourceOptions,\n): BackendWebsocketDataSource {\n return new BackendWebsocketDataSource(options);\n}\n"]}
@@ -1,80 +1,33 @@
1
1
  import type { BackendWebSocketServiceActions, BackendWebSocketServiceEvents } from "@metamask/core-backend";
2
- import type { Messenger } from "@metamask/messenger";
2
+ import type { ApiPlatformClient } from "@metamask/core-backend";
3
3
  import { AbstractDataSource } from "./AbstractDataSource.cjs";
4
4
  import type { DataSourceState, SubscriptionRequest } from "./AbstractDataSource.cjs";
5
- import type { ChainId, DataResponse } from "../types.cjs";
5
+ import type { AssetsControllerMessenger } from "../AssetsController.cjs";
6
+ import type { ChainId } from "../types.cjs";
6
7
  declare const CONTROLLER_NAME = "BackendWebsocketDataSource";
7
- export type BackendWebsocketDataSourceGetActiveChainsAction = {
8
- type: 'BackendWebsocketDataSource:getActiveChains';
9
- handler: () => Promise<ChainId[]>;
10
- };
11
- export type BackendWebsocketDataSourceSubscribeAction = {
12
- type: 'BackendWebsocketDataSource:subscribe';
13
- handler: (request: SubscriptionRequest) => Promise<void>;
14
- };
15
- export type BackendWebsocketDataSourceUnsubscribeAction = {
16
- type: 'BackendWebsocketDataSource:unsubscribe';
17
- handler: (subscriptionId: string) => Promise<void>;
18
- };
19
- export type BackendWebsocketDataSourceActions = BackendWebsocketDataSourceGetActiveChainsAction | BackendWebsocketDataSourceSubscribeAction | BackendWebsocketDataSourceUnsubscribeAction;
20
- export type BackendWebsocketDataSourceActiveChainsChangedEvent = {
21
- type: 'BackendWebsocketDataSource:activeChainsUpdated';
22
- payload: [ChainId[]];
23
- };
24
- export type BackendWebsocketDataSourceAssetsUpdatedEvent = {
25
- type: 'BackendWebsocketDataSource:assetsUpdated';
26
- payload: [DataResponse, string | undefined];
27
- };
28
- export type BackendWebsocketDataSourceEvents = BackendWebsocketDataSourceActiveChainsChangedEvent | BackendWebsocketDataSourceAssetsUpdatedEvent;
29
- type AssetsControllerActiveChainsUpdateAction = {
30
- type: 'AssetsController:activeChainsUpdate';
31
- handler: (dataSourceId: string, activeChains: ChainId[]) => void;
32
- };
33
- type AssetsControllerAssetsUpdateAction = {
34
- type: 'AssetsController:assetsUpdate';
35
- handler: (response: DataResponse, sourceId: string) => Promise<void>;
36
- };
37
- export type BackendWebsocketDataSourceAllowedActions = BackendWebSocketServiceActions | AssetsControllerActiveChainsUpdateAction | AssetsControllerAssetsUpdateAction;
38
- export type AccountsApiDataSourceActiveChainsChangedEvent = {
39
- type: 'AccountsApiDataSource:activeChainsUpdated';
40
- payload: [ChainId[]];
41
- };
42
- export type BackendWebsocketDataSourceAllowedEvents = BackendWebSocketServiceEvents | AccountsApiDataSourceActiveChainsChangedEvent;
43
- export type BackendWebsocketDataSourceMessenger = Messenger<typeof CONTROLLER_NAME, BackendWebsocketDataSourceActions | BackendWebsocketDataSourceAllowedActions, BackendWebsocketDataSourceEvents | BackendWebsocketDataSourceAllowedEvents>;
8
+ export type BackendWebsocketDataSourceAllowedActions = BackendWebSocketServiceActions;
9
+ export type BackendWebsocketDataSourceAllowedEvents = BackendWebSocketServiceEvents;
44
10
  export type BackendWebsocketDataSourceState = DataSourceState;
45
11
  export type BackendWebsocketDataSourceOptions = {
46
- messenger: BackendWebsocketDataSourceMessenger;
12
+ /** The AssetsController messenger (shared by all data sources). */
13
+ messenger: AssetsControllerMessenger;
14
+ /** ApiPlatformClient for fetching supported networks at init (same as AccountsApiDataSource). */
15
+ queryApiClient: ApiPlatformClient;
16
+ /** Called when active chains are updated (e.g. to notify AssetsController). */
17
+ onActiveChainsUpdated: (chains: ChainId[]) => void;
47
18
  state?: Partial<BackendWebsocketDataSourceState>;
48
19
  };
49
- /**
50
- * Data source for receiving real-time balance updates via WebSocket.
51
- *
52
- * This data source connects directly to BackendWebSocketService to receive
53
- * push notifications for account balance changes. Unlike AccountsApiDataSource
54
- * which polls for data, this provides instant updates.
55
- *
56
- * Uses Messenger pattern for all interactions:
57
- * - Calls BackendWebSocketService methods via messenger actions
58
- * - Exposes its own actions for AssetsController to call
59
- * - Publishes events for AssetsController to subscribe to
60
- *
61
- * Actions exposed:
62
- * - BackendWebsocketDataSource:getActiveChains
63
- * - BackendWebsocketDataSource:subscribe
64
- * - BackendWebsocketDataSource:unsubscribe
65
- *
66
- * Events published:
67
- * - BackendWebsocketDataSource:activeChainsUpdated
68
- * - BackendWebsocketDataSource:assetsUpdated
69
- *
70
- * Actions called (from BackendWebSocketService):
71
- * - BackendWebSocketService:subscribe
72
- * - BackendWebSocketService:getConnectionInfo
73
- * - BackendWebSocketService:findSubscriptionsByChannelPrefix
74
- */
75
20
  export declare class BackendWebsocketDataSource extends AbstractDataSource<typeof CONTROLLER_NAME, BackendWebsocketDataSourceState> {
76
21
  #private;
77
22
  constructor(options: BackendWebsocketDataSourceOptions);
23
+ /**
24
+ * Sync active chains from AccountsApiDataSource.
25
+ * Called by AssetsController.handleActiveChainsUpdate when the callback is
26
+ * invoked for BackendWebsocketDataSource (no messenger call; controller already updated).
27
+ *
28
+ * @param chains - Updated active chain IDs from AccountsApiDataSource.
29
+ */
30
+ setActiveChainsFromAccountsApi(chains: ChainId[]): void;
78
31
  /**
79
32
  * Update active chains when AccountsApiDataSource reports new supported chains.
80
33
  *
@@ -1 +1 @@
1
- {"version":3,"file":"BackendWebsocketDataSource.d.cts","sourceRoot":"","sources":["../../src/data-sources/BackendWebsocketDataSource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BackendWebsocketDataSource.d.cts","sourceRoot":"","sources":["../../src/data-sources/BackendWebsocketDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,8BAA8B,EAC9B,6BAA6B,EAM9B,+BAA+B;AAChC,OAAO,KAAK,EAAE,iBAAiB,EAAE,+BAA+B;AAEhE,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAC1D,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,iCAA6B;AAC9B,OAAO,KAAK,EAAE,yBAAyB,EAAE,gCAA4B;AAErE,OAAO,KAAK,EACV,OAAO,EAKR,qBAAiB;AAMlB,QAAA,MAAM,eAAe,+BAA+B,CAAC;AAUrD,MAAM,MAAM,wCAAwC,GAClD,8BAA8B,CAAC;AAGjC,MAAM,MAAM,uCAAuC,GACjD,6BAA6B,CAAC;AAMhC,MAAM,MAAM,+BAA+B,GAAG,eAAe,CAAC;AAU9D,MAAM,MAAM,iCAAiC,GAAG;IAC9C,mEAAmE;IACnE,SAAS,EAAE,yBAAyB,CAAC;IACrC,iGAAiG;IACjG,cAAc,EAAE,iBAAiB,CAAC;IAClC,+EAA+E;IAC/E,qBAAqB,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnD,KAAK,CAAC,EAAE,OAAO,CAAC,+BAA+B,CAAC,CAAC;CAClD,CAAC;AAoIF,qBAAa,0BAA2B,SAAQ,kBAAkB,CAChE,OAAO,eAAe,EACtB,+BAA+B,CAChC;;gBAmBa,OAAO,EAAE,iCAAiC;IAmFtD;;;;;;OAMG;IACH,8BAA8B,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IA8DvD;;;;OAIG;IACH,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IAUxC,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwOxE,OAAO,IAAI,IAAI;CAwBhB;AAMD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,0BAA0B,CAE5B"}