@metamask-previews/assets-controller 16.1.0-preview-b1e33e9f1 → 16.1.0-preview-d2ea3cc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -28
- package/dist/AssetsController-method-action-types.d.ts +1 -5
- package/dist/AssetsController-method-action-types.d.ts.map +1 -1
- package/dist/AssetsController-method-action-types.js.map +1 -1
- package/dist/AssetsController.d.ts +40 -8
- package/dist/AssetsController.d.ts.map +1 -1
- package/dist/AssetsController.js +235 -570
- package/dist/AssetsController.js.map +1 -1
- package/dist/data-sources/AbstractDataSource.d.ts +6 -1
- package/dist/data-sources/AbstractDataSource.d.ts.map +1 -1
- package/dist/data-sources/AbstractDataSource.js.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.ts +3 -17
- package/dist/data-sources/AccountsApiDataSource.d.ts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.js +125 -275
- package/dist/data-sources/AccountsApiDataSource.js.map +1 -1
- package/dist/data-sources/PriceDataSource.d.ts +6 -7
- package/dist/data-sources/PriceDataSource.d.ts.map +1 -1
- package/dist/data-sources/PriceDataSource.js +20 -15
- package/dist/data-sources/PriceDataSource.js.map +1 -1
- package/dist/data-sources/RpcDataSource.d.ts +1 -15
- package/dist/data-sources/RpcDataSource.d.ts.map +1 -1
- package/dist/data-sources/RpcDataSource.js +107 -398
- package/dist/data-sources/RpcDataSource.js.map +1 -1
- package/dist/data-sources/SnapDataSource.d.ts +0 -5
- package/dist/data-sources/SnapDataSource.d.ts.map +1 -1
- package/dist/data-sources/SnapDataSource.js +0 -114
- package/dist/data-sources/SnapDataSource.js.map +1 -1
- package/dist/data-sources/TokenDataSource.d.ts +1 -3
- package/dist/data-sources/TokenDataSource.d.ts.map +1 -1
- package/dist/data-sources/TokenDataSource.js +2 -4
- package/dist/data-sources/TokenDataSource.js.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.ts +0 -8
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.ts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.js +2 -47
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middlewares/CustomAssetGraduationMiddleware.d.ts +1 -3
- package/dist/middlewares/CustomAssetGraduationMiddleware.d.ts.map +1 -1
- package/dist/middlewares/CustomAssetGraduationMiddleware.js +1 -3
- package/dist/middlewares/CustomAssetGraduationMiddleware.js.map +1 -1
- package/dist/middlewares/DetectionMiddleware.d.ts +2 -10
- package/dist/middlewares/DetectionMiddleware.d.ts.map +1 -1
- package/dist/middlewares/DetectionMiddleware.js +23 -5
- package/dist/middlewares/DetectionMiddleware.js.map +1 -1
- package/dist/middlewares/ParallelMiddleware.d.ts.map +1 -1
- package/dist/middlewares/ParallelMiddleware.js +3 -0
- package/dist/middlewares/ParallelMiddleware.js.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.d.ts +15 -16
- package/dist/middlewares/RpcFallbackMiddleware.d.ts.map +1 -1
- package/dist/middlewares/RpcFallbackMiddleware.js +107 -125
- package/dist/middlewares/RpcFallbackMiddleware.js.map +1 -1
- package/dist/middlewares/index.d.ts +0 -1
- package/dist/middlewares/index.d.ts.map +1 -1
- package/dist/middlewares/index.js.map +1 -1
- package/dist/migrations/healAssetsInfoMetadata.d.ts +5 -5
- package/dist/migrations/healAssetsInfoMetadata.d.ts.map +1 -1
- package/dist/migrations/healAssetsInfoMetadata.js.map +1 -1
- package/dist/pipeline/buildFastFetchSources.d.ts +0 -5
- package/dist/pipeline/buildFastFetchSources.d.ts.map +1 -1
- package/dist/pipeline/buildFastFetchSources.js +1 -7
- package/dist/pipeline/buildFastFetchSources.js.map +1 -1
- package/dist/pipeline/executeAssetsPipeline.d.ts +3 -1
- package/dist/pipeline/executeAssetsPipeline.d.ts.map +1 -1
- package/dist/pipeline/executeAssetsPipeline.js +2 -1
- package/dist/pipeline/executeAssetsPipeline.js.map +1 -1
- package/dist/selectors/balance.js +1 -1
- package/dist/selectors/balance.js.map +1 -1
- package/dist/types.d.ts +23 -29
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/utils/assetVisibility.d.ts +0 -29
- package/dist/utils/assetVisibility.d.ts.map +0 -1
- package/dist/utils/assetVisibility.js +0 -80
- package/dist/utils/assetVisibility.js.map +0 -1
- package/dist/utils/filterFailedChainBalances.d.ts +0 -11
- package/dist/utils/filterFailedChainBalances.d.ts.map +0 -1
- package/dist/utils/filterFailedChainBalances.js +0 -22
- package/dist/utils/filterFailedChainBalances.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChainId, DataRequest, DataResponse } from '../types.js';
|
|
1
|
+
import type { ChainId, DataRequest, DataResponse, AssetsControllerStateInternal } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Subscription request from AssetsController.
|
|
4
4
|
*/
|
|
@@ -8,6 +8,11 @@ export type SubscriptionRequest = {
|
|
|
8
8
|
isUpdate: boolean;
|
|
9
9
|
/** Called when this data source has new asset data. Passed by the controller when subscribing. Optional request describes the update scope (e.g. dataTypes: ['balance'] to skip enrichment). */
|
|
10
10
|
onAssetsUpdate: (response: DataResponse, request?: DataRequest) => void | Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Optional state access (e.g. for price/token data sources that need assetsBalance).
|
|
13
|
+
* Provided by the controller when subscribing.
|
|
14
|
+
*/
|
|
15
|
+
getAssetsState?: () => AssetsControllerStateInternal;
|
|
11
16
|
/**
|
|
12
17
|
* When true, skip the one-shot fetch that normally runs when a subscription
|
|
13
18
|
* is created. Used after the controller has already force-fetched balances
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AbstractDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"AbstractDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AbstractDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,YAAY,EACZ,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAMrB;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,WAAW,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,gMAAgM;IAChM,cAAc,EAAE,CACd,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,WAAW,KAClB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,6BAA6B,CAAC;IACrD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sCAAsC;IACtC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,kCAAkC;IAClC,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,kEAAkE;IAClE,cAAc,EAAE,CACd,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,WAAW,KAClB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,wDAAwD;IACxD,YAAY,EAAE,OAAO,EAAE,CAAC;CACzB,CAAC;AAMF;;;;;;GAMG;AACH,8BAAsB,kBAAkB,CACtC,IAAI,SAAS,MAAM,EACnB,KAAK,SAAS,eAAe,GAAG,eAAe;IAE/C,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAE9B,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;IAEvB,iCAAiC;IACjC,SAAS,CAAC,QAAQ,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAC3D;IAEZ,YAAY,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAG1C;IAED;;;;OAIG;IACH,OAAO,IAAI,IAAI,CAEd;IAED;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAE1C;IAED;;;;OAIG;IACH,mBAAmB,IAAI,OAAO,EAAE,CAE/B;IAED;;OAEG;IACH,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;OAIG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMvD;IAED;;;;;OAKG;IACH,SAAS,CAAC,kBAAkB,CAC1B,MAAM,EAAE,OAAO,EAAE,EACjB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GACxC,IAAI,CAgBN;IAED;;;;;OAKG;IACH,SAAS,CAAC,cAAc,CACtB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GACxC,IAAI,CAKN;IAED;;;;;OAKG;IACH,SAAS,CAAC,iBAAiB,CACzB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GACxC,IAAI,CAON;IAED;;OAEG;IACH,OAAO,IAAI,IAAI,CAKd;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractDataSource.js","sourceRoot":"","sources":["../../src/data-sources/AbstractDataSource.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AbstractDataSource.js","sourceRoot":"","sources":["../../src/data-sources/AbstractDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ChainId,\n DataRequest,\n DataResponse,\n AssetsControllerStateInternal,\n} from '../types.js';\n\n// ============================================================================\n// DATA SOURCE BASE TYPES\n// ============================================================================\n\n/**\n * Subscription request from AssetsController.\n */\nexport type SubscriptionRequest = {\n request: DataRequest;\n subscriptionId: string;\n isUpdate: boolean;\n /** Called when this data source has new asset data. Passed by the controller when subscribing. Optional request describes the update scope (e.g. dataTypes: ['balance'] to skip enrichment). */\n onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n /**\n * Optional state access (e.g. for price/token data sources that need assetsBalance).\n * Provided by the controller when subscribing.\n */\n getAssetsState?: () => AssetsControllerStateInternal;\n /**\n * When true, skip the one-shot fetch that normally runs when a subscription\n * is created. Used after the controller has already force-fetched balances\n * for the same accounts/chains so we do not immediately hit the API again.\n */\n skipInitialFetch?: boolean;\n};\n\n/**\n * Active subscription entry stored by data sources.\n * Data sources can extend this with additional fields as needed.\n */\nexport type ActiveSubscription = {\n /** Cleanup function to unsubscribe */\n cleanup: () => void;\n /** Currently subscribed chains */\n chains: ChainId[];\n /** Original request (for polling data sources to handle account changes) */\n request?: DataRequest;\n /** Account addresses (for WebSocket data sources to detect account changes) */\n addresses?: string[];\n /** Callback to report asset updates (from SubscriptionRequest) */\n onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n};\n\n/**\n * Base state for all data sources.\n */\nexport type DataSourceState = {\n /** Currently active chains (supported AND available) */\n activeChains: ChainId[];\n};\n\n// ============================================================================\n// ABSTRACT DATA SOURCE\n// ============================================================================\n\n/**\n * Abstract base class for data sources.\n *\n * Data sources communicate with AssetsController via Messenger:\n * - Register actions that AssetsController can call\n * - Publish events that AssetsController subscribes to\n */\nexport abstract class AbstractDataSource<\n Name extends string,\n State extends DataSourceState = DataSourceState,\n> {\n protected readonly name: Name;\n\n protected state: State;\n\n /** Active subscriptions by ID */\n protected readonly activeSubscriptions: Map<string, ActiveSubscription> =\n new Map();\n\n constructor(name: Name, initialState: State) {\n this.name = name;\n this.state = initialState;\n }\n\n /**\n * Get the data source name/ID.\n *\n * @returns The name of this data source.\n */\n getName(): Name {\n return this.name;\n }\n\n /**\n * Get currently active chains (supported AND available).\n *\n * @returns Array of currently active chain IDs.\n */\n async getActiveChains(): Promise<ChainId[]> {\n return this.state.activeChains;\n }\n\n /**\n * Get currently active chains synchronously (no state duplication in controller).\n *\n * @returns Array of currently active chain IDs.\n */\n getActiveChainsSync(): ChainId[] {\n return this.state.activeChains;\n }\n\n /**\n * Subscribe to updates for the given request.\n */\n abstract subscribe(request: SubscriptionRequest): Promise<void>;\n\n /**\n * Unsubscribe from updates.\n *\n * @param subscriptionId - The ID of the subscription to cancel.\n */\n async unsubscribe(subscriptionId: string): Promise<void> {\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (subscription) {\n subscription.cleanup();\n this.activeSubscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Update active chains and notify listeners only if changed.\n *\n * @param chains - Array of chain IDs to set as active.\n * @param publishEvent - Callback to publish chain changes to listeners.\n */\n protected updateActiveChains(\n chains: ChainId[],\n publishEvent: (chains: ChainId[]) => void,\n ): void {\n const previousChains = new Set(this.state.activeChains);\n const newChains = new Set(chains);\n\n // Check if chains have actually changed\n const hasChanges =\n previousChains.size !== newChains.size ||\n chains.some((chain) => !previousChains.has(chain));\n\n // Always update state\n this.state.activeChains = chains;\n\n // Only publish event if there are actual changes\n if (hasChanges) {\n publishEvent(chains);\n }\n }\n\n /**\n * Add a chain to active chains.\n *\n * @param chainId - The chain ID to add.\n * @param publishEvent - Callback to publish chain changes to listeners.\n */\n protected addActiveChain(\n chainId: ChainId,\n publishEvent: (chains: ChainId[]) => void,\n ): void {\n if (!this.state.activeChains.includes(chainId)) {\n this.state.activeChains = [...this.state.activeChains, chainId];\n publishEvent(this.state.activeChains);\n }\n }\n\n /**\n * Remove a chain from active chains.\n *\n * @param chainId - The chain ID to remove.\n * @param publishEvent - Callback to publish chain changes to listeners.\n */\n protected removeActiveChain(\n chainId: ChainId,\n publishEvent: (chains: ChainId[]) => void,\n ): void {\n if (this.state.activeChains.includes(chainId)) {\n this.state.activeChains = this.state.activeChains.filter(\n (chain) => chain !== chainId,\n );\n publishEvent(this.state.activeChains);\n }\n }\n\n /**\n * Destroy the data source and clean up resources.\n */\n destroy(): void {\n for (const subscription of this.activeSubscriptions.values()) {\n subscription.cleanup();\n }\n this.activeSubscriptions.clear();\n }\n}\n"]}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ApiPlatformClient } from '@metamask/core-backend';
|
|
2
2
|
import type { RemoteFeatureFlagControllerGetStateAction, RemoteFeatureFlagControllerStateChangeEvent } from '@metamask/remote-feature-flag-controller';
|
|
3
3
|
import type { AssetsControllerMessenger } from '../AssetsController.js';
|
|
4
|
-
import type { ChainId, DataRequest, DataResponse, Middleware,
|
|
5
|
-
import type { GetAssetVisibility } from '../utils/assetVisibility.js';
|
|
4
|
+
import type { ChainId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from '../types.js';
|
|
6
5
|
import type { DataSourceState, SubscriptionRequest } from './AbstractDataSource.js';
|
|
7
6
|
import { AbstractDataSource } from './AbstractDataSource.js';
|
|
8
7
|
declare const CONTROLLER_NAME = "AccountsApiDataSource";
|
|
@@ -29,26 +28,13 @@ export type AccountsApiDataSourceConfig = {
|
|
|
29
28
|
export type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {
|
|
30
29
|
/**
|
|
31
30
|
* The AssetsController messenger (shared by all data sources). Used to read
|
|
32
|
-
*
|
|
31
|
+
* the `assetsAccountsApiV6` remote feature flag.
|
|
33
32
|
*/
|
|
34
33
|
messenger: AssetsControllerMessenger;
|
|
35
34
|
/** ApiPlatformClient for API calls with caching */
|
|
36
35
|
queryApiClient: ApiPlatformClient;
|
|
37
36
|
/** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
|
|
38
37
|
onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
|
|
39
|
-
/**
|
|
40
|
-
* Whether to use the Accounts API v6 balances endpoint (and `includeAssetIds`).
|
|
41
|
-
* Injected by AssetsController so the flag is read in one place. Defaults to
|
|
42
|
-
* `false` (v5). Read on demand, not cached.
|
|
43
|
-
*/
|
|
44
|
-
isBalanceV6Enabled?: () => boolean;
|
|
45
|
-
/**
|
|
46
|
-
* Current AssetsController state. Used for v6 include/exclude asset IDs and
|
|
47
|
-
* for filtering when token detection is off.
|
|
48
|
-
*/
|
|
49
|
-
getAssetsState: () => AssetsControllerState;
|
|
50
|
-
/** Returns shared visible/hidden assets for an account/chain scope. */
|
|
51
|
-
getAssetVisibility: GetAssetVisibility;
|
|
52
38
|
state?: Partial<AccountsApiDataSourceState>;
|
|
53
39
|
};
|
|
54
40
|
/**
|
|
@@ -59,7 +45,7 @@ export type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {
|
|
|
59
45
|
* @param assetsState - Current assets controller state to check existing balances against.
|
|
60
46
|
* @returns A new response with only known asset balances.
|
|
61
47
|
*/
|
|
62
|
-
export declare function filterResponseToKnownAssets(response: DataResponse, assetsState:
|
|
48
|
+
export declare function filterResponseToKnownAssets(response: DataResponse, assetsState: AssetsControllerStateInternal): DataResponse;
|
|
63
49
|
/**
|
|
64
50
|
* Data source for fetching balances from the MetaMask Accounts API.
|
|
65
51
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountsApiDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EACV,yCAAyC,EACzC,2CAA2C,EAC5C,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"AccountsApiDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AccountsApiDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EACV,yCAAyC,EACzC,2CAA2C,EAC5C,MAAM,0CAA0C,CAAC;AAQlD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EACV,OAAO,EAGP,WAAW,EACX,YAAY,EACZ,UAAU,EACV,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAMrB,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAO7D,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAehD,MAAM,MAAM,mCAAmC,GAC7C,yCAAyC,CAAC;AAI5C,MAAM,MAAM,kCAAkC,GAC5C,2CAA2C,CAAC;AAM9C,MAAM,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAUzD,wDAAwD;AACxD,MAAM,MAAM,2BAA2B,GAAG;IACxC,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC;IACtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,2BAA2B,GAAG;IACvE;;;OAGG;IACH,SAAS,EAAE,yBAAyB,CAAC;IACrC,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;IAClC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAgEF;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,6BAA6B,GACzC,YAAY,CAqCd;AAMD;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB,CAC3D,OAAO,eAAe,EACtB,0BAA0B,CAC3B;;IA0BC,YAAY,OAAO,EAAE,4BAA4B,EA+BhD;IA8FD;;;;;;OAMG;IACH,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnC;IA0BK,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA4FvD;IA2OD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAuEjC;IAMK,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoGvE;IAMD,OAAO,IAAI,IAAI,CAMd;CACF"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isCaipChainId, KnownCaipNamespace,
|
|
1
|
+
import { isCaipChainId, KnownCaipNamespace, toCaipChainId, } from '@metamask/utils';
|
|
2
2
|
import { projectLogger, createModuleLogger } from '../logger.js';
|
|
3
|
-
import { filterFailedChainBalances } from '../utils/filterFailedChainBalances.js';
|
|
4
3
|
import { fetchWithTimeout, normalizeAssetId } from '../utils/index.js';
|
|
5
4
|
import { getMigrationStages, shouldSupportChain, } from '../utils/snaps-assets-migration.js';
|
|
6
5
|
import { AbstractDataSource } from './AbstractDataSource.js';
|
|
@@ -117,14 +116,12 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
117
116
|
#tokenDetectionEnabled;
|
|
118
117
|
/** Shared AssetsController messenger, used to read remote feature flags. */
|
|
119
118
|
#messenger;
|
|
120
|
-
/** Injected by AssetsController; `true` when the v6 balances endpoint should be used. */
|
|
121
|
-
#isBalanceV6Enabled;
|
|
122
|
-
#getAssetsState;
|
|
123
|
-
#getAssetVisibility;
|
|
124
119
|
/** ApiPlatformClient for cached API calls */
|
|
125
120
|
#apiClient;
|
|
126
121
|
/** Chains refresh timer */
|
|
127
122
|
#chainsRefreshTimer = null;
|
|
123
|
+
/** State accessor from subscriptions (for filtering when tokenDetectionEnabled is false) */
|
|
124
|
+
#getAssetsState;
|
|
128
125
|
constructor(options) {
|
|
129
126
|
super(CONTROLLER_NAME, {
|
|
130
127
|
...defaultState,
|
|
@@ -136,10 +133,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
136
133
|
this.#tokenDetectionEnabled =
|
|
137
134
|
options.tokenDetectionEnabled ?? (() => true);
|
|
138
135
|
this.#messenger = options.messenger;
|
|
139
|
-
this.#isBalanceV6Enabled =
|
|
140
|
-
options.isBalanceV6Enabled ?? (() => false);
|
|
141
|
-
this.#getAssetsState = options.getAssetsState;
|
|
142
|
-
this.#getAssetVisibility = options.getAssetVisibility;
|
|
143
136
|
this.#apiClient = options.queryApiClient;
|
|
144
137
|
// The Snaps → AssetsController migration flags gate which migration networks
|
|
145
138
|
// (Solana, Stellar, Tron) are surfaced as active chains (see
|
|
@@ -172,6 +165,31 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
172
165
|
});
|
|
173
166
|
}
|
|
174
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Whether the Accounts API v6 balances endpoint is enabled, read from the
|
|
170
|
+
* RemoteFeatureFlagController (`assetsAccountsApiV6`). Read on demand (per
|
|
171
|
+
* fetch) rather than cached so the value can be toggled at runtime, and behaves
|
|
172
|
+
* the same across clients (extension, mobile). Defaults to `false` when the
|
|
173
|
+
* flag is unset or the controller is unavailable.
|
|
174
|
+
*
|
|
175
|
+
* The flag is a LaunchDarkly JSON variation shaped `{ value: boolean }` (same
|
|
176
|
+
* shape as `backendWebSocketConnection`), so the nested `value` is read rather
|
|
177
|
+
* than treating the flag as a plain boolean.
|
|
178
|
+
*
|
|
179
|
+
* @returns `true` when the v6 balances endpoint should be used.
|
|
180
|
+
*/
|
|
181
|
+
#isBalanceV6Enabled() {
|
|
182
|
+
try {
|
|
183
|
+
const { remoteFeatureFlags } = this.#messenger.call('RemoteFeatureFlagController:getState');
|
|
184
|
+
const flag = remoteFeatureFlags?.assetsAccountsApiV6;
|
|
185
|
+
return (typeof flag === 'object' &&
|
|
186
|
+
flag !== null &&
|
|
187
|
+
Boolean(flag.value));
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
175
193
|
// ============================================================================
|
|
176
194
|
// INITIALIZATION
|
|
177
195
|
// ============================================================================
|
|
@@ -233,33 +251,45 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
233
251
|
// FETCH
|
|
234
252
|
// ============================================================================
|
|
235
253
|
async fetch(request) {
|
|
236
|
-
if (this.#isBalanceV6Enabled()) {
|
|
237
|
-
return this.#fetchV6(request);
|
|
238
|
-
}
|
|
239
|
-
return this.#fetchV5(request);
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* v5 fetch. Unchanged from the pre-v6 handler; delete the v6 sibling first
|
|
243
|
-
* if `assetsAccountsApiV6` is rolled back.
|
|
244
|
-
*
|
|
245
|
-
* @param request - The data request.
|
|
246
|
-
* @returns Balances stamped `merge`, plus errors for chains the API could
|
|
247
|
-
* not process.
|
|
248
|
-
*/
|
|
249
|
-
async #fetchV5(request) {
|
|
250
254
|
let response = {};
|
|
251
|
-
|
|
255
|
+
// Filter to only chains supported by Accounts API
|
|
256
|
+
const supportedChains = new Set(this.state.activeChains);
|
|
257
|
+
const chainsToFetch = request.chainIds.filter((chainId) => supportedChains.has(chainId));
|
|
252
258
|
if (chainsToFetch.length === 0) {
|
|
253
|
-
|
|
259
|
+
// Mark unsupported chains as errors so they pass to next middleware
|
|
260
|
+
for (const chainId of request.chainIds) {
|
|
261
|
+
if (!supportedChains.has(chainId)) {
|
|
262
|
+
response.errors = response.errors ?? {};
|
|
263
|
+
response.errors[chainId] = 'Chain not supported by Accounts API';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
254
266
|
return response;
|
|
255
267
|
}
|
|
256
268
|
try {
|
|
257
|
-
|
|
269
|
+
// Build CAIP-10 account IDs (e.g., "eip155:1:0x1234...")
|
|
270
|
+
// Use pre-computed supportedChains per account from the request
|
|
271
|
+
const accountIds = request.accountsWithSupportedChains.flatMap(({ account, supportedChains: accountChains }) => chainsToFetch
|
|
272
|
+
.filter((chainId) => accountChains.includes(chainId))
|
|
273
|
+
.map((chainId) => `${chainId}:${account.address}`));
|
|
258
274
|
// Skip API call if no valid account-chain combinations
|
|
259
275
|
if (accountIds.length === 0) {
|
|
260
276
|
return response;
|
|
261
277
|
}
|
|
262
|
-
const
|
|
278
|
+
const fetchOptions = request.forceUpdate || request.bypassServerCache
|
|
279
|
+
? {
|
|
280
|
+
staleTime: 0,
|
|
281
|
+
gcTime: 0,
|
|
282
|
+
// Also defeats the API's server-side cache (via a random
|
|
283
|
+
// bypassServerCache query param) so a post-transaction refresh cannot
|
|
284
|
+
// be answered with a pre-transaction snapshot.
|
|
285
|
+
...(request.bypassServerCache ? { bypassServerCache: true } : {}),
|
|
286
|
+
}
|
|
287
|
+
: undefined;
|
|
288
|
+
// Feature-flagged: v6 endpoint with a fallback to legacy v5. The flag is
|
|
289
|
+
// read here (not cached) so a runtime toggle can revert v6 -> v5.
|
|
290
|
+
const { unprocessedNetworks, assetsBalance } = this.#isBalanceV6Enabled()
|
|
291
|
+
? await this.#fetchV6Balances(accountIds, fetchOptions, request)
|
|
292
|
+
: await this.#fetchV5Balances(accountIds, fetchOptions, request);
|
|
263
293
|
// Handle unprocessed networks - these will be passed to next middleware
|
|
264
294
|
if (unprocessedNetworks.length > 0) {
|
|
265
295
|
const unprocessedChainIds = unprocessedNetworks.map(caipChainIdToChainId);
|
|
@@ -273,145 +303,26 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
273
303
|
response.updateMode = 'merge';
|
|
274
304
|
}
|
|
275
305
|
catch (error) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
return response;
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* v6 fetch. Reads visibility from state, sends it as `includeAssetIds` /
|
|
287
|
-
* `excludeAssetIds`, and stamps `full`. A chain the API left unprocessed, or
|
|
288
|
-
* answered without every requested `includeAssetId`, is reported in `errors`
|
|
289
|
-
* and contributes no balances, so the RPC fallback can recover it.
|
|
290
|
-
*
|
|
291
|
-
* @param request - The data request.
|
|
292
|
-
* @returns An authoritative snapshot for the chains that succeeded.
|
|
293
|
-
*/
|
|
294
|
-
async #fetchV6(request) {
|
|
295
|
-
const response = {};
|
|
296
|
-
const chainsToFetch = this.#getChainsToFetch(request);
|
|
297
|
-
if (chainsToFetch.length === 0) {
|
|
298
|
-
this.#markUnsupportedChains(response, request);
|
|
299
|
-
return response;
|
|
300
|
-
}
|
|
301
|
-
try {
|
|
302
|
-
const accountIds = this.#buildAccountIds(request, chainsToFetch);
|
|
303
|
-
if (accountIds.length === 0) {
|
|
304
|
-
return response;
|
|
305
|
-
}
|
|
306
|
-
const { visibleAssetIds, hiddenAssetIds } = this.#getAssetVisibility(request.accountsWithSupportedChains.map(({ account }) => account.id), chainsToFetch);
|
|
307
|
-
const { unprocessedNetworks, unprocessedIncludeAssetIds, assetsBalance } = await this.#fetchV6Balances(accountIds, this.#buildFetchOptions(request), request, visibleAssetIds.length > 0 ? visibleAssetIds : undefined, hiddenAssetIds.length > 0 ? hiddenAssetIds : undefined);
|
|
308
|
-
response.updateMode = 'full';
|
|
309
|
-
const unprocessedChainIds = unprocessedNetworks.map(caipChainIdToChainId);
|
|
310
|
-
// Chains answered without every requested `includeAssetIds`. The
|
|
311
|
-
// snapshot is incomplete, so the chain counts as failed rather than
|
|
312
|
-
// silently dropping the assets it left out.
|
|
313
|
-
const incompleteChainIds = this.#getChainIdsForAssetIds(unprocessedIncludeAssetIds);
|
|
314
|
-
const failedChainIds = new Set([
|
|
315
|
-
...unprocessedChainIds,
|
|
316
|
-
...incompleteChainIds,
|
|
317
|
-
]);
|
|
318
|
-
// Errors hand these chains to the next middleware (the RPC fallback).
|
|
319
|
-
if (failedChainIds.size > 0) {
|
|
320
|
-
response.errors = response.errors ?? {};
|
|
321
|
-
for (const chainId of unprocessedChainIds) {
|
|
322
|
-
response.errors[chainId] = 'Unprocessed by Accounts API';
|
|
323
|
-
}
|
|
324
|
-
for (const chainId of incompleteChainIds) {
|
|
325
|
-
response.errors[chainId] ??= 'Unresolved includeAssetIds';
|
|
326
|
-
}
|
|
306
|
+
log('Fetch FAILED', { error, chains: chainsToFetch });
|
|
307
|
+
// On error, mark all chains as errors so they can be handled by next middleware
|
|
308
|
+
response.errors = response.errors ?? {};
|
|
309
|
+
for (const chainId of chainsToFetch) {
|
|
310
|
+
response.errors[chainId] =
|
|
311
|
+
`Fetch failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
327
312
|
}
|
|
328
|
-
// A failed chain contributes nothing, so its balances stay as they are.
|
|
329
|
-
response.assetsBalance = filterFailedChainBalances(assetsBalance, failedChainIds);
|
|
330
313
|
}
|
|
331
|
-
|
|
332
|
-
this.#markFetchFailure(response, chainsToFetch, error);
|
|
333
|
-
}
|
|
334
|
-
this.#markUnsupportedChains(response, request);
|
|
335
|
-
return response;
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Requested chains the Accounts API currently supports.
|
|
339
|
-
*
|
|
340
|
-
* @param request - The data request being fetched.
|
|
341
|
-
* @returns The subset of `request.chainIds` this source can answer.
|
|
342
|
-
*/
|
|
343
|
-
#getChainsToFetch(request) {
|
|
344
|
-
const supportedChains = new Set(this.state.activeChains);
|
|
345
|
-
return request.chainIds.filter((chainId) => supportedChains.has(chainId));
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Mark unsupported chains as errors so they pass to next middleware.
|
|
349
|
-
*
|
|
350
|
-
* @param response - Response being built; mutated in place.
|
|
351
|
-
* @param request - The data request being fetched.
|
|
352
|
-
*/
|
|
353
|
-
#markUnsupportedChains(response, request) {
|
|
354
|
-
const supportedChains = new Set(this.state.activeChains);
|
|
314
|
+
// Mark unsupported chains as errors so they pass to next middleware
|
|
355
315
|
for (const chainId of request.chainIds) {
|
|
356
316
|
if (!supportedChains.has(chainId)) {
|
|
357
317
|
response.errors = response.errors ?? {};
|
|
358
318
|
response.errors[chainId] = 'Chain not supported by Accounts API';
|
|
359
319
|
}
|
|
360
320
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
* pre-computed supportedChains per account on the request.
|
|
365
|
-
*
|
|
366
|
-
* @param request - The data request being fetched.
|
|
367
|
-
* @param chainsToFetch - Chains this source will ask the API for.
|
|
368
|
-
* @returns One CAIP-10 ID per account-chain combination.
|
|
369
|
-
*/
|
|
370
|
-
#buildAccountIds(request, chainsToFetch) {
|
|
371
|
-
return request.accountsWithSupportedChains.flatMap(({ account, supportedChains: accountChains }) => chainsToFetch
|
|
372
|
-
.filter((chainId) => accountChains.includes(chainId))
|
|
373
|
-
.map((chainId) => `${chainId}:${account.address}`));
|
|
374
|
-
}
|
|
375
|
-
#buildFetchOptions(request) {
|
|
376
|
-
if (!request.forceUpdate && !request.bypassServerCache) {
|
|
377
|
-
return undefined;
|
|
378
|
-
}
|
|
379
|
-
return {
|
|
380
|
-
staleTime: 0,
|
|
381
|
-
gcTime: 0,
|
|
382
|
-
// Also defeats the API's server-side cache (via a random
|
|
383
|
-
// bypassServerCache query param) so a post-transaction refresh cannot
|
|
384
|
-
// be answered with a pre-transaction snapshot.
|
|
385
|
-
...(request.bypassServerCache ? { bypassServerCache: true } : {}),
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* On error, mark all chains as errors so the next middleware handles them.
|
|
390
|
-
*
|
|
391
|
-
* @param response - Response being built; mutated in place.
|
|
392
|
-
* @param chainsToFetch - Chains the failed request covered.
|
|
393
|
-
* @param error - The thrown error.
|
|
394
|
-
*/
|
|
395
|
-
#markFetchFailure(response, chainsToFetch, error) {
|
|
396
|
-
log('Fetch FAILED', { error, chains: chainsToFetch });
|
|
397
|
-
response.errors = response.errors ?? {};
|
|
398
|
-
for (const chainId of chainsToFetch) {
|
|
399
|
-
response.errors[chainId] =
|
|
400
|
-
`Fetch failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
#getChainIdsForAssetIds(assetIds) {
|
|
404
|
-
const chainIds = new Set();
|
|
405
|
-
for (const assetId of assetIds) {
|
|
406
|
-
try {
|
|
407
|
-
chainIds.add(parseCaipAssetType(assetId).chainId);
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
// An unparseable ID cannot be attributed to a chain, so it cannot
|
|
411
|
-
// invalidate one either.
|
|
412
|
-
}
|
|
321
|
+
// When token detection is disabled, filter out tokens not already in state
|
|
322
|
+
if (!this.#tokenDetectionEnabled() && this.#getAssetsState) {
|
|
323
|
+
response = filterResponseToKnownAssets(response, this.#getAssetsState());
|
|
413
324
|
}
|
|
414
|
-
return
|
|
325
|
+
return response;
|
|
415
326
|
}
|
|
416
327
|
/**
|
|
417
328
|
* Fetch balances from the legacy v5 endpoint and process them.
|
|
@@ -426,8 +337,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
426
337
|
const { assetsBalance } = this.#processV5Balances(apiResponse.balances, request);
|
|
427
338
|
return {
|
|
428
339
|
unprocessedNetworks: apiResponse.unprocessedNetworks,
|
|
429
|
-
// v5 has no `includeAssetIds` support.
|
|
430
|
-
unprocessedIncludeAssetIds: [],
|
|
431
340
|
assetsBalance,
|
|
432
341
|
};
|
|
433
342
|
}
|
|
@@ -437,23 +346,13 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
437
346
|
* @param accountIds - CAIP-10 account IDs to fetch balances for.
|
|
438
347
|
* @param fetchOptions - Cache/fetch options (e.g. force update settings).
|
|
439
348
|
* @param request - The original data request containing accounts to map.
|
|
440
|
-
* @
|
|
441
|
-
* @param excludeAssetIds - Hidden asset IDs the backend must drop.
|
|
442
|
-
* @returns Unprocessed networks, unprocessed pinned assets, and processed
|
|
443
|
-
* asset balances by account.
|
|
349
|
+
* @returns Unprocessed networks and processed asset balances by account.
|
|
444
350
|
*/
|
|
445
|
-
async #fetchV6Balances(accountIds, fetchOptions, request
|
|
446
|
-
const
|
|
447
|
-
? {
|
|
448
|
-
...(includeAssetIds && { includeAssetIds }),
|
|
449
|
-
...(excludeAssetIds && { excludeAssetIds }),
|
|
450
|
-
}
|
|
451
|
-
: undefined;
|
|
452
|
-
const apiResponse = await fetchWithTimeout(() => this.#apiClient.accounts.fetchV6MultiAccountBalances(accountIds, params, fetchOptions), this.#fetchTimeoutMs);
|
|
351
|
+
async #fetchV6Balances(accountIds, fetchOptions, request) {
|
|
352
|
+
const apiResponse = await fetchWithTimeout(() => this.#apiClient.accounts.fetchV6MultiAccountBalances(accountIds, undefined, fetchOptions), this.#fetchTimeoutMs);
|
|
453
353
|
const { assetsBalance } = this.#processV6Balances(apiResponse.balances, request);
|
|
454
354
|
return {
|
|
455
355
|
unprocessedNetworks: apiResponse.unprocessedNetworks,
|
|
456
|
-
unprocessedIncludeAssetIds: apiResponse.unprocessedIncludeAssetIds,
|
|
457
356
|
assetsBalance,
|
|
458
357
|
};
|
|
459
358
|
}
|
|
@@ -583,118 +482,69 @@ export class AccountsApiDataSource extends AbstractDataSource {
|
|
|
583
482
|
* @returns The middleware function for the assets pipeline.
|
|
584
483
|
*/
|
|
585
484
|
get assetsMiddleware() {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
#assetsMiddlewareV5 = async (context, next) => {
|
|
592
|
-
const { request } = context;
|
|
593
|
-
// Price/metadata-only requests must not hit the Accounts API.
|
|
594
|
-
if (!request.dataTypes.includes('balance')) {
|
|
595
|
-
return next(context);
|
|
596
|
-
}
|
|
597
|
-
// If no chains requested, skip to next middleware
|
|
598
|
-
if (request.chainIds.length === 0) {
|
|
599
|
-
return next(context);
|
|
600
|
-
}
|
|
601
|
-
let successfullyHandledChains = [];
|
|
602
|
-
try {
|
|
603
|
-
const response = await this.fetch(request);
|
|
604
|
-
// Merge response into context
|
|
605
|
-
if (response.assetsBalance) {
|
|
606
|
-
context.response.assetsBalance ??= {};
|
|
607
|
-
for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
|
|
608
|
-
context.response.assetsBalance[accountId] = {
|
|
609
|
-
...context.response.assetsBalance[accountId],
|
|
610
|
-
...accountBalances,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
485
|
+
return async (context, next) => {
|
|
486
|
+
const { request } = context;
|
|
487
|
+
// Price/metadata-only requests must not hit the Accounts API.
|
|
488
|
+
if (!request.dataTypes.includes('balance')) {
|
|
489
|
+
return next(context);
|
|
613
490
|
}
|
|
614
|
-
//
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
// When token detection is off and we filtered out all balance data (e.g. new
|
|
618
|
-
// account with empty state), do not claim any chain as handled so that RPC
|
|
619
|
-
// middleware can still process them and fetch native balances (ETH, MATIC, etc.).
|
|
620
|
-
if (!this.#tokenDetectionEnabled() &&
|
|
621
|
-
(!response.assetsBalance ||
|
|
622
|
-
Object.keys(response.assetsBalance).length === 0)) {
|
|
623
|
-
successfullyHandledChains = [];
|
|
491
|
+
// If no chains requested, skip to next middleware
|
|
492
|
+
if (request.chainIds.length === 0) {
|
|
493
|
+
return next(context);
|
|
624
494
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
return next(context);
|
|
649
|
-
}
|
|
650
|
-
// If no chains requested, skip to next middleware
|
|
651
|
-
if (request.chainIds.length === 0) {
|
|
652
|
-
return next(context);
|
|
653
|
-
}
|
|
654
|
-
let remainingChains = request.chainIds;
|
|
655
|
-
try {
|
|
656
|
-
const response = await this.fetch(request);
|
|
657
|
-
// Merge response into context
|
|
658
|
-
if (response.assetsBalance) {
|
|
659
|
-
context.response.assetsBalance ??= {};
|
|
660
|
-
for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
|
|
661
|
-
context.response.assetsBalance[accountId] = {
|
|
662
|
-
...context.response.assetsBalance[accountId],
|
|
663
|
-
...accountBalances,
|
|
664
|
-
};
|
|
495
|
+
let successfullyHandledChains = [];
|
|
496
|
+
try {
|
|
497
|
+
const response = await this.fetch(request);
|
|
498
|
+
// Merge response into context
|
|
499
|
+
if (response.assetsBalance) {
|
|
500
|
+
context.response.assetsBalance ??= {};
|
|
501
|
+
for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
|
|
502
|
+
context.response.assetsBalance[accountId] = {
|
|
503
|
+
...context.response.assetsBalance[accountId],
|
|
504
|
+
...accountBalances,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// Determine successfully handled chains (exclude unprocessed/error chains)
|
|
509
|
+
const unprocessedChains = new Set(Object.keys(response.errors ?? {}));
|
|
510
|
+
successfullyHandledChains = request.chainIds.filter((chainId) => !unprocessedChains.has(chainId));
|
|
511
|
+
// When token detection is off and we filtered out all balance data (e.g. new
|
|
512
|
+
// account with empty state), do not claim any chain as handled so that RPC
|
|
513
|
+
// middleware can still process them and fetch native balances (ETH, MATIC, etc.).
|
|
514
|
+
if (!this.#tokenDetectionEnabled() &&
|
|
515
|
+
(!response.assetsBalance ||
|
|
516
|
+
Object.keys(response.assetsBalance).length === 0)) {
|
|
517
|
+
successfullyHandledChains = [];
|
|
665
518
|
}
|
|
666
519
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
};
|
|
671
|
-
const errors = response.errors ?? {};
|
|
672
|
-
remainingChains = request.chainIds.filter((chainId) => errors[chainId]);
|
|
673
|
-
// Remaining chains are exactly the failed ones; RPC fallback retries them.
|
|
674
|
-
if (remainingChains.length > 0) {
|
|
675
|
-
context.response.errors = {
|
|
676
|
-
...context.response.errors,
|
|
677
|
-
...Object.fromEntries(remainingChains.map((chainId) => [chainId, errors[chainId]])),
|
|
678
|
-
};
|
|
520
|
+
catch (error) {
|
|
521
|
+
log('Middleware fetch failed', { error });
|
|
522
|
+
successfullyHandledChains = [];
|
|
679
523
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
|
|
524
|
+
// Remove successfully handled chains from request for next middleware
|
|
525
|
+
if (successfullyHandledChains.length > 0) {
|
|
526
|
+
const remainingChains = request.chainIds.filter((chainId) => !successfullyHandledChains.includes(chainId));
|
|
527
|
+
return next({
|
|
528
|
+
...context,
|
|
529
|
+
request: {
|
|
530
|
+
...request,
|
|
531
|
+
chainIds: remainingChains,
|
|
532
|
+
},
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
// No chains handled - pass context unchanged
|
|
536
|
+
return next(context);
|
|
537
|
+
};
|
|
538
|
+
}
|
|
693
539
|
// ============================================================================
|
|
694
540
|
// SUBSCRIBE
|
|
695
541
|
// ============================================================================
|
|
696
542
|
async subscribe(subscriptionRequest) {
|
|
697
543
|
const { request, subscriptionId, isUpdate, skipInitialFetch } = subscriptionRequest;
|
|
544
|
+
// Store state accessor for filtering when tokenDetectionEnabled is false
|
|
545
|
+
if (subscriptionRequest.getAssetsState) {
|
|
546
|
+
this.#getAssetsState = subscriptionRequest.getAssetsState;
|
|
547
|
+
}
|
|
698
548
|
// Try all requested chains - API will handle unsupported ones via unprocessedNetworks
|
|
699
549
|
const chainsToSubscribe = request.chainIds;
|
|
700
550
|
if (chainsToSubscribe.length === 0) {
|