@metamask/assets-controller 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/LICENSE +20 -0
- package/README.md +15 -0
- package/dist/AssetsController.cjs +1057 -0
- package/dist/AssetsController.cjs.map +1 -0
- package/dist/AssetsController.d.cts +260 -0
- package/dist/AssetsController.d.cts.map +1 -0
- package/dist/AssetsController.d.mts +260 -0
- package/dist/AssetsController.d.mts.map +1 -0
- package/dist/AssetsController.mjs +1050 -0
- package/dist/AssetsController.mjs.map +1 -0
- package/dist/data-sources/AbstractDataSource.cjs +103 -0
- package/dist/data-sources/AbstractDataSource.cjs.map +1 -0
- package/dist/data-sources/AbstractDataSource.d.cts +92 -0
- package/dist/data-sources/AbstractDataSource.d.cts.map +1 -0
- package/dist/data-sources/AbstractDataSource.d.mts +92 -0
- package/dist/data-sources/AbstractDataSource.d.mts.map +1 -0
- package/dist/data-sources/AbstractDataSource.mjs +99 -0
- package/dist/data-sources/AbstractDataSource.mjs.map +1 -0
- package/dist/data-sources/AccountsApiDataSource.cjs +421 -0
- package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -0
- package/dist/data-sources/AccountsApiDataSource.d.cts +103 -0
- package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -0
- package/dist/data-sources/AccountsApiDataSource.d.mts +103 -0
- package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -0
- package/dist/data-sources/AccountsApiDataSource.mjs +416 -0
- package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -0
- package/dist/data-sources/BackendWebsocketDataSource.cjs +388 -0
- package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -0
- package/dist/data-sources/BackendWebsocketDataSource.d.cts +95 -0
- package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -0
- package/dist/data-sources/BackendWebsocketDataSource.d.mts +95 -0
- package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -0
- package/dist/data-sources/BackendWebsocketDataSource.mjs +383 -0
- package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -0
- package/dist/data-sources/PriceDataSource.cjs +343 -0
- package/dist/data-sources/PriceDataSource.cjs.map +1 -0
- package/dist/data-sources/PriceDataSource.d.cts +153 -0
- package/dist/data-sources/PriceDataSource.d.cts.map +1 -0
- package/dist/data-sources/PriceDataSource.d.mts +153 -0
- package/dist/data-sources/PriceDataSource.d.mts.map +1 -0
- package/dist/data-sources/PriceDataSource.mjs +339 -0
- package/dist/data-sources/PriceDataSource.mjs.map +1 -0
- package/dist/data-sources/RpcDataSource.cjs +829 -0
- package/dist/data-sources/RpcDataSource.cjs.map +1 -0
- package/dist/data-sources/RpcDataSource.d.cts +243 -0
- package/dist/data-sources/RpcDataSource.d.cts.map +1 -0
- package/dist/data-sources/RpcDataSource.d.mts +243 -0
- package/dist/data-sources/RpcDataSource.d.mts.map +1 -0
- package/dist/data-sources/RpcDataSource.mjs +820 -0
- package/dist/data-sources/RpcDataSource.mjs.map +1 -0
- package/dist/data-sources/SnapDataSource.cjs +463 -0
- package/dist/data-sources/SnapDataSource.cjs.map +1 -0
- package/dist/data-sources/SnapDataSource.d.cts +155 -0
- package/dist/data-sources/SnapDataSource.d.cts.map +1 -0
- package/dist/data-sources/SnapDataSource.d.mts +155 -0
- package/dist/data-sources/SnapDataSource.d.mts.map +1 -0
- package/dist/data-sources/SnapDataSource.mjs +456 -0
- package/dist/data-sources/SnapDataSource.mjs.map +1 -0
- package/dist/data-sources/TokenDataSource.cjs +211 -0
- package/dist/data-sources/TokenDataSource.cjs.map +1 -0
- package/dist/data-sources/TokenDataSource.d.cts +62 -0
- package/dist/data-sources/TokenDataSource.d.cts.map +1 -0
- package/dist/data-sources/TokenDataSource.d.mts +62 -0
- package/dist/data-sources/TokenDataSource.d.mts.map +1 -0
- package/dist/data-sources/TokenDataSource.mjs +207 -0
- package/dist/data-sources/TokenDataSource.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs +638 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts +47 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts +47 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs +632 -0
- package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.cjs +8 -0
- package/dist/data-sources/evm-rpc-services/clients/index.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.d.cts +3 -0
- package/dist/data-sources/evm-rpc-services/clients/index.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.d.mts +3 -0
- package/dist/data-sources/evm-rpc-services/clients/index.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/clients/index.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/index.cjs +12 -0
- package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/index.d.cts +5 -0
- package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/index.d.mts +5 -0
- package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/index.mjs +4 -0
- package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs +202 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts +75 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts +75 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs +198 -0
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +214 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +71 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +71 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +210 -0
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/index.cjs +8 -0
- package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/index.d.cts +3 -0
- package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/index.d.mts +3 -0
- package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/services/index.mjs +3 -0
- package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/assets.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/assets.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/assets.d.cts +53 -0
- package/dist/data-sources/evm-rpc-services/types/assets.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/assets.d.mts +53 -0
- package/dist/data-sources/evm-rpc-services/types/assets.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/assets.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/assets.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/config.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/config.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/config.d.cts +18 -0
- package/dist/data-sources/evm-rpc-services/types/config.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/config.d.mts +18 -0
- package/dist/data-sources/evm-rpc-services/types/config.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/config.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/config.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/core.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/core.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/core.d.cts +19 -0
- package/dist/data-sources/evm-rpc-services/types/core.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/core.d.mts +19 -0
- package/dist/data-sources/evm-rpc-services/types/core.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/core.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/core.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/index.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/index.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/index.d.cts +7 -0
- package/dist/data-sources/evm-rpc-services/types/index.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/index.d.mts +7 -0
- package/dist/data-sources/evm-rpc-services/types/index.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/index.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/index.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.d.cts +24 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.d.mts +24 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/multicall.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/services.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/services.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/services.d.cts +74 -0
- package/dist/data-sources/evm-rpc-services/types/services.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/services.d.mts +74 -0
- package/dist/data-sources/evm-rpc-services/types/services.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/services.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/services.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/state.cjs +3 -0
- package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/state.d.cts +52 -0
- package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/state.d.mts +52 -0
- package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/types/state.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.cjs +45 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.d.cts +28 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.d.mts +28 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.mjs +40 -0
- package/dist/data-sources/evm-rpc-services/utils/batch.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/index.cjs +7 -0
- package/dist/data-sources/evm-rpc-services/utils/index.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/index.d.cts +2 -0
- package/dist/data-sources/evm-rpc-services/utils/index.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/index.d.mts +2 -0
- package/dist/data-sources/evm-rpc-services/utils/index.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/utils/index.mjs +2 -0
- package/dist/data-sources/evm-rpc-services/utils/index.mjs.map +1 -0
- package/dist/data-sources/index.cjs +33 -0
- package/dist/data-sources/index.cjs.map +1 -0
- package/dist/data-sources/index.d.cts +9 -0
- package/dist/data-sources/index.d.cts.map +1 -0
- package/dist/data-sources/index.d.mts +9 -0
- package/dist/data-sources/index.d.mts.map +1 -0
- package/dist/data-sources/index.mjs +15 -0
- package/dist/data-sources/index.mjs.map +1 -0
- package/dist/data-sources/initDataSources.cjs +215 -0
- package/dist/data-sources/initDataSources.cjs.map +1 -0
- package/dist/data-sources/initDataSources.d.cts +140 -0
- package/dist/data-sources/initDataSources.d.cts.map +1 -0
- package/dist/data-sources/initDataSources.d.mts +140 -0
- package/dist/data-sources/initDataSources.d.mts.map +1 -0
- package/dist/data-sources/initDataSources.mjs +210 -0
- package/dist/data-sources/initDataSources.mjs.map +1 -0
- package/dist/index.cjs +47 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +21 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +21 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +25 -0
- package/dist/index.mjs.map +1 -0
- package/dist/logger.cjs +7 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.cts +5 -0
- package/dist/logger.d.cts.map +1 -0
- package/dist/logger.d.mts +5 -0
- package/dist/logger.d.mts.map +1 -0
- package/dist/logger.mjs +4 -0
- package/dist/logger.mjs.map +1 -0
- package/dist/middlewares/DetectionMiddleware.cjs +101 -0
- package/dist/middlewares/DetectionMiddleware.cjs.map +1 -0
- package/dist/middlewares/DetectionMiddleware.d.cts +54 -0
- package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -0
- package/dist/middlewares/DetectionMiddleware.d.mts +54 -0
- package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -0
- package/dist/middlewares/DetectionMiddleware.mjs +97 -0
- package/dist/middlewares/DetectionMiddleware.mjs.map +1 -0
- package/dist/middlewares/index.cjs +6 -0
- package/dist/middlewares/index.cjs.map +1 -0
- package/dist/middlewares/index.d.cts +2 -0
- package/dist/middlewares/index.d.cts.map +1 -0
- package/dist/middlewares/index.d.mts +2 -0
- package/dist/middlewares/index.d.mts.map +1 -0
- package/dist/middlewares/index.mjs +2 -0
- package/dist/middlewares/index.mjs.map +1 -0
- package/dist/types.cjs +39 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +433 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +433 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +35 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils.cjs +28 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +13 -0
- package/dist/utils.d.cts.map +1 -0
- package/dist/utils.d.mts +13 -0
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +24 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +93 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
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");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
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");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _SnapDataSource_instances, _SnapDataSource_messenger, _SnapDataSource_handleSnapBalancesUpdatedBound, _SnapDataSource_handlePermissionStateChangeBound, _SnapDataSource_keyringClientCache, _SnapDataSource_subscribeToEvents, _SnapDataSource_handleSnapBalancesUpdated, _SnapDataSource_isChainSupportedBySnap, _SnapDataSource_registerActionHandlers, _SnapDataSource_getRunnableSnaps, _SnapDataSource_getSnapPermissions, _SnapDataSource_discoverKeyringSnaps, _SnapDataSource_getKeyringClient;
|
|
13
|
+
import { KeyringClient } from "@metamask/keyring-snap-client";
|
|
14
|
+
import { HandlerType, SnapCaveatType } from "@metamask/snaps-utils";
|
|
15
|
+
import { AbstractDataSource } from "./AbstractDataSource.mjs";
|
|
16
|
+
import { projectLogger, createModuleLogger } from "../logger.mjs";
|
|
17
|
+
const log = createModuleLogger(projectLogger, 'SnapDataSource');
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// CONSTANTS
|
|
20
|
+
// ============================================================================
|
|
21
|
+
export const SNAP_DATA_SOURCE_NAME = 'SnapDataSource';
|
|
22
|
+
/** The permission name for snap keyring endowment */
|
|
23
|
+
export const KEYRING_PERMISSION = 'endowment:keyring';
|
|
24
|
+
/** The permission name for snap assets endowment (contains chainIds) */
|
|
25
|
+
export const ASSETS_PERMISSION = 'endowment:assets';
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// PERMISSION UTILITIES
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Getter function to get the chainIds caveat from a permission.
|
|
31
|
+
*
|
|
32
|
+
* This does basic validation of the caveat, but does not validate the type or
|
|
33
|
+
* value of the namespaces object itself, as this is handled by the
|
|
34
|
+
* `PermissionsController` when the permission is requested.
|
|
35
|
+
*
|
|
36
|
+
* @param permission - The permission to get the `chainIds` caveat from.
|
|
37
|
+
* @returns An array of `chainIds` that the snap supports, or null if none.
|
|
38
|
+
*/
|
|
39
|
+
export function getChainIdsCaveat(permission) {
|
|
40
|
+
if (!permission?.caveats) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const caveat = permission.caveats.find((permCaveat) => permCaveat.type === SnapCaveatType.ChainIds);
|
|
44
|
+
return caveat ? caveat.value : null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract chain ID from a CAIP-19 asset ID.
|
|
48
|
+
* e.g., "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501" -> "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
|
|
49
|
+
*
|
|
50
|
+
* @param assetId - The CAIP-19 asset ID to extract chain from.
|
|
51
|
+
* @returns The CAIP-2 chain ID portion of the asset ID.
|
|
52
|
+
*/
|
|
53
|
+
export function extractChainFromAssetId(assetId) {
|
|
54
|
+
const parts = assetId.split('/');
|
|
55
|
+
return parts[0];
|
|
56
|
+
}
|
|
57
|
+
const defaultSnapState = {
|
|
58
|
+
activeChains: [],
|
|
59
|
+
chainToSnap: {},
|
|
60
|
+
};
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// SNAP DATA SOURCE
|
|
63
|
+
// ============================================================================
|
|
64
|
+
/**
|
|
65
|
+
* Unified Snap data source that routes requests to the appropriate wallet snap
|
|
66
|
+
* based on the chain ID prefix.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const snapDataSource = new SnapDataSource({
|
|
71
|
+
* messenger,
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* // Fetch will automatically route to the correct snap
|
|
75
|
+
* await snapDataSource.fetch({
|
|
76
|
+
* chainIds: ['solana:mainnet', 'bip122:000000000019d6689c085ae165831e93'],
|
|
77
|
+
* accountIds: ['account1'],
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export class SnapDataSource extends AbstractDataSource {
|
|
82
|
+
constructor(options) {
|
|
83
|
+
super(SNAP_DATA_SOURCE_NAME, {
|
|
84
|
+
...defaultSnapState,
|
|
85
|
+
...options.state,
|
|
86
|
+
});
|
|
87
|
+
_SnapDataSource_instances.add(this);
|
|
88
|
+
_SnapDataSource_messenger.set(this, void 0);
|
|
89
|
+
/** Bound handler for snap keyring balance updates, stored for cleanup */
|
|
90
|
+
_SnapDataSource_handleSnapBalancesUpdatedBound.set(this, void 0);
|
|
91
|
+
_SnapDataSource_handlePermissionStateChangeBound.set(this, void 0);
|
|
92
|
+
/** Cache of KeyringClient instances per snap ID to avoid re-instantiation */
|
|
93
|
+
_SnapDataSource_keyringClientCache.set(this, new Map());
|
|
94
|
+
__classPrivateFieldSet(this, _SnapDataSource_messenger, options.messenger, "f");
|
|
95
|
+
// Bind handlers for cleanup in destroy()
|
|
96
|
+
__classPrivateFieldSet(this, _SnapDataSource_handleSnapBalancesUpdatedBound, __classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_handleSnapBalancesUpdated).bind(this), "f");
|
|
97
|
+
__classPrivateFieldSet(this, _SnapDataSource_handlePermissionStateChangeBound, __classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_discoverKeyringSnaps).bind(this), "f");
|
|
98
|
+
__classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_registerActionHandlers).call(this);
|
|
99
|
+
__classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_subscribeToEvents).call(this);
|
|
100
|
+
// Discover keyring-capable snaps and populate activeChains dynamically
|
|
101
|
+
__classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_discoverKeyringSnaps).call(this);
|
|
102
|
+
}
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// FETCH
|
|
105
|
+
// ============================================================================
|
|
106
|
+
async fetch(request) {
|
|
107
|
+
var _a;
|
|
108
|
+
// Guard against undefined request
|
|
109
|
+
// Note: chainIds filtering is done by middleware/subscribe before calling fetch
|
|
110
|
+
if (!request?.accounts || !request?.chainIds?.length) {
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
const results = {
|
|
114
|
+
assetsBalance: {},
|
|
115
|
+
assetsMetadata: {},
|
|
116
|
+
};
|
|
117
|
+
// Fetch balances for each account using its snap ID from metadata
|
|
118
|
+
for (const account of request.accounts) {
|
|
119
|
+
// Skip accounts without snap metadata (non-snap accounts)
|
|
120
|
+
const snapId = account.metadata.snap?.id;
|
|
121
|
+
if (!snapId) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Skip accounts whose snap doesn't support any of the requested chains
|
|
125
|
+
const snapSupportsRequestedChains = request.chainIds.some((chainId) => this.state.chainToSnap[chainId] === snapId);
|
|
126
|
+
if (!snapSupportsRequestedChains) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const accountId = account.id;
|
|
130
|
+
try {
|
|
131
|
+
const client = __classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_getKeyringClient).call(this, snapId);
|
|
132
|
+
// Step 1: Get the list of assets for this account
|
|
133
|
+
const accountAssets = await client.listAccountAssets(accountId);
|
|
134
|
+
// If no assets, skip to next account
|
|
135
|
+
if (!accountAssets || accountAssets.length === 0) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// Step 2: Get balances for those specific assets
|
|
139
|
+
const balances = await client.getAccountBalances(accountId, accountAssets);
|
|
140
|
+
// Transform keyring response to DataResponse format
|
|
141
|
+
if (balances && typeof balances === 'object' && results.assetsBalance) {
|
|
142
|
+
for (const [assetId, balance] of Object.entries(balances)) {
|
|
143
|
+
(_a = results.assetsBalance)[accountId] ?? (_a[accountId] = {});
|
|
144
|
+
const accountBalances = results.assetsBalance[accountId];
|
|
145
|
+
if (accountBalances) {
|
|
146
|
+
accountBalances[assetId] = {
|
|
147
|
+
amount: balance.amount,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Expected when account doesn't belong to this snap
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// MIDDLEWARE
|
|
161
|
+
// ============================================================================
|
|
162
|
+
/**
|
|
163
|
+
* Get the middleware for fetching balances via Snaps.
|
|
164
|
+
* This middleware:
|
|
165
|
+
* - Supports multiple accounts in a single request
|
|
166
|
+
* - Filters request to only chains this data source supports
|
|
167
|
+
* - Fetches balances for those chains for all accounts
|
|
168
|
+
* - Merges response into context
|
|
169
|
+
* - Removes handled chains from request for next middleware
|
|
170
|
+
*
|
|
171
|
+
* @returns The middleware function for the assets pipeline.
|
|
172
|
+
*/
|
|
173
|
+
get assetsMiddleware() {
|
|
174
|
+
return async (context, next) => {
|
|
175
|
+
var _a;
|
|
176
|
+
const { request } = context;
|
|
177
|
+
// Filter to chains this data source supports
|
|
178
|
+
const supportedChains = request.chainIds.filter((chainId) => this.state.activeChains.includes(chainId));
|
|
179
|
+
// If no supported chains, skip and pass to next middleware
|
|
180
|
+
if (supportedChains.length === 0) {
|
|
181
|
+
return next(context);
|
|
182
|
+
}
|
|
183
|
+
let successfullyHandledChains = [];
|
|
184
|
+
try {
|
|
185
|
+
// Fetch for supported chains
|
|
186
|
+
const response = await this.fetch({
|
|
187
|
+
...request,
|
|
188
|
+
chainIds: supportedChains,
|
|
189
|
+
});
|
|
190
|
+
// Merge response into context
|
|
191
|
+
if (response.assetsBalance) {
|
|
192
|
+
(_a = context.response).assetsBalance ?? (_a.assetsBalance = {});
|
|
193
|
+
for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
|
|
194
|
+
context.response.assetsBalance[accountId] = {
|
|
195
|
+
...context.response.assetsBalance[accountId],
|
|
196
|
+
...accountBalances,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (response.assetsMetadata) {
|
|
201
|
+
context.response.assetsMetadata = {
|
|
202
|
+
...context.response.assetsMetadata,
|
|
203
|
+
...response.assetsMetadata,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (response.assetsPrice) {
|
|
207
|
+
context.response.assetsPrice = {
|
|
208
|
+
...context.response.assetsPrice,
|
|
209
|
+
...response.assetsPrice,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// Determine successfully handled chains (exclude errors)
|
|
213
|
+
const failedChains = new Set(Object.keys(response.errors ?? {}));
|
|
214
|
+
successfullyHandledChains = supportedChains.filter((chainId) => !failedChains.has(chainId));
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
log('Middleware fetch failed', { error });
|
|
218
|
+
successfullyHandledChains = [];
|
|
219
|
+
}
|
|
220
|
+
// Prepare context for next middleware
|
|
221
|
+
let nextContext = context;
|
|
222
|
+
if (successfullyHandledChains.length > 0) {
|
|
223
|
+
const remainingChains = request.chainIds.filter((chainId) => !successfullyHandledChains.includes(chainId));
|
|
224
|
+
nextContext = {
|
|
225
|
+
...context,
|
|
226
|
+
request: {
|
|
227
|
+
...request,
|
|
228
|
+
chainIds: remainingChains,
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Call next middleware
|
|
233
|
+
return next(nextContext);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// SUBSCRIBE - Routes to appropriate snap(s)
|
|
238
|
+
// ============================================================================
|
|
239
|
+
async subscribe(subscriptionRequest) {
|
|
240
|
+
const { request, subscriptionId, isUpdate } = subscriptionRequest;
|
|
241
|
+
// Guard against undefined request or chainIds
|
|
242
|
+
if (!request?.chainIds) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// Filter to chains we have a snap for
|
|
246
|
+
const supportedChains = request.chainIds.filter((chainId) => __classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_isChainSupportedBySnap).call(this, chainId));
|
|
247
|
+
if (supportedChains.length === 0) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (isUpdate) {
|
|
251
|
+
const existing = this.activeSubscriptions.get(subscriptionId);
|
|
252
|
+
if (existing) {
|
|
253
|
+
existing.chains = supportedChains;
|
|
254
|
+
// Do a fetch to get latest data on subscription update
|
|
255
|
+
this.fetch({
|
|
256
|
+
...request,
|
|
257
|
+
chainIds: supportedChains,
|
|
258
|
+
})
|
|
259
|
+
.then(async (fetchResponse) => {
|
|
260
|
+
if (Object.keys(fetchResponse.assetsBalance ?? {}).length > 0) {
|
|
261
|
+
await __classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('AssetsController:assetsUpdate', fetchResponse, SNAP_DATA_SOURCE_NAME);
|
|
262
|
+
}
|
|
263
|
+
return fetchResponse;
|
|
264
|
+
})
|
|
265
|
+
.catch((error) => {
|
|
266
|
+
log('Subscription update fetch failed', { subscriptionId, error });
|
|
267
|
+
});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
await this.unsubscribe(subscriptionId);
|
|
272
|
+
// Snaps provide real-time updates via AccountsController:accountBalancesUpdated
|
|
273
|
+
// We only need to track the subscription and do an initial fetch
|
|
274
|
+
// No polling needed - updates come through #handleSnapBalancesUpdated
|
|
275
|
+
this.activeSubscriptions.set(subscriptionId, {
|
|
276
|
+
cleanup: () => {
|
|
277
|
+
// No timer to clear - we use event-based updates
|
|
278
|
+
},
|
|
279
|
+
chains: supportedChains,
|
|
280
|
+
});
|
|
281
|
+
// Initial fetch to get current balances
|
|
282
|
+
try {
|
|
283
|
+
const fetchResponse = await this.fetch({
|
|
284
|
+
...request,
|
|
285
|
+
chainIds: supportedChains,
|
|
286
|
+
});
|
|
287
|
+
if (Object.keys(fetchResponse.assetsBalance ?? {}).length > 0) {
|
|
288
|
+
await __classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('AssetsController:assetsUpdate', fetchResponse, SNAP_DATA_SOURCE_NAME);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
log('Initial fetch failed', { subscriptionId, error });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// CLEANUP
|
|
297
|
+
// ============================================================================
|
|
298
|
+
destroy() {
|
|
299
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
300
|
+
const messenger = __classPrivateFieldGet(this, _SnapDataSource_messenger, "f");
|
|
301
|
+
// Unsubscribe from snap keyring events
|
|
302
|
+
try {
|
|
303
|
+
messenger.unsubscribe('AccountsController:accountBalancesUpdated', __classPrivateFieldGet(this, _SnapDataSource_handleSnapBalancesUpdatedBound, "f"));
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
log('Failed to unsubscribe from snap keyring events', { error });
|
|
307
|
+
}
|
|
308
|
+
// Unsubscribe from permission changes
|
|
309
|
+
try {
|
|
310
|
+
messenger.unsubscribe('PermissionController:stateChange', __classPrivateFieldGet(this, _SnapDataSource_handlePermissionStateChangeBound, "f"));
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
log('Failed to unsubscribe from permission changes', { error });
|
|
314
|
+
}
|
|
315
|
+
// Clean up active subscriptions
|
|
316
|
+
for (const [subscriptionId] of this.activeSubscriptions) {
|
|
317
|
+
this.unsubscribe(subscriptionId).catch(() => {
|
|
318
|
+
// Ignore cleanup errors
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
// Clear keyring client cache
|
|
322
|
+
__classPrivateFieldGet(this, _SnapDataSource_keyringClientCache, "f").clear();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
_SnapDataSource_messenger = new WeakMap(), _SnapDataSource_handleSnapBalancesUpdatedBound = new WeakMap(), _SnapDataSource_handlePermissionStateChangeBound = new WeakMap(), _SnapDataSource_keyringClientCache = new WeakMap(), _SnapDataSource_instances = new WeakSet(), _SnapDataSource_subscribeToEvents = function _SnapDataSource_subscribeToEvents() {
|
|
326
|
+
// Subscribe to snap keyring events for real-time balance updates.
|
|
327
|
+
// The snaps emit AccountBalancesUpdated events when balances change,
|
|
328
|
+
// which are re-published by AccountsController.
|
|
329
|
+
__classPrivateFieldGet(this, _SnapDataSource_messenger, "f").subscribe('AccountsController:accountBalancesUpdated', __classPrivateFieldGet(this, _SnapDataSource_handleSnapBalancesUpdatedBound, "f"));
|
|
330
|
+
// Subscribe to permission changes to detect new keyring snaps at runtime.
|
|
331
|
+
// Re-runs snap discovery when permissions change.
|
|
332
|
+
__classPrivateFieldGet(this, _SnapDataSource_messenger, "f").subscribe('PermissionController:stateChange', __classPrivateFieldGet(this, _SnapDataSource_handlePermissionStateChangeBound, "f"));
|
|
333
|
+
}, _SnapDataSource_handleSnapBalancesUpdated = function _SnapDataSource_handleSnapBalancesUpdated(payload) {
|
|
334
|
+
// Transform the snap keyring payload to DataResponse format
|
|
335
|
+
let assetsBalance;
|
|
336
|
+
for (const [accountId, assets] of Object.entries(payload.balances)) {
|
|
337
|
+
let accountAssets;
|
|
338
|
+
for (const [assetId, balance] of Object.entries(assets)) {
|
|
339
|
+
const chainId = extractChainFromAssetId(assetId);
|
|
340
|
+
if (__classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_isChainSupportedBySnap).call(this, chainId)) {
|
|
341
|
+
accountAssets ?? (accountAssets = {});
|
|
342
|
+
accountAssets[assetId] = {
|
|
343
|
+
amount: balance.amount,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (accountAssets) {
|
|
348
|
+
assetsBalance ?? (assetsBalance = {});
|
|
349
|
+
assetsBalance[accountId] = accountAssets;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// Only report if we have snap-related updates
|
|
353
|
+
if (assetsBalance) {
|
|
354
|
+
const response = { assetsBalance };
|
|
355
|
+
__classPrivateFieldGet(this, _SnapDataSource_messenger, "f")
|
|
356
|
+
.call('AssetsController:assetsUpdate', response, SNAP_DATA_SOURCE_NAME)
|
|
357
|
+
.catch(console.error);
|
|
358
|
+
}
|
|
359
|
+
}, _SnapDataSource_isChainSupportedBySnap = function _SnapDataSource_isChainSupportedBySnap(chainId) {
|
|
360
|
+
return this.state.activeChains.includes(chainId);
|
|
361
|
+
}, _SnapDataSource_registerActionHandlers = function _SnapDataSource_registerActionHandlers() {
|
|
362
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
363
|
+
const messenger = __classPrivateFieldGet(this, _SnapDataSource_messenger, "f");
|
|
364
|
+
messenger.registerActionHandler('SnapDataSource:getAssetsMiddleware', () => this.assetsMiddleware);
|
|
365
|
+
messenger.registerActionHandler('SnapDataSource:getActiveChains', async () => this.getActiveChains());
|
|
366
|
+
messenger.registerActionHandler('SnapDataSource:fetch', async (request) => this.fetch(request));
|
|
367
|
+
messenger.registerActionHandler('SnapDataSource:subscribe', async (request) => this.subscribe(request));
|
|
368
|
+
messenger.registerActionHandler('SnapDataSource:unsubscribe', async (subscriptionId) => this.unsubscribe(subscriptionId));
|
|
369
|
+
}, _SnapDataSource_getRunnableSnaps = function _SnapDataSource_getRunnableSnaps() {
|
|
370
|
+
try {
|
|
371
|
+
return __classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('SnapController:getRunnableSnaps');
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
log('Failed to get runnable snaps', error);
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
}, _SnapDataSource_getSnapPermissions = function _SnapDataSource_getSnapPermissions(snapId) {
|
|
378
|
+
try {
|
|
379
|
+
return __classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('PermissionController:getPermissions', snapId);
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
log('Failed to get permissions for snap', { snapId, error });
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
}, _SnapDataSource_discoverKeyringSnaps = function _SnapDataSource_discoverKeyringSnaps() {
|
|
386
|
+
try {
|
|
387
|
+
const runnableSnaps = __classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_getRunnableSnaps).call(this);
|
|
388
|
+
const chainToSnap = {};
|
|
389
|
+
const supportedChains = [];
|
|
390
|
+
for (const snap of runnableSnaps) {
|
|
391
|
+
const permissions = __classPrivateFieldGet(this, _SnapDataSource_instances, "m", _SnapDataSource_getSnapPermissions).call(this, snap.id);
|
|
392
|
+
// Must have endowment:keyring permission to be a keyring snap
|
|
393
|
+
if (!permissions?.[KEYRING_PERMISSION]) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
// Get chainIds caveat from the assets permission (not keyring permission)
|
|
397
|
+
// The chainIds are stored in endowment:assets
|
|
398
|
+
const assetsPermission = permissions[ASSETS_PERMISSION];
|
|
399
|
+
const chainIds = getChainIdsCaveat(assetsPermission);
|
|
400
|
+
// Map each chain to this snap (first snap wins if multiple support same chain)
|
|
401
|
+
if (chainIds) {
|
|
402
|
+
for (const chainId of chainIds) {
|
|
403
|
+
if (!(chainId in chainToSnap)) {
|
|
404
|
+
chainToSnap[chainId] = snap.id;
|
|
405
|
+
supportedChains.push(chainId);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// Update chainToSnap mapping
|
|
411
|
+
this.state.chainToSnap = chainToSnap;
|
|
412
|
+
// Notify if chains changed
|
|
413
|
+
try {
|
|
414
|
+
this.updateActiveChains(supportedChains, (updatedChains) => {
|
|
415
|
+
__classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('AssetsController:activeChainsUpdate', SNAP_DATA_SOURCE_NAME, updatedChains);
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
// AssetsController not ready yet - expected during initialization
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
log('Keyring snap discovery failed', { error });
|
|
424
|
+
this.state.chainToSnap = {};
|
|
425
|
+
try {
|
|
426
|
+
this.updateActiveChains([], (updatedChains) => {
|
|
427
|
+
__classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('AssetsController:activeChainsUpdate', SNAP_DATA_SOURCE_NAME, updatedChains);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
// AssetsController not ready yet - expected during initialization
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}, _SnapDataSource_getKeyringClient = function _SnapDataSource_getKeyringClient(snapId) {
|
|
435
|
+
const cachedClient = __classPrivateFieldGet(this, _SnapDataSource_keyringClientCache, "f").get(snapId);
|
|
436
|
+
if (cachedClient) {
|
|
437
|
+
return cachedClient;
|
|
438
|
+
}
|
|
439
|
+
const client = new KeyringClient({
|
|
440
|
+
send: async (request) => (await __classPrivateFieldGet(this, _SnapDataSource_messenger, "f").call('SnapController:handleRequest', {
|
|
441
|
+
snapId: snapId,
|
|
442
|
+
origin: 'metamask',
|
|
443
|
+
handler: HandlerType.OnKeyringRequest,
|
|
444
|
+
request,
|
|
445
|
+
})),
|
|
446
|
+
});
|
|
447
|
+
__classPrivateFieldGet(this, _SnapDataSource_keyringClientCache, "f").set(snapId, client);
|
|
448
|
+
return client;
|
|
449
|
+
};
|
|
450
|
+
// ============================================================================
|
|
451
|
+
// FACTORY
|
|
452
|
+
// ============================================================================
|
|
453
|
+
export function createSnapDataSource(options) {
|
|
454
|
+
return new SnapDataSource(options);
|
|
455
|
+
}
|
|
456
|
+
//# sourceMappingURL=SnapDataSource.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SnapDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/SnapDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Balance, CaipAssetType } from '@metamask/keyring-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n Caveat,\n GetPermissions,\n PermissionConstraint,\n PermissionControllerStateChange,\n SubjectPermissions,\n} from '@metamask/permission-controller';\nimport type {\n GetRunnableSnaps,\n HandleSnapRequest,\n} from '@metamask/snaps-controllers';\nimport type { Snap, SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType, SnapCaveatType } from '@metamask/snaps-utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\n\nimport { AbstractDataSource } from './AbstractDataSource';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n AssetBalance,\n ChainId,\n Caip19AssetId,\n DataRequest,\n DataResponse,\n Middleware,\n} from '../types';\n\n// ============================================================================\n// SNAP KEYRING EVENT TYPES\n// ============================================================================\n\n/**\n * Payload for AccountsController:accountBalancesUpdated event.\n * Re-published from SnapKeyring:accountBalancesUpdated.\n */\nexport type AccountBalancesUpdatedEventPayload = {\n balances: {\n [accountId: string]: {\n [assetId: string]: {\n amount: string;\n unit: string;\n };\n };\n };\n};\n\n/**\n * Event from AccountsController when snap balances are updated.\n */\nexport type AccountsControllerAccountBalancesUpdatedEvent = {\n type: 'AccountsController:accountBalancesUpdated';\n payload: [AccountBalancesUpdatedEventPayload];\n};\n\nconst log = createModuleLogger(projectLogger, 'SnapDataSource');\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const SNAP_DATA_SOURCE_NAME = 'SnapDataSource';\n\n/** The permission name for snap keyring endowment */\nexport const KEYRING_PERMISSION = 'endowment:keyring';\n\n/** The permission name for snap assets endowment (contains chainIds) */\nexport const ASSETS_PERMISSION = 'endowment:assets';\n\n// ============================================================================\n// PERMISSION UTILITIES\n// ============================================================================\n\n/**\n * Getter function to get the chainIds caveat from a permission.\n *\n * This does basic validation of the caveat, but does not validate the type or\n * value of the namespaces object itself, as this is handled by the\n * `PermissionsController` when the permission is requested.\n *\n * @param permission - The permission to get the `chainIds` caveat from.\n * @returns An array of `chainIds` that the snap supports, or null if none.\n */\nexport function getChainIdsCaveat(\n permission?: PermissionConstraint,\n): ChainId[] | null {\n if (!permission?.caveats) {\n return null;\n }\n\n const caveat = permission.caveats.find(\n (permCaveat) => permCaveat.type === SnapCaveatType.ChainIds,\n ) as Caveat<string, string[]> | undefined;\n\n return caveat ? (caveat.value as ChainId[]) : null;\n}\n\n/**\n * Extract chain ID from a CAIP-19 asset ID.\n * e.g., \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501\" -> \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\"\n *\n * @param assetId - The CAIP-19 asset ID to extract chain from.\n * @returns The CAIP-2 chain ID portion of the asset ID.\n */\nexport function extractChainFromAssetId(assetId: string): ChainId {\n const parts = assetId.split('/');\n return parts[0] as ChainId;\n}\n\n// ============================================================================\n// STATE\n// ============================================================================\n\n/**\n * State for the SnapDataSource.\n * Uses dynamic snap discovery - chains are populated from PermissionController.\n */\nexport type SnapDataSourceState = {\n /**\n * Mapping of chain IDs to snap IDs that support them.\n * Used to filter which accounts to process for a given chain request.\n */\n chainToSnap: Record<ChainId, string>;\n} & DataSourceState;\n\nconst defaultSnapState: SnapDataSourceState = {\n activeChains: [],\n chainToSnap: {},\n};\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\nexport type SnapDataSourceGetAssetsMiddlewareAction = {\n type: 'SnapDataSource:getAssetsMiddleware';\n handler: () => Middleware;\n};\n\nexport type SnapDataSourceGetActiveChainsAction = {\n type: 'SnapDataSource:getActiveChains';\n handler: () => Promise<ChainId[]>;\n};\n\nexport type SnapDataSourceFetchAction = {\n type: 'SnapDataSource:fetch';\n handler: (request: DataRequest) => Promise<DataResponse>;\n};\n\nexport type SnapDataSourceSubscribeAction = {\n type: 'SnapDataSource:subscribe';\n handler: (request: SubscriptionRequest) => Promise<void>;\n};\n\nexport type SnapDataSourceUnsubscribeAction = {\n type: 'SnapDataSource:unsubscribe';\n handler: (subscriptionId: string) => Promise<void>;\n};\n\nexport type SnapDataSourceActions =\n | SnapDataSourceGetAssetsMiddlewareAction\n | SnapDataSourceGetActiveChainsAction\n | SnapDataSourceFetchAction\n | SnapDataSourceSubscribeAction\n | SnapDataSourceUnsubscribeAction;\n\nexport type SnapDataSourceActiveChainsChangedEvent = {\n type: 'SnapDataSource:activeChainsUpdated';\n payload: [ChainId[]];\n};\n\nexport type SnapDataSourceAssetsUpdatedEvent = {\n type: 'SnapDataSource:assetsUpdated';\n payload: [DataResponse, string | undefined];\n};\n\nexport type SnapDataSourceEvents =\n | SnapDataSourceActiveChainsChangedEvent\n | SnapDataSourceAssetsUpdatedEvent;\n\n/**\n * Allowed events that SnapDataSource can subscribe to.\n */\nexport type SnapDataSourceAllowedEvents =\n | AccountsControllerAccountBalancesUpdatedEvent\n | PermissionControllerStateChange;\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\nexport type SnapDataSourceAllowedActions =\n | AssetsControllerActiveChainsUpdateAction\n | AssetsControllerAssetsUpdateAction\n | GetRunnableSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type SnapDataSourceMessenger = Messenger<\n typeof SNAP_DATA_SOURCE_NAME,\n SnapDataSourceActions | SnapDataSourceAllowedActions,\n SnapDataSourceEvents | SnapDataSourceAllowedEvents\n>;\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type SnapDataSourceOptions = {\n /** Messenger for this data source */\n messenger: SnapDataSourceMessenger;\n /** Configured networks to support (defaults to all snap networks) */\n configuredNetworks?: ChainId[];\n /** Default polling interval in ms for subscriptions */\n pollInterval?: number;\n /** Initial state */\n state?: Partial<SnapDataSourceState>;\n};\n\n// ============================================================================\n// SNAP DATA SOURCE\n// ============================================================================\n\n/**\n * Unified Snap data source that routes requests to the appropriate wallet snap\n * based on the chain ID prefix.\n *\n * @example\n * ```typescript\n * const snapDataSource = new SnapDataSource({\n * messenger,\n * });\n *\n * // Fetch will automatically route to the correct snap\n * await snapDataSource.fetch({\n * chainIds: ['solana:mainnet', 'bip122:000000000019d6689c085ae165831e93'],\n * accountIds: ['account1'],\n * });\n * ```\n */\nexport class SnapDataSource extends AbstractDataSource<\n typeof SNAP_DATA_SOURCE_NAME,\n SnapDataSourceState\n> {\n readonly #messenger: SnapDataSourceMessenger;\n\n /** Bound handler for snap keyring balance updates, stored for cleanup */\n readonly #handleSnapBalancesUpdatedBound: (\n payload: AccountBalancesUpdatedEventPayload,\n ) => void;\n\n readonly #handlePermissionStateChangeBound: () => void;\n\n /** Cache of KeyringClient instances per snap ID to avoid re-instantiation */\n readonly #keyringClientCache: Map<string, KeyringClient> = new Map();\n\n constructor(options: SnapDataSourceOptions) {\n super(SNAP_DATA_SOURCE_NAME, {\n ...defaultSnapState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n\n // Bind handlers for cleanup in destroy()\n this.#handleSnapBalancesUpdatedBound = this.#handleSnapBalancesUpdated.bind(\n this,\n ) as (payload: AccountBalancesUpdatedEventPayload) => void;\n this.#handlePermissionStateChangeBound =\n this.#discoverKeyringSnaps.bind(this);\n\n this.#registerActionHandlers();\n this.#subscribeToEvents();\n\n // Discover keyring-capable snaps and populate activeChains dynamically\n this.#discoverKeyringSnaps();\n }\n\n /**\n * Subscribe to all events needed by SnapDataSource.\n * Groups snap keyring events and permission change events.\n */\n #subscribeToEvents(): void {\n // Subscribe to snap keyring events for real-time balance updates.\n // The snaps emit AccountBalancesUpdated events when balances change,\n // which are re-published by AccountsController.\n this.#messenger.subscribe(\n 'AccountsController:accountBalancesUpdated',\n this.#handleSnapBalancesUpdatedBound,\n );\n\n // Subscribe to permission changes to detect new keyring snaps at runtime.\n // Re-runs snap discovery when permissions change.\n this.#messenger.subscribe(\n 'PermissionController:stateChange',\n this.#handlePermissionStateChangeBound,\n );\n }\n\n /**\n * Handle snap balance updates from the keyring.\n * Transforms the payload and publishes to AssetsController.\n *\n * @param payload - The balance update payload from AccountsController.\n */\n #handleSnapBalancesUpdated(\n payload: AccountBalancesUpdatedEventPayload,\n ): void {\n // Transform the snap keyring payload to DataResponse format\n let assetsBalance: NonNullable<DataResponse['assetsBalance']> | undefined;\n\n for (const [accountId, assets] of Object.entries(payload.balances)) {\n let accountAssets: Record<Caip19AssetId, AssetBalance> | undefined;\n\n for (const [assetId, balance] of Object.entries(assets)) {\n const chainId = extractChainFromAssetId(assetId);\n if (this.#isChainSupportedBySnap(chainId)) {\n accountAssets ??= {};\n accountAssets[assetId as Caip19AssetId] = {\n amount: balance.amount,\n };\n }\n }\n\n if (accountAssets) {\n assetsBalance ??= {};\n assetsBalance[accountId] = accountAssets;\n }\n }\n\n // Only report if we have snap-related updates\n if (assetsBalance) {\n const response: DataResponse = { assetsBalance };\n this.#messenger\n .call('AssetsController:assetsUpdate', response, SNAP_DATA_SOURCE_NAME)\n .catch(console.error);\n }\n }\n\n /**\n * Check if a chain ID is supported by any discovered snap.\n *\n * @param chainId - The CAIP-2 chain ID to check.\n * @returns True if we have a snap that supports this chain.\n */\n #isChainSupportedBySnap(chainId: ChainId): boolean {\n return this.state.activeChains.includes(chainId);\n }\n\n #registerActionHandlers(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n\n messenger.registerActionHandler(\n 'SnapDataSource:getAssetsMiddleware',\n () => this.assetsMiddleware,\n );\n\n messenger.registerActionHandler(\n 'SnapDataSource:getActiveChains',\n async () => this.getActiveChains(),\n );\n\n messenger.registerActionHandler(\n 'SnapDataSource:fetch',\n async (request: DataRequest) => this.fetch(request),\n );\n\n messenger.registerActionHandler(\n 'SnapDataSource:subscribe',\n async (request: SubscriptionRequest) => this.subscribe(request),\n );\n\n messenger.registerActionHandler(\n 'SnapDataSource:unsubscribe',\n async (subscriptionId: string) => this.unsubscribe(subscriptionId),\n );\n }\n\n // ============================================================================\n // SNAP DISCOVERY (Dynamic via PermissionController)\n // ============================================================================\n\n /**\n * Get all runnable snaps from SnapController.\n * Runnable snaps are enabled and not blocked.\n *\n * @returns Array of runnable snaps.\n */\n #getRunnableSnaps(): Snap[] {\n try {\n return this.#messenger.call('SnapController:getRunnableSnaps');\n } catch (error) {\n log('Failed to get runnable snaps', error);\n return [];\n }\n }\n\n /**\n * Get permissions for a snap from PermissionController.\n *\n * @param snapId - The snap ID to get permissions for.\n * @returns The snap's permissions, or undefined if none.\n */\n #getSnapPermissions(\n snapId: string,\n ): SubjectPermissions<PermissionConstraint> | undefined {\n try {\n return this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n ) as SubjectPermissions<PermissionConstraint>;\n } catch (error) {\n log('Failed to get permissions for snap', { snapId, error });\n return undefined;\n }\n }\n\n /**\n * Discover all snaps with keyring capabilities and their supported chains.\n * Uses PermissionController to find snaps with endowment:keyring permission.\n * Updates chainToSnap mapping and activeChains.\n *\n * Called on initialization and whenever PermissionController state changes\n * (e.g., new snaps installed, permissions granted/revoked).\n *\n * @remarks\n * **Known limitation:** If discovery fails (e.g., SnapController not ready),\n * the data source continues with empty chainToSnap. This means no snap\n * chains will be supported until a re-discovery is triggered by a permission\n * change. Callers should be aware that initialization may complete with no\n * active chains.\n */\n #discoverKeyringSnaps(): void {\n try {\n const runnableSnaps = this.#getRunnableSnaps();\n const chainToSnap: Record<ChainId, string> = {};\n const supportedChains: ChainId[] = [];\n\n for (const snap of runnableSnaps) {\n const permissions = this.#getSnapPermissions(snap.id);\n // Must have endowment:keyring permission to be a keyring snap\n if (!permissions?.[KEYRING_PERMISSION]) {\n continue;\n }\n\n // Get chainIds caveat from the assets permission (not keyring permission)\n // The chainIds are stored in endowment:assets\n const assetsPermission = permissions[ASSETS_PERMISSION];\n const chainIds = getChainIdsCaveat(assetsPermission);\n\n // Map each chain to this snap (first snap wins if multiple support same chain)\n if (chainIds) {\n for (const chainId of chainIds) {\n if (!(chainId in chainToSnap)) {\n chainToSnap[chainId] = snap.id;\n supportedChains.push(chainId);\n }\n }\n }\n }\n\n // Update chainToSnap mapping\n this.state.chainToSnap = chainToSnap;\n\n // Notify if chains changed\n try {\n this.updateActiveChains(supportedChains, (updatedChains) => {\n this.#messenger.call(\n 'AssetsController:activeChainsUpdate',\n SNAP_DATA_SOURCE_NAME,\n updatedChains,\n );\n });\n } catch {\n // AssetsController not ready yet - expected during initialization\n }\n } catch (error) {\n log('Keyring snap discovery failed', { error });\n this.state.chainToSnap = {};\n try {\n this.updateActiveChains([], (updatedChains) => {\n this.#messenger.call(\n 'AssetsController:activeChainsUpdate',\n SNAP_DATA_SOURCE_NAME,\n updatedChains,\n );\n });\n } catch {\n // AssetsController not ready yet - expected during initialization\n }\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n // Guard against undefined request\n // Note: chainIds filtering is done by middleware/subscribe before calling fetch\n if (!request?.accounts || !request?.chainIds?.length) {\n return {};\n }\n\n const results: DataResponse = {\n assetsBalance: {},\n assetsMetadata: {},\n };\n\n // Fetch balances for each account using its snap ID from metadata\n for (const account of request.accounts) {\n // Skip accounts without snap metadata (non-snap accounts)\n const snapId = account.metadata.snap?.id;\n if (!snapId) {\n continue;\n }\n\n // Skip accounts whose snap doesn't support any of the requested chains\n const snapSupportsRequestedChains = request.chainIds.some(\n (chainId) => this.state.chainToSnap[chainId] === snapId,\n );\n if (!snapSupportsRequestedChains) {\n continue;\n }\n\n const accountId = account.id;\n try {\n const client = this.#getKeyringClient(snapId);\n\n // Step 1: Get the list of assets for this account\n const accountAssets = await client.listAccountAssets(accountId);\n\n // If no assets, skip to next account\n if (!accountAssets || accountAssets.length === 0) {\n continue;\n }\n\n // Step 2: Get balances for those specific assets\n const balances: Record<CaipAssetType, Balance> =\n await client.getAccountBalances(\n accountId,\n accountAssets as CaipAssetType[],\n );\n\n // Transform keyring response to DataResponse format\n if (balances && typeof balances === 'object' && results.assetsBalance) {\n for (const [assetId, balance] of Object.entries(balances)) {\n results.assetsBalance[accountId] ??= {};\n const accountBalances = results.assetsBalance[accountId];\n if (accountBalances) {\n (accountBalances as Record<string, unknown>)[assetId] = {\n amount: balance.amount,\n };\n }\n }\n }\n } catch {\n // Expected when account doesn't belong to this snap\n }\n }\n\n return results;\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Snaps.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Filters request to only chains this data source supports\n * - Fetches balances for those chains for all accounts\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // Filter to chains this data source supports\n const supportedChains = request.chainIds.filter((chainId) =>\n this.state.activeChains.includes(chainId),\n );\n\n // If no supported chains, skip and pass to next middleware\n if (supportedChains.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n // Fetch for supported chains\n const response = await this.fetch({\n ...request,\n chainIds: supportedChains,\n });\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n if (response.assetsMetadata) {\n context.response.assetsMetadata = {\n ...context.response.assetsMetadata,\n ...response.assetsMetadata,\n };\n }\n if (response.assetsPrice) {\n context.response.assetsPrice = {\n ...context.response.assetsPrice,\n ...response.assetsPrice,\n };\n }\n\n // Determine successfully handled chains (exclude errors)\n const failedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = supportedChains.filter(\n (chainId) => !failedChains.has(chainId),\n );\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Prepare context for next middleware\n let nextContext = context;\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n nextContext = {\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n };\n }\n\n // Call next middleware\n return next(nextContext);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE - Routes to appropriate snap(s)\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Guard against undefined request or chainIds\n if (!request?.chainIds) {\n return;\n }\n\n // Filter to chains we have a snap for\n const supportedChains = request.chainIds.filter((chainId) =>\n this.#isChainSupportedBySnap(chainId),\n );\n\n if (supportedChains.length === 0) {\n return;\n }\n\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.chains = supportedChains;\n // Do a fetch to get latest data on subscription update\n this.fetch({\n ...request,\n chainIds: supportedChains,\n })\n .then(async (fetchResponse) => {\n if (Object.keys(fetchResponse.assetsBalance ?? {}).length > 0) {\n await this.#messenger.call(\n 'AssetsController:assetsUpdate',\n fetchResponse,\n SNAP_DATA_SOURCE_NAME,\n );\n }\n return fetchResponse;\n })\n .catch((error) => {\n log('Subscription update fetch failed', { subscriptionId, error });\n });\n return;\n }\n }\n\n await this.unsubscribe(subscriptionId);\n\n // Snaps provide real-time updates via AccountsController:accountBalancesUpdated\n // We only need to track the subscription and do an initial fetch\n // No polling needed - updates come through #handleSnapBalancesUpdated\n\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n // No timer to clear - we use event-based updates\n },\n chains: supportedChains,\n });\n\n // Initial fetch to get current balances\n try {\n const fetchResponse = await this.fetch({\n ...request,\n chainIds: supportedChains,\n });\n\n if (Object.keys(fetchResponse.assetsBalance ?? {}).length > 0) {\n await this.#messenger.call(\n 'AssetsController:assetsUpdate',\n fetchResponse,\n SNAP_DATA_SOURCE_NAME,\n );\n }\n } catch (error) {\n log('Initial fetch failed', { subscriptionId, error });\n }\n }\n\n // ============================================================================\n // KEYRING CLIENT\n // ============================================================================\n\n /**\n * Gets a `KeyringClient` for a Snap.\n * Caches clients per snap ID to avoid re-instantiation across multiple calls.\n *\n * @param snapId - ID of the Snap to get the client for.\n * @returns A `KeyringClient` for the Snap.\n */\n #getKeyringClient(snapId: string): KeyringClient {\n const cachedClient = this.#keyringClientCache.get(snapId);\n if (cachedClient) {\n return cachedClient;\n }\n\n const client = new KeyringClient({\n send: async (request: JsonRpcRequest): Promise<Json> =>\n (await this.#messenger.call('SnapController:handleRequest', {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n })) as Json,\n });\n\n this.#keyringClientCache.set(snapId, client);\n return client;\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n\n // Unsubscribe from snap keyring events\n try {\n messenger.unsubscribe(\n 'AccountsController:accountBalancesUpdated',\n this.#handleSnapBalancesUpdatedBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from snap keyring events', { error });\n }\n\n // Unsubscribe from permission changes\n try {\n messenger.unsubscribe(\n 'PermissionController:stateChange',\n this.#handlePermissionStateChangeBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from permission changes', { error });\n }\n\n // Clean up active subscriptions\n for (const [subscriptionId] of this.activeSubscriptions) {\n this.unsubscribe(subscriptionId).catch(() => {\n // Ignore cleanup errors\n });\n }\n\n // Clear keyring client cache\n this.#keyringClientCache.clear();\n }\n}\n\n// ============================================================================\n// FACTORY\n// ============================================================================\n\nexport function createSnapDataSource(\n options: SnapDataSourceOptions,\n): SnapDataSource {\n return new SnapDataSource(options);\n}\n"]}
|