@metamask-previews/assets-controller 11.3.1-preview-a42e8d0d2 → 12.0.0-preview-d2f661012

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 (60) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/AssetsController.cjs +21 -52
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +3 -3
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +3 -3
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +21 -52
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/AccountActivityDataSource.cjs +262 -0
  11. package/dist/data-sources/AccountActivityDataSource.cjs.map +1 -0
  12. package/dist/data-sources/AccountActivityDataSource.d.cts +80 -0
  13. package/dist/data-sources/AccountActivityDataSource.d.cts.map +1 -0
  14. package/dist/data-sources/AccountActivityDataSource.d.mts +80 -0
  15. package/dist/data-sources/AccountActivityDataSource.d.mts.map +1 -0
  16. package/dist/data-sources/AccountActivityDataSource.mjs +254 -0
  17. package/dist/data-sources/AccountActivityDataSource.mjs.map +1 -0
  18. package/dist/data-sources/AccountsApiDataSource.cjs +22 -0
  19. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  20. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  21. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  22. package/dist/data-sources/AccountsApiDataSource.mjs +22 -0
  23. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  24. package/dist/data-sources/index.cjs +4 -4
  25. package/dist/data-sources/index.cjs.map +1 -1
  26. package/dist/data-sources/index.d.cts +1 -1
  27. package/dist/data-sources/index.d.cts.map +1 -1
  28. package/dist/data-sources/index.d.mts +1 -1
  29. package/dist/data-sources/index.d.mts.map +1 -1
  30. package/dist/data-sources/index.mjs +1 -1
  31. package/dist/data-sources/index.mjs.map +1 -1
  32. package/dist/index.cjs +22 -26
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +0 -2
  35. package/dist/index.d.cts.map +1 -1
  36. package/dist/index.d.mts +0 -2
  37. package/dist/index.d.mts.map +1 -1
  38. package/dist/index.mjs +0 -2
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/middlewares/CustomAssetGraduationMiddleware.cjs +1 -1
  41. package/dist/middlewares/CustomAssetGraduationMiddleware.cjs.map +1 -1
  42. package/dist/middlewares/CustomAssetGraduationMiddleware.mjs +1 -1
  43. package/dist/middlewares/CustomAssetGraduationMiddleware.mjs.map +1 -1
  44. package/package.json +9 -9
  45. package/dist/data-sources/BackendWebsocketDataSource.cjs +0 -597
  46. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +0 -1
  47. package/dist/data-sources/BackendWebsocketDataSource.d.cts +0 -65
  48. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +0 -1
  49. package/dist/data-sources/BackendWebsocketDataSource.d.mts +0 -65
  50. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +0 -1
  51. package/dist/data-sources/BackendWebsocketDataSource.mjs +0 -592
  52. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +0 -1
  53. package/dist/utils/processAccountActivityBalanceUpdates.cjs +0 -54
  54. package/dist/utils/processAccountActivityBalanceUpdates.cjs.map +0 -1
  55. package/dist/utils/processAccountActivityBalanceUpdates.d.cts +0 -13
  56. package/dist/utils/processAccountActivityBalanceUpdates.d.cts.map +0 -1
  57. package/dist/utils/processAccountActivityBalanceUpdates.d.mts +0 -13
  58. package/dist/utils/processAccountActivityBalanceUpdates.d.mts.map +0 -1
  59. package/dist/utils/processAccountActivityBalanceUpdates.mjs +0 -47
  60. package/dist/utils/processAccountActivityBalanceUpdates.mjs.map +0 -1
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ var _AccountActivityDataSource_instances, _AccountActivityDataSource_messenger, _AccountActivityDataSource_onActiveChainsUpdated, _AccountActivityDataSource_getAssetType, _AccountActivityDataSource_onAssetsUpdate, _AccountActivityDataSource_onBalanceUpdatedBound, _AccountActivityDataSource_subscribeToEvents, _AccountActivityDataSource_onBalanceUpdated, _AccountActivityDataSource_findAccountForAddress, _AccountActivityDataSource_getWalletAccounts, _AccountActivityDataSource_onAccountActivityStatusChanged;
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.createAccountActivityDataSource = exports.AccountActivityDataSource = void 0;
19
+ const utils_1 = require("@metamask/utils");
20
+ const bignumber_js_1 = __importDefault(require("bignumber.js"));
21
+ const logger_js_1 = require("../logger.cjs");
22
+ const AbstractDataSource_js_1 = require("./AbstractDataSource.cjs");
23
+ // ============================================================================
24
+ // CONSTANTS
25
+ // ============================================================================
26
+ const CONTROLLER_NAME = 'AccountActivityDataSource';
27
+ const log = (0, logger_js_1.createModuleLogger)(logger_js_1.projectLogger, CONTROLLER_NAME);
28
+ // ============================================================================
29
+ // BALANCE UPDATE PROCESSING
30
+ // ============================================================================
31
+ /**
32
+ * Convert AccountActivityMessage balance updates into a {@link DataResponse}
33
+ * for AssetsController.
34
+ *
35
+ * @param updates - Balance updates from account-activity websocket payload.
36
+ * @param accountId - Internal account UUID.
37
+ * @param getAssetType - Resolver for asset metadata type.
38
+ * @returns DataResponse with merge mode when balances are present.
39
+ */
40
+ function processAccountActivityBalanceUpdates(updates, accountId, getAssetType) {
41
+ const assetsBalance = Object.create(null);
42
+ assetsBalance[accountId] = Object.create(null);
43
+ const assetsMetadata = Object.create(null);
44
+ for (const update of updates) {
45
+ const { asset, postBalance } = update;
46
+ if (!asset || !postBalance) {
47
+ continue;
48
+ }
49
+ const assetId = asset.type;
50
+ if (asset.decimals === undefined) {
51
+ continue;
52
+ }
53
+ const rawBalanceStr = postBalance.amount.startsWith('0x')
54
+ ? BigInt(postBalance.amount).toString()
55
+ : postBalance.amount;
56
+ const humanReadableAmount = new bignumber_js_1.default(rawBalanceStr)
57
+ .dividedBy(new bignumber_js_1.default(10).pow(asset.decimals))
58
+ .toFixed();
59
+ assetsBalance[accountId][assetId] = {
60
+ amount: humanReadableAmount,
61
+ };
62
+ assetsMetadata[assetId] = {
63
+ type: getAssetType(assetId),
64
+ symbol: asset.unit,
65
+ name: asset.unit,
66
+ decimals: asset.decimals,
67
+ };
68
+ }
69
+ const response = { updateMode: 'merge' };
70
+ if (Object.keys(assetsBalance[accountId]).length > 0) {
71
+ response.assetsBalance = assetsBalance;
72
+ response.assetsInfo = assetsMetadata;
73
+ }
74
+ return response;
75
+ }
76
+ const defaultState = {
77
+ activeChains: [],
78
+ };
79
+ // ============================================================================
80
+ // ACCOUNT ACTIVITY DATA SOURCE
81
+ // ============================================================================
82
+ /**
83
+ * Data source that consumes real-time updates from `AccountActivityService`.
84
+ *
85
+ * `AccountActivityService` owns the WebSocket connection and channel
86
+ * subscriptions; this data source is a thin consumer of the two high-level
87
+ * events that it publishes:
88
+ *
89
+ * - `AccountActivityService:balanceUpdated` — post-transaction balances for the
90
+ * subscribed account(s). The address is resolved against the wallet's
91
+ * selected account group, transformed into a {@link DataResponse} (via
92
+ * {@link processAccountActivityBalanceUpdates}), and pushed to the controller
93
+ * through the injected `onAssetsUpdate` callback.
94
+ * - `AccountActivityService:statusChanged` — per-chain "up"/"down" notifications.
95
+ * A chain reported "up" is claimed as an active chain (WebSocket is providing
96
+ * real-time data); a chain reported "down" is released so polling data sources
97
+ * can take over. Each change is applied directly via `updateActiveChains`,
98
+ * which notifies the controller through `onActiveChainsUpdated`.
99
+ *
100
+ * This data source does NOT debounce, jitter, or gate chain updates itself.
101
+ * Coalescing (collapsing bursts) and jitter (staggering the WS-subscribe herd
102
+ * across clients) live in `AssetsController`, where the expensive re-subscribe
103
+ * happens and where updates from all data sources converge. `activeChains` are
104
+ * never seeded from the accounts API; they only ever reflect live
105
+ * `statusChanged` events (the service flushes all tracked chains as "down" when
106
+ * the WebSocket disconnects, releasing them).
107
+ *
108
+ * It subscribes to these events in its constructor (the same way
109
+ * `TokenBalancesController` does). Unlike the polling data sources it does not
110
+ * take part in the controller's subscribe/unsubscribe handoff: `subscribe` is a
111
+ * no-op, balance updates are routed by resolving the address against the
112
+ * wallet, and updates are pushed through the injected `onAssetsUpdate` callback.
113
+ */
114
+ class AccountActivityDataSource extends AbstractDataSource_js_1.AbstractDataSource {
115
+ constructor(options) {
116
+ super(CONTROLLER_NAME, {
117
+ ...defaultState,
118
+ ...options.state,
119
+ });
120
+ _AccountActivityDataSource_instances.add(this);
121
+ _AccountActivityDataSource_messenger.set(this, void 0);
122
+ _AccountActivityDataSource_onActiveChainsUpdated.set(this, void 0);
123
+ _AccountActivityDataSource_getAssetType.set(this, void 0);
124
+ _AccountActivityDataSource_onAssetsUpdate.set(this, void 0);
125
+ _AccountActivityDataSource_onBalanceUpdatedBound.set(this, void 0);
126
+ // ============================================================================
127
+ // STATUS CHANGES
128
+ // ============================================================================
129
+ /**
130
+ * Handle a `statusChanged` notification. Chains reported "up" are claimed as
131
+ * active (WebSocket provides real-time data); chains reported "down" are
132
+ * released so polling data sources take over. The change is applied directly;
133
+ * coalescing and jitter are handled by `AssetsController`.
134
+ *
135
+ * @param options - The status change notification.
136
+ * @param options.chainIds - The CAIP-2 chain IDs whose status changed.
137
+ * @param options.status - Whether the chains are `up` or `down`.
138
+ * @param options.timestamp - Optional timestamp of the status change.
139
+ */
140
+ _AccountActivityDataSource_onAccountActivityStatusChanged.set(this, ({ chainIds, status, }) => {
141
+ try {
142
+ // Act on every namespace (eip155, solana, etc.); AssetsController is
143
+ // multichain. Only skip identifiers that are not valid CAIP-2 chain IDs.
144
+ const validChains = chainIds.filter((chainId) => (0, utils_1.isCaipChainId)(chainId));
145
+ if (validChains.length === 0) {
146
+ return;
147
+ }
148
+ const next = new Set(this.state.activeChains);
149
+ if (status === 'up') {
150
+ for (const chainId of validChains) {
151
+ next.add(chainId);
152
+ }
153
+ }
154
+ else {
155
+ for (const chainId of validChains) {
156
+ next.delete(chainId);
157
+ }
158
+ }
159
+ const previous = [...this.state.activeChains];
160
+ this.updateActiveChains(Array.from(next), (updatedChains) => __classPrivateFieldGet(this, _AccountActivityDataSource_onActiveChainsUpdated, "f").call(this, this.getName(), updatedChains, previous));
161
+ }
162
+ catch (error) {
163
+ log('Error handling status change', error);
164
+ }
165
+ });
166
+ __classPrivateFieldSet(this, _AccountActivityDataSource_messenger, options.messenger, "f");
167
+ __classPrivateFieldSet(this, _AccountActivityDataSource_onActiveChainsUpdated, options.onActiveChainsUpdated, "f");
168
+ __classPrivateFieldSet(this, _AccountActivityDataSource_getAssetType, options.getAssetType, "f");
169
+ __classPrivateFieldSet(this, _AccountActivityDataSource_onAssetsUpdate, options.onAssetsUpdate, "f");
170
+ __classPrivateFieldSet(this, _AccountActivityDataSource_onBalanceUpdatedBound, __classPrivateFieldGet(this, _AccountActivityDataSource_instances, "m", _AccountActivityDataSource_onBalanceUpdated).bind(this), "f");
171
+ __classPrivateFieldGet(this, _AccountActivityDataSource_instances, "m", _AccountActivityDataSource_subscribeToEvents).call(this);
172
+ }
173
+ // ============================================================================
174
+ // SUBSCRIBE / UNSUBSCRIBE
175
+ // ============================================================================
176
+ /**
177
+ * AADS is event-driven and chain-agnostic: it never participates in the
178
+ * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`
179
+ * events are routed by resolving the address against the wallet's accounts
180
+ * (see `#onBalanceUpdated`), and updates are pushed through the injected
181
+ * `#onAssetsUpdate` callback. This override exists only to satisfy the
182
+ * abstract contract and is intentionally a no-op.
183
+ */
184
+ async subscribe() {
185
+ // Intentionally empty — see method doc.
186
+ }
187
+ // ============================================================================
188
+ // CLEANUP
189
+ // ============================================================================
190
+ destroy() {
191
+ try {
192
+ __classPrivateFieldGet(this, _AccountActivityDataSource_messenger, "f").unsubscribe('AccountActivityService:balanceUpdated', __classPrivateFieldGet(this, _AccountActivityDataSource_onBalanceUpdatedBound, "f"));
193
+ }
194
+ catch (error) {
195
+ log('Failed to unsubscribe from balanceUpdated', { error });
196
+ }
197
+ try {
198
+ __classPrivateFieldGet(this, _AccountActivityDataSource_messenger, "f").unsubscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _AccountActivityDataSource_onAccountActivityStatusChanged, "f"));
199
+ }
200
+ catch (error) {
201
+ log('Failed to unsubscribe from statusChanged', { error });
202
+ }
203
+ super.destroy();
204
+ }
205
+ }
206
+ exports.AccountActivityDataSource = AccountActivityDataSource;
207
+ _AccountActivityDataSource_messenger = new WeakMap(), _AccountActivityDataSource_onActiveChainsUpdated = new WeakMap(), _AccountActivityDataSource_getAssetType = new WeakMap(), _AccountActivityDataSource_onAssetsUpdate = new WeakMap(), _AccountActivityDataSource_onBalanceUpdatedBound = new WeakMap(), _AccountActivityDataSource_onAccountActivityStatusChanged = new WeakMap(), _AccountActivityDataSource_instances = new WeakSet(), _AccountActivityDataSource_subscribeToEvents = function _AccountActivityDataSource_subscribeToEvents() {
208
+ __classPrivateFieldGet(this, _AccountActivityDataSource_messenger, "f").subscribe('AccountActivityService:balanceUpdated', __classPrivateFieldGet(this, _AccountActivityDataSource_onBalanceUpdatedBound, "f"));
209
+ __classPrivateFieldGet(this, _AccountActivityDataSource_messenger, "f").subscribe('AccountActivityService:statusChanged', __classPrivateFieldGet(this, _AccountActivityDataSource_onAccountActivityStatusChanged, "f"));
210
+ }, _AccountActivityDataSource_onBalanceUpdated = function _AccountActivityDataSource_onBalanceUpdated({ address, chain, updates, }) {
211
+ try {
212
+ if (!address || !chain || !updates || updates.length === 0) {
213
+ return;
214
+ }
215
+ const account = __classPrivateFieldGet(this, _AccountActivityDataSource_instances, "m", _AccountActivityDataSource_findAccountForAddress).call(this, address);
216
+ if (!account) {
217
+ return;
218
+ }
219
+ const chainId = chain;
220
+ const response = processAccountActivityBalanceUpdates(updates, account.id, (assetId) => __classPrivateFieldGet(this, _AccountActivityDataSource_getAssetType, "f").call(this, assetId));
221
+ if (!response.assetsBalance) {
222
+ return;
223
+ }
224
+ const request = {
225
+ accountsWithSupportedChains: [{ account, supportedChains: [chainId] }],
226
+ chainIds: [chainId],
227
+ dataTypes: ['balance', 'metadata'],
228
+ };
229
+ Promise.resolve(__classPrivateFieldGet(this, _AccountActivityDataSource_onAssetsUpdate, "f").call(this, response, request)).catch((error) => {
230
+ log('Failed to report balance update', { error });
231
+ });
232
+ }
233
+ catch (error) {
234
+ log('Error handling balance update', error);
235
+ }
236
+ }, _AccountActivityDataSource_findAccountForAddress = function _AccountActivityDataSource_findAccountForAddress(address) {
237
+ const isEvm = address.startsWith('0x');
238
+ return (__classPrivateFieldGet(this, _AccountActivityDataSource_instances, "m", _AccountActivityDataSource_getWalletAccounts).call(this).find((candidate) => isEvm
239
+ ? candidate.address.toLowerCase() === address.toLowerCase()
240
+ : candidate.address === address) ?? null);
241
+ }, _AccountActivityDataSource_getWalletAccounts = function _AccountActivityDataSource_getWalletAccounts() {
242
+ const accounts = __classPrivateFieldGet(this, _AccountActivityDataSource_messenger, "f").call('AccountTreeController:getAccountsFromSelectedAccountGroup');
243
+ if (accounts.length > 0) {
244
+ return accounts;
245
+ }
246
+ const selectedAccount = __classPrivateFieldGet(this, _AccountActivityDataSource_messenger, "f").call('AccountsController:getSelectedAccount');
247
+ return selectedAccount ? [selectedAccount] : [];
248
+ };
249
+ // ============================================================================
250
+ // FACTORY FUNCTION
251
+ // ============================================================================
252
+ /**
253
+ * Creates an AccountActivityDataSource instance.
254
+ *
255
+ * @param options - Configuration options for the data source.
256
+ * @returns A new AccountActivityDataSource instance.
257
+ */
258
+ function createAccountActivityDataSource(options) {
259
+ return new AccountActivityDataSource(options);
260
+ }
261
+ exports.createAccountActivityDataSource = createAccountActivityDataSource;
262
+ //# sourceMappingURL=AccountActivityDataSource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountActivityDataSource.cjs","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountActivityServiceBalanceUpdatedEvent,\n AccountActivityServiceStatusChangedEvent,\n BalanceUpdate,\n} from '@metamask/core-backend';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { isCaipChainId } from '@metamask/utils';\nimport BigNumberJS from 'bignumber.js';\n\nimport type { AssetsControllerMessenger } from '../AssetsController.js';\nimport { projectLogger, createModuleLogger } from '../logger.js';\nimport type {\n AssetBalance,\n AssetMetadata,\n ChainId,\n Caip19AssetId,\n DataRequest,\n DataResponse,\n} from '../types.js';\nimport { AbstractDataSource } from './AbstractDataSource.js';\nimport type { DataSourceState } from './AbstractDataSource.js';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountActivityDataSource';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// BALANCE UPDATE PROCESSING\n// ============================================================================\n\n/**\n * Convert AccountActivityMessage balance updates into a {@link DataResponse}\n * for AssetsController.\n *\n * @param updates - Balance updates from account-activity websocket payload.\n * @param accountId - Internal account UUID.\n * @param getAssetType - Resolver for asset metadata type.\n * @returns DataResponse with merge mode when balances are present.\n */\nfunction processAccountActivityBalanceUpdates(\n updates: BalanceUpdate[],\n accountId: string,\n getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl',\n): DataResponse {\n const assetsBalance = Object.create(null) as Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n >;\n assetsBalance[accountId] = Object.create(null) as Record<\n Caip19AssetId,\n AssetBalance\n >;\n const assetsMetadata = Object.create(null) as Record<\n Caip19AssetId,\n AssetMetadata\n >;\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n if (!asset || !postBalance) {\n continue;\n }\n\n const assetId = asset.type as Caip19AssetId;\n\n if (asset.decimals === undefined) {\n continue;\n }\n\n const rawBalanceStr = postBalance.amount.startsWith('0x')\n ? BigInt(postBalance.amount).toString()\n : postBalance.amount;\n\n const humanReadableAmount = new BigNumberJS(rawBalanceStr)\n .dividedBy(new BigNumberJS(10).pow(asset.decimals))\n .toFixed();\n\n assetsBalance[accountId][assetId] = {\n amount: humanReadableAmount,\n };\n\n assetsMetadata[assetId] = {\n type: getAssetType(assetId),\n symbol: asset.unit,\n name: asset.unit,\n decimals: asset.decimals,\n };\n }\n\n const response: DataResponse = { updateMode: 'merge' };\n if (Object.keys(assetsBalance[accountId]).length > 0) {\n response.assetsBalance = assetsBalance;\n response.assetsInfo = assetsMetadata;\n }\n\n return response;\n}\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/** Allowed events that AccountActivityDataSource subscribes to. */\nexport type AccountActivityDataSourceAllowedEvents =\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceStatusChangedEvent;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountActivityDataSourceState = DataSourceState;\n\nconst defaultState: AccountActivityDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type AccountActivityDataSourceOptions = {\n /** The AssetsController messenger (shared by all data sources). */\n messenger: AssetsControllerMessenger;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n /** Returns the asset type ('native' | 'erc20' | 'spl') for a given CAIP-19 asset ID. */\n getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl';\n /**\n * Pushes decoded balance updates to the controller (bound to\n * `AssetsController.handleAssetsUpdate`). AADS is event-driven and never\n * takes part in the controller's subscribe handoff, so it receives this\n * callback directly instead of via a `SubscriptionRequest`.\n */\n onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n state?: Partial<AccountActivityDataSourceState>;\n};\n\n// ============================================================================\n// ACCOUNT ACTIVITY DATA SOURCE\n// ============================================================================\n\n/**\n * Data source that consumes real-time updates from `AccountActivityService`.\n *\n * `AccountActivityService` owns the WebSocket connection and channel\n * subscriptions; this data source is a thin consumer of the two high-level\n * events that it publishes:\n *\n * - `AccountActivityService:balanceUpdated` — post-transaction balances for the\n * subscribed account(s). The address is resolved against the wallet's\n * selected account group, transformed into a {@link DataResponse} (via\n * {@link processAccountActivityBalanceUpdates}), and pushed to the controller\n * through the injected `onAssetsUpdate` callback.\n * - `AccountActivityService:statusChanged` — per-chain \"up\"/\"down\" notifications.\n * A chain reported \"up\" is claimed as an active chain (WebSocket is providing\n * real-time data); a chain reported \"down\" is released so polling data sources\n * can take over. Each change is applied directly via `updateActiveChains`,\n * which notifies the controller through `onActiveChainsUpdated`.\n *\n * This data source does NOT debounce, jitter, or gate chain updates itself.\n * Coalescing (collapsing bursts) and jitter (staggering the WS-subscribe herd\n * across clients) live in `AssetsController`, where the expensive re-subscribe\n * happens and where updates from all data sources converge. `activeChains` are\n * never seeded from the accounts API; they only ever reflect live\n * `statusChanged` events (the service flushes all tracked chains as \"down\" when\n * the WebSocket disconnects, releasing them).\n *\n * It subscribes to these events in its constructor (the same way\n * `TokenBalancesController` does). Unlike the polling data sources it does not\n * take part in the controller's subscribe/unsubscribe handoff: `subscribe` is a\n * no-op, balance updates are routed by resolving the address against the\n * wallet, and updates are pushed through the injected `onAssetsUpdate` callback.\n */\nexport class AccountActivityDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountActivityDataSourceState\n> {\n readonly #messenger: AssetsControllerMessenger;\n\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #getAssetType: (\n assetId: Caip19AssetId,\n ) => 'native' | 'erc20' | 'spl';\n\n readonly #onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n\n readonly #onBalanceUpdatedBound: (event: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }) => void;\n\n constructor(options: AccountActivityDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#getAssetType = options.getAssetType;\n this.#onAssetsUpdate = options.onAssetsUpdate;\n this.#onBalanceUpdatedBound = this.#onBalanceUpdated.bind(this);\n\n this.#subscribeToEvents();\n }\n\n // ============================================================================\n // EVENT SUBSCRIPTIONS\n // ============================================================================\n\n #subscribeToEvents(): void {\n this.#messenger.subscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onBalanceUpdatedBound,\n );\n this.#messenger.subscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged,\n );\n }\n\n // ============================================================================\n // SUBSCRIBE / UNSUBSCRIBE\n // ============================================================================\n\n /**\n * AADS is event-driven and chain-agnostic: it never participates in the\n * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`\n * events are routed by resolving the address against the wallet's accounts\n * (see `#onBalanceUpdated`), and updates are pushed through the injected\n * `#onAssetsUpdate` callback. This override exists only to satisfy the\n * abstract contract and is intentionally a no-op.\n */\n async subscribe(): Promise<void> {\n // Intentionally empty — see method doc.\n }\n\n // ============================================================================\n // BALANCE UPDATES\n // ============================================================================\n\n #onBalanceUpdated({\n address,\n chain,\n updates,\n }: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }): void {\n try {\n if (!address || !chain || !updates || updates.length === 0) {\n return;\n }\n\n const account = this.#findAccountForAddress(address);\n if (!account) {\n return;\n }\n\n const chainId = chain as ChainId;\n\n const response = processAccountActivityBalanceUpdates(\n updates,\n account.id,\n (assetId) => this.#getAssetType(assetId),\n );\n\n if (!response.assetsBalance) {\n return;\n }\n\n const request: DataRequest = {\n accountsWithSupportedChains: [{ account, supportedChains: [chainId] }],\n chainIds: [chainId],\n dataTypes: ['balance', 'metadata'],\n };\n\n Promise.resolve(this.#onAssetsUpdate(response, request)).catch(\n (error) => {\n log('Failed to report balance update', { error });\n },\n );\n } catch (error) {\n log('Error handling balance update', error);\n }\n }\n\n /**\n * Find the wallet account matching the given activity address. EVM addresses\n * are matched case-insensitively; other namespaces are matched exactly.\n *\n * The candidate set is the accounts of the selected account group, resolved\n * from the wallet at event time (rather than from a stored subscription), so\n * AADS does not need to be re-subscribed whenever the account set changes.\n *\n * @param address - The account address from the activity message.\n * @returns The matching account, or null.\n */\n #findAccountForAddress(address: string): InternalAccount | null {\n const isEvm = address.startsWith('0x');\n\n return (\n this.#getWalletAccounts().find((candidate) =>\n isEvm\n ? candidate.address.toLowerCase() === address.toLowerCase()\n : candidate.address === address,\n ) ?? null\n );\n }\n\n /**\n * Resolve the accounts of the selected account group from the wallet,\n * mirroring `AssetsController.#getSelectedAccounts`.\n *\n * @returns The accounts of the selected account group.\n */\n #getWalletAccounts(): InternalAccount[] {\n const accounts = this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n if (accounts.length > 0) {\n return accounts;\n }\n\n const selectedAccount = this.#messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n return selectedAccount ? [selectedAccount] : [];\n }\n\n // ============================================================================\n // STATUS CHANGES\n // ============================================================================\n\n /**\n * Handle a `statusChanged` notification. Chains reported \"up\" are claimed as\n * active (WebSocket provides real-time data); chains reported \"down\" are\n * released so polling data sources take over. The change is applied directly;\n * coalescing and jitter are handled by `AssetsController`.\n *\n * @param options - The status change notification.\n * @param options.chainIds - The CAIP-2 chain IDs whose status changed.\n * @param options.status - Whether the chains are `up` or `down`.\n * @param options.timestamp - Optional timestamp of the status change.\n */\n readonly #onAccountActivityStatusChanged = ({\n chainIds,\n status,\n }: {\n chainIds: string[];\n status: 'up' | 'down';\n timestamp?: number;\n }): void => {\n try {\n // Act on every namespace (eip155, solana, etc.); AssetsController is\n // multichain. Only skip identifiers that are not valid CAIP-2 chain IDs.\n const validChains = chainIds.filter((chainId) =>\n isCaipChainId(chainId),\n ) as ChainId[];\n\n if (validChains.length === 0) {\n return;\n }\n\n const next = new Set(this.state.activeChains);\n if (status === 'up') {\n for (const chainId of validChains) {\n next.add(chainId);\n }\n } else {\n for (const chainId of validChains) {\n next.delete(chainId);\n }\n }\n\n const previous = [...this.state.activeChains];\n this.updateActiveChains(Array.from(next), (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n } catch (error) {\n log('Error handling status change', error);\n }\n };\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n try {\n this.#messenger.unsubscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onBalanceUpdatedBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from balanceUpdated', { error });\n }\n\n try {\n this.#messenger.unsubscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged,\n );\n } catch (error) {\n log('Failed to unsubscribe from statusChanged', { error });\n }\n\n super.destroy();\n }\n}\n\n// ============================================================================\n// FACTORY FUNCTION\n// ============================================================================\n\n/**\n * Creates an AccountActivityDataSource instance.\n *\n * @param options - Configuration options for the data source.\n * @returns A new AccountActivityDataSource instance.\n */\nexport function createAccountActivityDataSource(\n options: AccountActivityDataSourceOptions,\n): AccountActivityDataSource {\n return new AccountActivityDataSource(options);\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import type { AccountActivityServiceBalanceUpdatedEvent, AccountActivityServiceStatusChangedEvent } from "@metamask/core-backend";
2
+ import type { AssetsControllerMessenger } from "../AssetsController.cjs";
3
+ import type { ChainId, Caip19AssetId, DataRequest, DataResponse } from "../types.cjs";
4
+ import { AbstractDataSource } from "./AbstractDataSource.cjs";
5
+ import type { DataSourceState } from "./AbstractDataSource.cjs";
6
+ declare const CONTROLLER_NAME = "AccountActivityDataSource";
7
+ /** Allowed events that AccountActivityDataSource subscribes to. */
8
+ export type AccountActivityDataSourceAllowedEvents = AccountActivityServiceBalanceUpdatedEvent | AccountActivityServiceStatusChangedEvent;
9
+ export type AccountActivityDataSourceState = DataSourceState;
10
+ export type AccountActivityDataSourceOptions = {
11
+ /** The AssetsController messenger (shared by all data sources). */
12
+ messenger: AssetsControllerMessenger;
13
+ /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
14
+ onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
15
+ /** Returns the asset type ('native' | 'erc20' | 'spl') for a given CAIP-19 asset ID. */
16
+ getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl';
17
+ /**
18
+ * Pushes decoded balance updates to the controller (bound to
19
+ * `AssetsController.handleAssetsUpdate`). AADS is event-driven and never
20
+ * takes part in the controller's subscribe handoff, so it receives this
21
+ * callback directly instead of via a `SubscriptionRequest`.
22
+ */
23
+ onAssetsUpdate: (response: DataResponse, request?: DataRequest) => void | Promise<void>;
24
+ state?: Partial<AccountActivityDataSourceState>;
25
+ };
26
+ /**
27
+ * Data source that consumes real-time updates from `AccountActivityService`.
28
+ *
29
+ * `AccountActivityService` owns the WebSocket connection and channel
30
+ * subscriptions; this data source is a thin consumer of the two high-level
31
+ * events that it publishes:
32
+ *
33
+ * - `AccountActivityService:balanceUpdated` — post-transaction balances for the
34
+ * subscribed account(s). The address is resolved against the wallet's
35
+ * selected account group, transformed into a {@link DataResponse} (via
36
+ * {@link processAccountActivityBalanceUpdates}), and pushed to the controller
37
+ * through the injected `onAssetsUpdate` callback.
38
+ * - `AccountActivityService:statusChanged` — per-chain "up"/"down" notifications.
39
+ * A chain reported "up" is claimed as an active chain (WebSocket is providing
40
+ * real-time data); a chain reported "down" is released so polling data sources
41
+ * can take over. Each change is applied directly via `updateActiveChains`,
42
+ * which notifies the controller through `onActiveChainsUpdated`.
43
+ *
44
+ * This data source does NOT debounce, jitter, or gate chain updates itself.
45
+ * Coalescing (collapsing bursts) and jitter (staggering the WS-subscribe herd
46
+ * across clients) live in `AssetsController`, where the expensive re-subscribe
47
+ * happens and where updates from all data sources converge. `activeChains` are
48
+ * never seeded from the accounts API; they only ever reflect live
49
+ * `statusChanged` events (the service flushes all tracked chains as "down" when
50
+ * the WebSocket disconnects, releasing them).
51
+ *
52
+ * It subscribes to these events in its constructor (the same way
53
+ * `TokenBalancesController` does). Unlike the polling data sources it does not
54
+ * take part in the controller's subscribe/unsubscribe handoff: `subscribe` is a
55
+ * no-op, balance updates are routed by resolving the address against the
56
+ * wallet, and updates are pushed through the injected `onAssetsUpdate` callback.
57
+ */
58
+ export declare class AccountActivityDataSource extends AbstractDataSource<typeof CONTROLLER_NAME, AccountActivityDataSourceState> {
59
+ #private;
60
+ constructor(options: AccountActivityDataSourceOptions);
61
+ /**
62
+ * AADS is event-driven and chain-agnostic: it never participates in the
63
+ * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`
64
+ * events are routed by resolving the address against the wallet's accounts
65
+ * (see `#onBalanceUpdated`), and updates are pushed through the injected
66
+ * `#onAssetsUpdate` callback. This override exists only to satisfy the
67
+ * abstract contract and is intentionally a no-op.
68
+ */
69
+ subscribe(): Promise<void>;
70
+ destroy(): void;
71
+ }
72
+ /**
73
+ * Creates an AccountActivityDataSource instance.
74
+ *
75
+ * @param options - Configuration options for the data source.
76
+ * @returns A new AccountActivityDataSource instance.
77
+ */
78
+ export declare function createAccountActivityDataSource(options: AccountActivityDataSourceOptions): AccountActivityDataSource;
79
+ export {};
80
+ //# sourceMappingURL=AccountActivityDataSource.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountActivityDataSource.d.cts","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yCAAyC,EACzC,wCAAwC,EAEzC,+BAA+B;AAKhC,OAAO,KAAK,EAAE,yBAAyB,EAAE,gCAA+B;AAExE,OAAO,KAAK,EAGV,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACb,qBAAoB;AACrB,OAAO,EAAE,kBAAkB,EAAE,iCAAgC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,iCAAgC;AAM/D,QAAA,MAAM,eAAe,8BAA8B,CAAC;AAiFpD,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAC9C,yCAAyC,GACzC,wCAAwC,CAAC;AAM7C,MAAM,MAAM,8BAA8B,GAAG,eAAe,CAAC;AAU7D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,mEAAmE;IACnE,SAAS,EAAE,yBAAyB,CAAC;IACrC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,wFAAwF;IACxF,YAAY,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IACrE;;;;;OAKG;IACH,cAAc,EAAE,CACd,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,WAAW,KAClB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CACjD,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,yBAA0B,SAAQ,kBAAkB,CAC/D,OAAO,eAAe,EACtB,8BAA8B,CAC/B;;gBAwBa,OAAO,EAAE,gCAAgC;IAkCrD;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA4JhC,OAAO,IAAI,IAAI;CAqBhB;AAMD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,yBAAyB,CAE3B"}
@@ -0,0 +1,80 @@
1
+ import type { AccountActivityServiceBalanceUpdatedEvent, AccountActivityServiceStatusChangedEvent } from "@metamask/core-backend";
2
+ import type { AssetsControllerMessenger } from "../AssetsController.mjs";
3
+ import type { ChainId, Caip19AssetId, DataRequest, DataResponse } from "../types.mjs";
4
+ import { AbstractDataSource } from "./AbstractDataSource.mjs";
5
+ import type { DataSourceState } from "./AbstractDataSource.mjs";
6
+ declare const CONTROLLER_NAME = "AccountActivityDataSource";
7
+ /** Allowed events that AccountActivityDataSource subscribes to. */
8
+ export type AccountActivityDataSourceAllowedEvents = AccountActivityServiceBalanceUpdatedEvent | AccountActivityServiceStatusChangedEvent;
9
+ export type AccountActivityDataSourceState = DataSourceState;
10
+ export type AccountActivityDataSourceOptions = {
11
+ /** The AssetsController messenger (shared by all data sources). */
12
+ messenger: AssetsControllerMessenger;
13
+ /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
14
+ onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
15
+ /** Returns the asset type ('native' | 'erc20' | 'spl') for a given CAIP-19 asset ID. */
16
+ getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl';
17
+ /**
18
+ * Pushes decoded balance updates to the controller (bound to
19
+ * `AssetsController.handleAssetsUpdate`). AADS is event-driven and never
20
+ * takes part in the controller's subscribe handoff, so it receives this
21
+ * callback directly instead of via a `SubscriptionRequest`.
22
+ */
23
+ onAssetsUpdate: (response: DataResponse, request?: DataRequest) => void | Promise<void>;
24
+ state?: Partial<AccountActivityDataSourceState>;
25
+ };
26
+ /**
27
+ * Data source that consumes real-time updates from `AccountActivityService`.
28
+ *
29
+ * `AccountActivityService` owns the WebSocket connection and channel
30
+ * subscriptions; this data source is a thin consumer of the two high-level
31
+ * events that it publishes:
32
+ *
33
+ * - `AccountActivityService:balanceUpdated` — post-transaction balances for the
34
+ * subscribed account(s). The address is resolved against the wallet's
35
+ * selected account group, transformed into a {@link DataResponse} (via
36
+ * {@link processAccountActivityBalanceUpdates}), and pushed to the controller
37
+ * through the injected `onAssetsUpdate` callback.
38
+ * - `AccountActivityService:statusChanged` — per-chain "up"/"down" notifications.
39
+ * A chain reported "up" is claimed as an active chain (WebSocket is providing
40
+ * real-time data); a chain reported "down" is released so polling data sources
41
+ * can take over. Each change is applied directly via `updateActiveChains`,
42
+ * which notifies the controller through `onActiveChainsUpdated`.
43
+ *
44
+ * This data source does NOT debounce, jitter, or gate chain updates itself.
45
+ * Coalescing (collapsing bursts) and jitter (staggering the WS-subscribe herd
46
+ * across clients) live in `AssetsController`, where the expensive re-subscribe
47
+ * happens and where updates from all data sources converge. `activeChains` are
48
+ * never seeded from the accounts API; they only ever reflect live
49
+ * `statusChanged` events (the service flushes all tracked chains as "down" when
50
+ * the WebSocket disconnects, releasing them).
51
+ *
52
+ * It subscribes to these events in its constructor (the same way
53
+ * `TokenBalancesController` does). Unlike the polling data sources it does not
54
+ * take part in the controller's subscribe/unsubscribe handoff: `subscribe` is a
55
+ * no-op, balance updates are routed by resolving the address against the
56
+ * wallet, and updates are pushed through the injected `onAssetsUpdate` callback.
57
+ */
58
+ export declare class AccountActivityDataSource extends AbstractDataSource<typeof CONTROLLER_NAME, AccountActivityDataSourceState> {
59
+ #private;
60
+ constructor(options: AccountActivityDataSourceOptions);
61
+ /**
62
+ * AADS is event-driven and chain-agnostic: it never participates in the
63
+ * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`
64
+ * events are routed by resolving the address against the wallet's accounts
65
+ * (see `#onBalanceUpdated`), and updates are pushed through the injected
66
+ * `#onAssetsUpdate` callback. This override exists only to satisfy the
67
+ * abstract contract and is intentionally a no-op.
68
+ */
69
+ subscribe(): Promise<void>;
70
+ destroy(): void;
71
+ }
72
+ /**
73
+ * Creates an AccountActivityDataSource instance.
74
+ *
75
+ * @param options - Configuration options for the data source.
76
+ * @returns A new AccountActivityDataSource instance.
77
+ */
78
+ export declare function createAccountActivityDataSource(options: AccountActivityDataSourceOptions): AccountActivityDataSource;
79
+ export {};
80
+ //# sourceMappingURL=AccountActivityDataSource.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountActivityDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yCAAyC,EACzC,wCAAwC,EAEzC,+BAA+B;AAKhC,OAAO,KAAK,EAAE,yBAAyB,EAAE,gCAA+B;AAExE,OAAO,KAAK,EAGV,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACb,qBAAoB;AACrB,OAAO,EAAE,kBAAkB,EAAE,iCAAgC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,iCAAgC;AAM/D,QAAA,MAAM,eAAe,8BAA8B,CAAC;AAiFpD,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAC9C,yCAAyC,GACzC,wCAAwC,CAAC;AAM7C,MAAM,MAAM,8BAA8B,GAAG,eAAe,CAAC;AAU7D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,mEAAmE;IACnE,SAAS,EAAE,yBAAyB,CAAC;IACrC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,wFAAwF;IACxF,YAAY,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IACrE;;;;;OAKG;IACH,cAAc,EAAE,CACd,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,WAAW,KAClB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CACjD,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,yBAA0B,SAAQ,kBAAkB,CAC/D,OAAO,eAAe,EACtB,8BAA8B,CAC/B;;gBAwBa,OAAO,EAAE,gCAAgC;IAkCrD;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA4JhC,OAAO,IAAI,IAAI;CAqBhB;AAMD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,yBAAyB,CAE3B"}