@metamask-previews/assets-controller 16.0.0-preview-ff6e6f34a → 16.0.0-preview-95104ffdb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +4 -10
  2. package/dist/AssetsController.d.ts +3 -7
  3. package/dist/AssetsController.d.ts.map +1 -1
  4. package/dist/AssetsController.js +240 -671
  5. package/dist/AssetsController.js.map +1 -1
  6. package/dist/data-sources/AbstractDataSource.d.ts +1 -13
  7. package/dist/data-sources/AbstractDataSource.d.ts.map +1 -1
  8. package/dist/data-sources/AbstractDataSource.js +0 -23
  9. package/dist/data-sources/AbstractDataSource.js.map +1 -1
  10. package/dist/data-sources/AccountActivityDataSource.d.ts.map +1 -1
  11. package/dist/data-sources/AccountActivityDataSource.js +1 -2
  12. package/dist/data-sources/AccountActivityDataSource.js.map +1 -1
  13. package/dist/data-sources/AccountsApiDataSource.d.ts +2 -18
  14. package/dist/data-sources/AccountsApiDataSource.d.ts.map +1 -1
  15. package/dist/data-sources/AccountsApiDataSource.js +41 -165
  16. package/dist/data-sources/AccountsApiDataSource.js.map +1 -1
  17. package/dist/data-sources/RpcDataSource.d.ts +0 -17
  18. package/dist/data-sources/RpcDataSource.d.ts.map +1 -1
  19. package/dist/data-sources/RpcDataSource.js +23 -200
  20. package/dist/data-sources/RpcDataSource.js.map +1 -1
  21. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.ts +2 -7
  22. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.ts.map +1 -1
  23. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.js +12 -23
  24. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.js.map +1 -1
  25. package/dist/data-sources/evm-rpc-services/types/state.d.ts +2 -3
  26. package/dist/data-sources/evm-rpc-services/types/state.d.ts.map +1 -1
  27. package/dist/data-sources/evm-rpc-services/types/state.js.map +1 -1
  28. package/dist/middlewares/ParallelMiddleware.d.ts.map +1 -1
  29. package/dist/middlewares/ParallelMiddleware.js +0 -9
  30. package/dist/middlewares/ParallelMiddleware.js.map +1 -1
  31. package/dist/middlewares/RpcFallbackMiddleware.d.ts +14 -10
  32. package/dist/middlewares/RpcFallbackMiddleware.d.ts.map +1 -1
  33. package/dist/middlewares/RpcFallbackMiddleware.js +123 -157
  34. package/dist/middlewares/RpcFallbackMiddleware.js.map +1 -1
  35. package/dist/types.d.ts +24 -28
  36. package/dist/types.d.ts.map +1 -1
  37. package/dist/types.js.map +1 -1
  38. package/package.json +4 -4
@@ -1,4 +1,4 @@
1
- import type { Caip19AssetId, ChainId, DataRequest, DataResponse, AssetsControllerStateInternal } from '../types.js';
1
+ import type { ChainId, DataRequest, DataResponse, AssetsControllerStateInternal } from '../types.js';
2
2
  /**
3
3
  * Subscription request from AssetsController.
4
4
  */
@@ -74,18 +74,6 @@ export declare abstract class AbstractDataSource<Name extends string, State exte
74
74
  * @returns Array of currently active chain IDs.
75
75
  */
76
76
  getActiveChainsSync(): ChainId[];
77
- /**
78
- * Claim the pinned assets this source commits to serving. Called during the
79
- * subscription handoff; claimed assets are not offered to lower-priority
80
- * sources. Assets a source cannot resolve at fetch time are released via
81
- * `DataResponse.unprocessedCustomAssets`. Default: claim assets on this
82
- * source's assigned chains.
83
- *
84
- * @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.
85
- * @param assignedChains - Chains assigned to this source in the handoff.
86
- * @returns The claimed subset of `customAssets`.
87
- */
88
- claimCustomAssets(customAssets: Caip19AssetId[], assignedChains: ChainId[]): Caip19AssetId[];
89
77
  /**
90
78
  * Subscribe to updates for the given request.
91
79
  */
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AbstractDataSource.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,aAAa,EACb,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;;;;;;;;;;OAUG;IACH,iBAAiB,CACf,YAAY,EAAE,aAAa,EAAE,EAC7B,cAAc,EAAE,OAAO,EAAE,GACxB,aAAa,EAAE,CASjB;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
+ {"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,4 +1,3 @@
1
- import { parseCaipAssetType } from '@metamask/utils';
2
1
  // ============================================================================
3
2
  // ABSTRACT DATA SOURCE
4
3
  // ============================================================================
@@ -42,28 +41,6 @@ export class AbstractDataSource {
42
41
  getActiveChainsSync() {
43
42
  return this.state.activeChains;
44
43
  }
45
- /**
46
- * Claim the pinned assets this source commits to serving. Called during the
47
- * subscription handoff; claimed assets are not offered to lower-priority
48
- * sources. Assets a source cannot resolve at fetch time are released via
49
- * `DataResponse.unprocessedCustomAssets`. Default: claim assets on this
50
- * source's assigned chains.
51
- *
52
- * @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.
53
- * @param assignedChains - Chains assigned to this source in the handoff.
54
- * @returns The claimed subset of `customAssets`.
55
- */
56
- claimCustomAssets(customAssets, assignedChains) {
57
- const assigned = new Set(assignedChains);
58
- return customAssets.filter((assetId) => {
59
- try {
60
- return assigned.has(parseCaipAssetType(assetId).chainId);
61
- }
62
- catch {
63
- return false;
64
- }
65
- });
66
- }
67
44
  /**
68
45
  * Unsubscribe from updates.
69
46
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractDataSource.js","sourceRoot":"","sources":["../../src/data-sources/AbstractDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseCaipAssetType } from '@metamask/utils';\n\nimport type {\n Caip19AssetId,\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 * Claim the pinned assets this source commits to serving. Called during the\n * subscription handoff; claimed assets are not offered to lower-priority\n * sources. Assets a source cannot resolve at fetch time are released via\n * `DataResponse.unprocessedCustomAssets`. Default: claim assets on this\n * source's assigned chains.\n *\n * @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.\n * @param assignedChains - Chains assigned to this source in the handoff.\n * @returns The claimed subset of `customAssets`.\n */\n claimCustomAssets(\n customAssets: Caip19AssetId[],\n assignedChains: ChainId[],\n ): Caip19AssetId[] {\n const assigned = new Set<ChainId>(assignedChains);\n return customAssets.filter((assetId) => {\n try {\n return assigned.has(parseCaipAssetType(assetId).chainId);\n } catch {\n return false;\n }\n });\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
+ {"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 +1 @@
1
- {"version":3,"file":"AccountActivityDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yCAAyC,EACzC,wCAAwC,EAEzC,MAAM,wBAAwB,CAAC;AAKhC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EAGV,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM/D,QAAA,MAAM,eAAe,8BAA8B,CAAC;AAqFpD,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAC9C,yCAAyC,GACzC,wCAAwC,CAAC;AAM7C,MAAM,MAAM,8BAA8B,GAAG,eAAe,CAAC;AAU7D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,mEAAmE;IACnE,SAAS,EAAE,yBAAyB,CAAC;IACrC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,wFAAwF;IACxF,YAAY,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IACrE;;;;;OAKG;IACH,cAAc,EAAE,CACd,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,WAAW,KAClB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CACjD,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,yBAA0B,SAAQ,kBAAkB,CAC/D,OAAO,eAAe,EACtB,8BAA8B,CAC/B;;IAwBC,YAAY,OAAO,EAAE,gCAAgC,EAapD;IAwBD;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/B;IAwJD,OAAO,IAAI,IAAI,CAoBd;CACF;AAMD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,yBAAyB,CAE3B"}
1
+ {"version":3,"file":"AccountActivityDataSource.d.ts","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yCAAyC,EACzC,wCAAwC,EAEzC,MAAM,wBAAwB,CAAC;AAKhC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EAGV,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACb,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM/D,QAAA,MAAM,eAAe,8BAA8B,CAAC;AAsFpD,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAC9C,yCAAyC,GACzC,wCAAwC,CAAC;AAM7C,MAAM,MAAM,8BAA8B,GAAG,eAAe,CAAC;AAU7D,MAAM,MAAM,gCAAgC,GAAG;IAC7C,mEAAmE;IACnE,SAAS,EAAE,yBAAyB,CAAC;IACrC,qGAAqG;IACrG,qBAAqB,EAAE,CACrB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,OAAO,EAAE,EACjB,cAAc,EAAE,OAAO,EAAE,KACtB,IAAI,CAAC;IACV,wFAAwF;IACxF,YAAY,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IACrE;;;;;OAKG;IACH,cAAc,EAAE,CACd,QAAQ,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,WAAW,KAClB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CACjD,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,yBAA0B,SAAQ,kBAAkB,CAC/D,OAAO,eAAe,EACtB,8BAA8B,CAC/B;;IAwBC,YAAY,OAAO,EAAE,gCAAgC,EAapD;IAqBD;;;;;;;OAOG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAE/B;IAwJD,OAAO,IAAI,IAAI,CAoBd;CACF;AAMD;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,gCAAgC,GACxC,yBAAyB,CAE3B"}
@@ -45,6 +45,7 @@ function processAccountActivityBalanceUpdates(updates, accountId, getAssetType)
45
45
  .toFixed();
46
46
  assetsBalance[accountId][assetId] = {
47
47
  amount: humanReadableAmount,
48
+ ...(postBalance.metadata ? { metadata: postBalance.metadata } : {}),
48
49
  };
49
50
  assetsMetadata[assetId] = {
50
51
  type: getAssetType(assetId),
@@ -126,8 +127,6 @@ export class AccountActivityDataSource extends AbstractDataSource {
126
127
  // ============================================================================
127
128
  // SUBSCRIBE / UNSUBSCRIBE
128
129
  // ============================================================================
129
- // Pins on active chains are claimed via the inherited `claimCustomAssets`:
130
- // the activity stream pushes all activity for the address, pins included.
131
130
  /**
132
131
  * AADS is event-driven and chain-agnostic: it never participates in the
133
132
  * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`
@@ -1 +1 @@
1
- {"version":3,"file":"AccountActivityDataSource.js","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountActivityServiceBalanceUpdatedEvent,\n AccountActivityServiceStatusChangedEvent,\n BalanceUpdate,\n} from '@metamask/core-backend';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { isCaipChainId } from '@metamask/utils';\nimport { BigNumber as BigNumberJS } from 'bignumber.js';\n\nimport type { AssetsControllerMessenger } from '../AssetsController.js';\nimport { projectLogger, createModuleLogger } from '../logger.js';\nimport type {\n AssetBalance,\n AssetMetadata,\n ChainId,\n Caip19AssetId,\n DataRequest,\n DataResponse,\n} from '../types.js';\nimport { safeNormalizeAssetId } from '../utils/index.js';\nimport { AbstractDataSource } from './AbstractDataSource.js';\nimport type { DataSourceState } from './AbstractDataSource.js';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountActivityDataSource';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// BALANCE UPDATE PROCESSING\n// ============================================================================\n\n/**\n * Convert AccountActivityMessage balance updates into a {@link DataResponse}\n * for AssetsController.\n *\n * @param updates - Balance updates from account-activity websocket payload.\n * @param accountId - Internal account UUID.\n * @param getAssetType - Resolver for asset metadata type.\n * @returns DataResponse with merge mode when balances are present.\n */\nfunction processAccountActivityBalanceUpdates(\n updates: BalanceUpdate[],\n accountId: string,\n getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl',\n): DataResponse {\n const assetsBalance = Object.create(null) as Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n >;\n assetsBalance[accountId] = Object.create(null) as Record<\n Caip19AssetId,\n AssetBalance\n >;\n const assetsMetadata = Object.create(null) as Record<\n Caip19AssetId,\n AssetMetadata\n >;\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n if (!asset || !postBalance) {\n continue;\n }\n\n // The websocket sends lower-case ERC-20 addresses, while state and the\n // rest of the pipeline key assets by their checksummed ID. Normalizing\n // here keeps middleware comparisons (detection, custom-asset graduation,\n // occurrence filtering) from treating an existing holding as brand new.\n const assetId = safeNormalizeAssetId(asset.type as Caip19AssetId);\n\n if (asset.decimals === undefined) {\n continue;\n }\n\n const rawBalanceStr = postBalance.amount.startsWith('0x')\n ? BigInt(postBalance.amount).toString()\n : postBalance.amount;\n\n const humanReadableAmount = new BigNumberJS(rawBalanceStr)\n .dividedBy(new BigNumberJS(10).pow(asset.decimals))\n .toFixed();\n\n assetsBalance[accountId][assetId] = {\n amount: humanReadableAmount,\n };\n\n assetsMetadata[assetId] = {\n type: getAssetType(assetId),\n symbol: asset.unit,\n name: asset.unit,\n decimals: asset.decimals,\n };\n }\n\n const response: DataResponse = { updateMode: 'merge' };\n if (Object.keys(assetsBalance[accountId]).length > 0) {\n response.assetsBalance = assetsBalance;\n response.assetsInfo = assetsMetadata;\n }\n\n return response;\n}\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/** Allowed events that AccountActivityDataSource subscribes to. */\nexport type AccountActivityDataSourceAllowedEvents =\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceStatusChangedEvent;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountActivityDataSourceState = DataSourceState;\n\nconst defaultState: AccountActivityDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type AccountActivityDataSourceOptions = {\n /** The AssetsController messenger (shared by all data sources). */\n messenger: AssetsControllerMessenger;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n /** Returns the asset type ('native' | 'erc20' | 'spl') for a given CAIP-19 asset ID. */\n getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl';\n /**\n * Pushes decoded balance updates to the controller (bound to\n * `AssetsController.handleAssetsUpdate`). AADS is event-driven and never\n * takes part in the controller's subscribe handoff, so it receives this\n * callback directly instead of via a `SubscriptionRequest`.\n */\n onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n state?: Partial<AccountActivityDataSourceState>;\n};\n\n// ============================================================================\n// ACCOUNT ACTIVITY DATA SOURCE\n// ============================================================================\n\n/**\n * Data source that consumes real-time updates from `AccountActivityService`.\n *\n * `AccountActivityService` owns the WebSocket connection and channel\n * subscriptions; this data source is a thin consumer of the two high-level\n * events that it publishes:\n *\n * - `AccountActivityService:balanceUpdated` — post-transaction balances for the\n * subscribed account(s). The address is resolved against the wallet's\n * selected account group, transformed into a {@link DataResponse} (via\n * {@link processAccountActivityBalanceUpdates}), and pushed to the controller\n * through the injected `onAssetsUpdate` callback.\n * - `AccountActivityService:statusChanged` — per-chain \"up\"/\"down\" notifications.\n * A chain reported \"up\" is claimed as an active chain (WebSocket is providing\n * real-time data); a chain reported \"down\" is released so polling data sources\n * can take over. Each change is applied directly via `updateActiveChains`,\n * which notifies the controller through `onActiveChainsUpdated`.\n *\n * This data source does NOT debounce, jitter, or gate chain updates itself.\n * Coalescing (collapsing bursts) and jitter (staggering the WS-subscribe herd\n * across clients) live in `AssetsController`, where the expensive re-subscribe\n * happens and where updates from all data sources converge. `activeChains` are\n * never seeded from the accounts API; they only ever reflect live\n * `statusChanged` events (the service flushes all tracked chains as \"down\" when\n * the WebSocket disconnects, releasing them).\n *\n * It subscribes to these events in its constructor (the same way\n * `TokenBalancesController` does). Unlike the polling data sources it does not\n * take part in the controller's subscribe/unsubscribe handoff: `subscribe` is a\n * no-op, balance updates are routed by resolving the address against the\n * wallet, and updates are pushed through the injected `onAssetsUpdate` callback.\n */\nexport class AccountActivityDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountActivityDataSourceState\n> {\n readonly #messenger: AssetsControllerMessenger;\n\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #getAssetType: (\n assetId: Caip19AssetId,\n ) => 'native' | 'erc20' | 'spl';\n\n readonly #onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n\n readonly #onBalanceUpdatedBound: (event: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }) => void;\n\n constructor(options: AccountActivityDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#getAssetType = options.getAssetType;\n this.#onAssetsUpdate = options.onAssetsUpdate;\n this.#onBalanceUpdatedBound = this.#onBalanceUpdated.bind(this);\n\n this.#subscribeToEvents();\n }\n\n // ============================================================================\n // EVENT SUBSCRIPTIONS\n // ============================================================================\n\n #subscribeToEvents(): void {\n this.#messenger.subscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onBalanceUpdatedBound,\n );\n this.#messenger.subscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged,\n );\n }\n\n // ============================================================================\n // SUBSCRIBE / UNSUBSCRIBE\n // ============================================================================\n\n // Pins on active chains are claimed via the inherited `claimCustomAssets`:\n // the activity stream pushes all activity for the address, pins included.\n\n /**\n * AADS is event-driven and chain-agnostic: it never participates in the\n * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`\n * events are routed by resolving the address against the wallet's accounts\n * (see `#onBalanceUpdated`), and updates are pushed through the injected\n * `#onAssetsUpdate` callback. This override exists only to satisfy the\n * abstract contract and is intentionally a no-op.\n */\n async subscribe(): Promise<void> {\n // Intentionally empty — see method doc.\n }\n\n // ============================================================================\n // BALANCE UPDATES\n // ============================================================================\n\n #onBalanceUpdated({\n address,\n chain,\n updates,\n }: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }): void {\n try {\n if (!address || !chain || !updates || updates.length === 0) {\n return;\n }\n\n const account = this.#findAccountForAddress(address);\n if (!account) {\n return;\n }\n\n const chainId = chain as ChainId;\n\n const response = processAccountActivityBalanceUpdates(\n updates,\n account.id,\n (assetId) => this.#getAssetType(assetId),\n );\n\n if (!response.assetsBalance) {\n return;\n }\n\n const request: DataRequest = {\n accountsWithSupportedChains: [{ account, supportedChains: [chainId] }],\n chainIds: [chainId],\n dataTypes: ['balance', 'metadata'],\n };\n\n Promise.resolve(this.#onAssetsUpdate(response, request)).catch(\n (error) => {\n log('Failed to report balance update', { error });\n },\n );\n } catch (error) {\n log('Error handling balance update', error);\n }\n }\n\n /**\n * Find the wallet account matching the given activity address. EVM addresses\n * are matched case-insensitively; other namespaces are matched exactly.\n *\n * The candidate set is the accounts of the selected account group, resolved\n * from the wallet at event time (rather than from a stored subscription), so\n * AADS does not need to be re-subscribed whenever the account set changes.\n *\n * @param address - The account address from the activity message.\n * @returns The matching account, or null.\n */\n #findAccountForAddress(address: string): InternalAccount | null {\n const isEvm = address.startsWith('0x');\n\n return (\n this.#getWalletAccounts().find((candidate) =>\n isEvm\n ? candidate.address.toLowerCase() === address.toLowerCase()\n : candidate.address === address,\n ) ?? null\n );\n }\n\n /**\n * Resolve the accounts of the selected account group from the wallet,\n * mirroring `AssetsController.#getSelectedAccounts`.\n *\n * @returns The accounts of the selected account group.\n */\n #getWalletAccounts(): InternalAccount[] {\n const accounts = this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n if (accounts.length > 0) {\n return accounts;\n }\n\n const selectedAccount = this.#messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n return selectedAccount ? [selectedAccount] : [];\n }\n\n // ============================================================================\n // STATUS CHANGES\n // ============================================================================\n\n /**\n * Handle a `statusChanged` notification. Chains reported \"up\" are claimed as\n * active (WebSocket provides real-time data); chains reported \"down\" are\n * released so polling data sources take over. The change is applied directly;\n * coalescing and jitter are handled by `AssetsController`.\n *\n * @param options - The status change notification.\n * @param options.chainIds - The CAIP-2 chain IDs whose status changed.\n * @param options.status - Whether the chains are `up` or `down`.\n * @param options.timestamp - Optional timestamp of the status change.\n */\n readonly #onAccountActivityStatusChanged = ({\n chainIds,\n status,\n }: {\n chainIds: string[];\n status: 'up' | 'down';\n timestamp?: number;\n }): void => {\n try {\n // Act on every namespace (eip155, solana, etc.); AssetsController is\n // multichain. Only skip identifiers that are not valid CAIP-2 chain IDs.\n const validChains = chainIds.filter((chainId) => isCaipChainId(chainId));\n\n if (validChains.length === 0) {\n return;\n }\n\n const next = new Set(this.state.activeChains);\n if (status === 'up') {\n for (const chainId of validChains) {\n next.add(chainId);\n }\n } else {\n for (const chainId of validChains) {\n next.delete(chainId);\n }\n }\n\n const previous = [...this.state.activeChains];\n this.updateActiveChains(Array.from(next), (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n } catch (error) {\n log('Error handling status change', error);\n }\n };\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n try {\n this.#messenger.unsubscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onBalanceUpdatedBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from balanceUpdated', { error });\n }\n\n try {\n this.#messenger.unsubscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged,\n );\n } catch (error) {\n log('Failed to unsubscribe from statusChanged', { error });\n }\n\n super.destroy();\n }\n}\n\n// ============================================================================\n// FACTORY FUNCTION\n// ============================================================================\n\n/**\n * Creates an AccountActivityDataSource instance.\n *\n * @param options - Configuration options for the data source.\n * @returns A new AccountActivityDataSource instance.\n */\nexport function createAccountActivityDataSource(\n options: AccountActivityDataSourceOptions,\n): AccountActivityDataSource {\n return new AccountActivityDataSource(options);\n}\n"]}
1
+ {"version":3,"file":"AccountActivityDataSource.js","sourceRoot":"","sources":["../../src/data-sources/AccountActivityDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountActivityServiceBalanceUpdatedEvent,\n AccountActivityServiceStatusChangedEvent,\n BalanceUpdate,\n} from '@metamask/core-backend';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { isCaipChainId } from '@metamask/utils';\nimport { BigNumber as BigNumberJS } from 'bignumber.js';\n\nimport type { AssetsControllerMessenger } from '../AssetsController.js';\nimport { projectLogger, createModuleLogger } from '../logger.js';\nimport type {\n AssetBalance,\n AssetMetadata,\n ChainId,\n Caip19AssetId,\n DataRequest,\n DataResponse,\n} from '../types.js';\nimport { safeNormalizeAssetId } from '../utils/index.js';\nimport { AbstractDataSource } from './AbstractDataSource.js';\nimport type { DataSourceState } from './AbstractDataSource.js';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'AccountActivityDataSource';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// BALANCE UPDATE PROCESSING\n// ============================================================================\n\n/**\n * Convert AccountActivityMessage balance updates into a {@link DataResponse}\n * for AssetsController.\n *\n * @param updates - Balance updates from account-activity websocket payload.\n * @param accountId - Internal account UUID.\n * @param getAssetType - Resolver for asset metadata type.\n * @returns DataResponse with merge mode when balances are present.\n */\nfunction processAccountActivityBalanceUpdates(\n updates: BalanceUpdate[],\n accountId: string,\n getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl',\n): DataResponse {\n const assetsBalance = Object.create(null) as Record<\n string,\n Record<Caip19AssetId, AssetBalance>\n >;\n assetsBalance[accountId] = Object.create(null) as Record<\n Caip19AssetId,\n AssetBalance\n >;\n const assetsMetadata = Object.create(null) as Record<\n Caip19AssetId,\n AssetMetadata\n >;\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n if (!asset || !postBalance) {\n continue;\n }\n\n // The websocket sends lower-case ERC-20 addresses, while state and the\n // rest of the pipeline key assets by their checksummed ID. Normalizing\n // here keeps middleware comparisons (detection, custom-asset graduation,\n // occurrence filtering) from treating an existing holding as brand new.\n const assetId = safeNormalizeAssetId(asset.type as Caip19AssetId);\n\n if (asset.decimals === undefined) {\n continue;\n }\n\n const rawBalanceStr = postBalance.amount.startsWith('0x')\n ? BigInt(postBalance.amount).toString()\n : postBalance.amount;\n\n const humanReadableAmount = new BigNumberJS(rawBalanceStr)\n .dividedBy(new BigNumberJS(10).pow(asset.decimals))\n .toFixed();\n\n assetsBalance[accountId][assetId] = {\n amount: humanReadableAmount,\n ...(postBalance.metadata ? { metadata: postBalance.metadata } : {}),\n };\n\n assetsMetadata[assetId] = {\n type: getAssetType(assetId),\n symbol: asset.unit,\n name: asset.unit,\n decimals: asset.decimals,\n };\n }\n\n const response: DataResponse = { updateMode: 'merge' };\n if (Object.keys(assetsBalance[accountId]).length > 0) {\n response.assetsBalance = assetsBalance;\n response.assetsInfo = assetsMetadata;\n }\n\n return response;\n}\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/** Allowed events that AccountActivityDataSource subscribes to. */\nexport type AccountActivityDataSourceAllowedEvents =\n | AccountActivityServiceBalanceUpdatedEvent\n | AccountActivityServiceStatusChangedEvent;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type AccountActivityDataSourceState = DataSourceState;\n\nconst defaultState: AccountActivityDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type AccountActivityDataSourceOptions = {\n /** The AssetsController messenger (shared by all data sources). */\n messenger: AssetsControllerMessenger;\n /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n /** Returns the asset type ('native' | 'erc20' | 'spl') for a given CAIP-19 asset ID. */\n getAssetType: (assetId: Caip19AssetId) => 'native' | 'erc20' | 'spl';\n /**\n * Pushes decoded balance updates to the controller (bound to\n * `AssetsController.handleAssetsUpdate`). AADS is event-driven and never\n * takes part in the controller's subscribe handoff, so it receives this\n * callback directly instead of via a `SubscriptionRequest`.\n */\n onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n state?: Partial<AccountActivityDataSourceState>;\n};\n\n// ============================================================================\n// ACCOUNT ACTIVITY DATA SOURCE\n// ============================================================================\n\n/**\n * Data source that consumes real-time updates from `AccountActivityService`.\n *\n * `AccountActivityService` owns the WebSocket connection and channel\n * subscriptions; this data source is a thin consumer of the two high-level\n * events that it publishes:\n *\n * - `AccountActivityService:balanceUpdated` — post-transaction balances for the\n * subscribed account(s). The address is resolved against the wallet's\n * selected account group, transformed into a {@link DataResponse} (via\n * {@link processAccountActivityBalanceUpdates}), and pushed to the controller\n * through the injected `onAssetsUpdate` callback.\n * - `AccountActivityService:statusChanged` — per-chain \"up\"/\"down\" notifications.\n * A chain reported \"up\" is claimed as an active chain (WebSocket is providing\n * real-time data); a chain reported \"down\" is released so polling data sources\n * can take over. Each change is applied directly via `updateActiveChains`,\n * which notifies the controller through `onActiveChainsUpdated`.\n *\n * This data source does NOT debounce, jitter, or gate chain updates itself.\n * Coalescing (collapsing bursts) and jitter (staggering the WS-subscribe herd\n * across clients) live in `AssetsController`, where the expensive re-subscribe\n * happens and where updates from all data sources converge. `activeChains` are\n * never seeded from the accounts API; they only ever reflect live\n * `statusChanged` events (the service flushes all tracked chains as \"down\" when\n * the WebSocket disconnects, releasing them).\n *\n * It subscribes to these events in its constructor (the same way\n * `TokenBalancesController` does). Unlike the polling data sources it does not\n * take part in the controller's subscribe/unsubscribe handoff: `subscribe` is a\n * no-op, balance updates are routed by resolving the address against the\n * wallet, and updates are pushed through the injected `onAssetsUpdate` callback.\n */\nexport class AccountActivityDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n AccountActivityDataSourceState\n> {\n readonly #messenger: AssetsControllerMessenger;\n\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n readonly #getAssetType: (\n assetId: Caip19AssetId,\n ) => 'native' | 'erc20' | 'spl';\n\n readonly #onAssetsUpdate: (\n response: DataResponse,\n request?: DataRequest,\n ) => void | Promise<void>;\n\n readonly #onBalanceUpdatedBound: (event: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }) => void;\n\n constructor(options: AccountActivityDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n this.#getAssetType = options.getAssetType;\n this.#onAssetsUpdate = options.onAssetsUpdate;\n this.#onBalanceUpdatedBound = this.#onBalanceUpdated.bind(this);\n\n this.#subscribeToEvents();\n }\n\n // ============================================================================\n // EVENT SUBSCRIPTIONS\n // ============================================================================\n\n #subscribeToEvents(): void {\n this.#messenger.subscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onBalanceUpdatedBound,\n );\n this.#messenger.subscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged,\n );\n }\n\n // ============================================================================\n // SUBSCRIBE / UNSUBSCRIBE\n // ============================================================================\n\n /**\n * AADS is event-driven and chain-agnostic: it never participates in the\n * controller's subscribe/unsubscribe handoff. Incoming `balanceUpdated`\n * events are routed by resolving the address against the wallet's accounts\n * (see `#onBalanceUpdated`), and updates are pushed through the injected\n * `#onAssetsUpdate` callback. This override exists only to satisfy the\n * abstract contract and is intentionally a no-op.\n */\n async subscribe(): Promise<void> {\n // Intentionally empty — see method doc.\n }\n\n // ============================================================================\n // BALANCE UPDATES\n // ============================================================================\n\n #onBalanceUpdated({\n address,\n chain,\n updates,\n }: {\n address: string;\n chain: string;\n updates: BalanceUpdate[];\n }): void {\n try {\n if (!address || !chain || !updates || updates.length === 0) {\n return;\n }\n\n const account = this.#findAccountForAddress(address);\n if (!account) {\n return;\n }\n\n const chainId = chain as ChainId;\n\n const response = processAccountActivityBalanceUpdates(\n updates,\n account.id,\n (assetId) => this.#getAssetType(assetId),\n );\n\n if (!response.assetsBalance) {\n return;\n }\n\n const request: DataRequest = {\n accountsWithSupportedChains: [{ account, supportedChains: [chainId] }],\n chainIds: [chainId],\n dataTypes: ['balance', 'metadata'],\n };\n\n Promise.resolve(this.#onAssetsUpdate(response, request)).catch(\n (error) => {\n log('Failed to report balance update', { error });\n },\n );\n } catch (error) {\n log('Error handling balance update', error);\n }\n }\n\n /**\n * Find the wallet account matching the given activity address. EVM addresses\n * are matched case-insensitively; other namespaces are matched exactly.\n *\n * The candidate set is the accounts of the selected account group, resolved\n * from the wallet at event time (rather than from a stored subscription), so\n * AADS does not need to be re-subscribed whenever the account set changes.\n *\n * @param address - The account address from the activity message.\n * @returns The matching account, or null.\n */\n #findAccountForAddress(address: string): InternalAccount | null {\n const isEvm = address.startsWith('0x');\n\n return (\n this.#getWalletAccounts().find((candidate) =>\n isEvm\n ? candidate.address.toLowerCase() === address.toLowerCase()\n : candidate.address === address,\n ) ?? null\n );\n }\n\n /**\n * Resolve the accounts of the selected account group from the wallet,\n * mirroring `AssetsController.#getSelectedAccounts`.\n *\n * @returns The accounts of the selected account group.\n */\n #getWalletAccounts(): InternalAccount[] {\n const accounts = this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n if (accounts.length > 0) {\n return accounts;\n }\n\n const selectedAccount = this.#messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n return selectedAccount ? [selectedAccount] : [];\n }\n\n // ============================================================================\n // STATUS CHANGES\n // ============================================================================\n\n /**\n * Handle a `statusChanged` notification. Chains reported \"up\" are claimed as\n * active (WebSocket provides real-time data); chains reported \"down\" are\n * released so polling data sources take over. The change is applied directly;\n * coalescing and jitter are handled by `AssetsController`.\n *\n * @param options - The status change notification.\n * @param options.chainIds - The CAIP-2 chain IDs whose status changed.\n * @param options.status - Whether the chains are `up` or `down`.\n * @param options.timestamp - Optional timestamp of the status change.\n */\n readonly #onAccountActivityStatusChanged = ({\n chainIds,\n status,\n }: {\n chainIds: string[];\n status: 'up' | 'down';\n timestamp?: number;\n }): void => {\n try {\n // Act on every namespace (eip155, solana, etc.); AssetsController is\n // multichain. Only skip identifiers that are not valid CAIP-2 chain IDs.\n const validChains = chainIds.filter((chainId) => isCaipChainId(chainId));\n\n if (validChains.length === 0) {\n return;\n }\n\n const next = new Set(this.state.activeChains);\n if (status === 'up') {\n for (const chainId of validChains) {\n next.add(chainId);\n }\n } else {\n for (const chainId of validChains) {\n next.delete(chainId);\n }\n }\n\n const previous = [...this.state.activeChains];\n this.updateActiveChains(Array.from(next), (updatedChains) =>\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous),\n );\n } catch (error) {\n log('Error handling status change', error);\n }\n };\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n try {\n this.#messenger.unsubscribe(\n 'AccountActivityService:balanceUpdated',\n this.#onBalanceUpdatedBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from balanceUpdated', { error });\n }\n\n try {\n this.#messenger.unsubscribe(\n 'AccountActivityService:statusChanged',\n this.#onAccountActivityStatusChanged,\n );\n } catch (error) {\n log('Failed to unsubscribe from statusChanged', { error });\n }\n\n super.destroy();\n }\n}\n\n// ============================================================================\n// FACTORY FUNCTION\n// ============================================================================\n\n/**\n * Creates an AccountActivityDataSource instance.\n *\n * @param options - Configuration options for the data source.\n * @returns A new AccountActivityDataSource instance.\n */\nexport function createAccountActivityDataSource(\n options: AccountActivityDataSourceOptions,\n): AccountActivityDataSource {\n return new AccountActivityDataSource(options);\n}\n"]}
@@ -1,7 +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, Caip19AssetId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from '../types.js';
4
+ import type { ChainId, DataRequest, DataResponse, Middleware, AssetsControllerStateInternal } from '../types.js';
5
5
  import type { DataSourceState, SubscriptionRequest } from './AbstractDataSource.js';
6
6
  import { AbstractDataSource } from './AbstractDataSource.js';
7
7
  declare const CONTROLLER_NAME = "AccountsApiDataSource";
@@ -28,19 +28,13 @@ export type AccountsApiDataSourceConfig = {
28
28
  export type AccountsApiDataSourceOptions = AccountsApiDataSourceConfig & {
29
29
  /**
30
30
  * The AssetsController messenger (shared by all data sources). Used to read
31
- * Snaps → AssetsController migration flags and subscribe to flag changes.
31
+ * the `assetsAccountsApiV6` remote feature flag.
32
32
  */
33
33
  messenger: AssetsControllerMessenger;
34
34
  /** ApiPlatformClient for API calls with caching */
35
35
  queryApiClient: ApiPlatformClient;
36
36
  /** Called when active chains are updated. Pass dataSourceName so the controller knows the source. */
37
37
  onActiveChainsUpdated: (dataSourceName: string, chains: ChainId[], previousChains: ChainId[]) => void;
38
- /**
39
- * Whether to use the Accounts API v6 balances endpoint (and `includeAssetIds`).
40
- * Injected by AssetsController so the flag is read in one place. Defaults to
41
- * `false` (v5). Read on demand, not cached.
42
- */
43
- isBalanceV6Enabled?: () => boolean;
44
38
  state?: Partial<AccountsApiDataSourceState>;
45
39
  };
46
40
  /**
@@ -81,16 +75,6 @@ export declare class AccountsApiDataSource extends AbstractDataSource<typeof CON
81
75
  * @returns The middleware function for the assets pipeline.
82
76
  */
83
77
  get assetsMiddleware(): Middleware;
84
- /**
85
- * Claim EVM pins on assigned chains (sent to v6 as `includeAssetIds`).
86
- * v5 has no `includeAssetIds`, so with the v6 flag off nothing is claimed
87
- * and pins fall through to RPC.
88
- *
89
- * @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.
90
- * @param assignedChains - Chains assigned to this source in the handoff.
91
- * @returns The claimed subset of `customAssets`.
92
- */
93
- claimCustomAssets(customAssets: Caip19AssetId[], assignedChains: ChainId[]): Caip19AssetId[];
94
78
  subscribe(subscriptionRequest: SubscriptionRequest): Promise<void>;
95
79
  destroy(): void;
96
80
  }
@@ -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;AAQlD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,KAAK,EACV,OAAO,EACP,aAAa,EAEb,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;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAgCF;;;;;;;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;;IA6BC,YAAY,OAAO,EAAE,4BAA4B,EAiChD;IAiED;;;;;;OAMG;IACH,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnC;IA0BK,KAAK,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAgJvD;IA0UD;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAiGjC;IAMD;;;;;;;;OAQG;IACH,iBAAiB,CACf,YAAY,EAAE,aAAa,EAAE,EAC7B,cAAc,EAAE,OAAO,EAAE,GACxB,aAAa,EAAE,CAgBjB;IAEK,SAAS,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoGvE;IAMD,OAAO,IAAI,IAAI,CAMd;CACF"}
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;AAgCF;;;;;;;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,4 +1,4 @@
1
- import { isCaipChainId, KnownCaipNamespace, parseCaipAssetType, toCaipChainId, } from '@metamask/utils';
1
+ import { isCaipChainId, KnownCaipNamespace, toCaipChainId, } from '@metamask/utils';
2
2
  import { projectLogger, createModuleLogger } from '../logger.js';
3
3
  import { fetchWithTimeout, normalizeAssetId } from '../utils/index.js';
4
4
  import { getMigrationStages, shouldSupportChain, } from '../utils/snaps-assets-migration.js';
@@ -93,8 +93,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
93
93
  #tokenDetectionEnabled;
94
94
  /** Shared AssetsController messenger, used to read remote feature flags. */
95
95
  #messenger;
96
- /** Injected by AssetsController; `true` when the v6 balances endpoint should be used. */
97
- #isBalanceV6Enabled;
98
96
  /** ApiPlatformClient for cached API calls */
99
97
  #apiClient;
100
98
  /** Chains refresh timer */
@@ -112,8 +110,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
112
110
  this.#tokenDetectionEnabled =
113
111
  options.tokenDetectionEnabled ?? (() => true);
114
112
  this.#messenger = options.messenger;
115
- this.#isBalanceV6Enabled =
116
- options.isBalanceV6Enabled ?? (() => false);
117
113
  this.#apiClient = options.queryApiClient;
118
114
  // The Snaps → AssetsController migration flags gate which migration networks
119
115
  // (Solana, Stellar, Tron) are surfaced as active chains (see
@@ -146,6 +142,31 @@ export class AccountsApiDataSource extends AbstractDataSource {
146
142
  });
147
143
  }
148
144
  }
145
+ /**
146
+ * Whether the Accounts API v6 balances endpoint is enabled, read from the
147
+ * RemoteFeatureFlagController (`assetsAccountsApiV6`). Read on demand (per
148
+ * fetch) rather than cached so the value can be toggled at runtime, and behaves
149
+ * the same across clients (extension, mobile). Defaults to `false` when the
150
+ * flag is unset or the controller is unavailable.
151
+ *
152
+ * The flag is a LaunchDarkly JSON variation shaped `{ value: boolean }` (same
153
+ * shape as `backendWebSocketConnection`), so the nested `value` is read rather
154
+ * than treating the flag as a plain boolean.
155
+ *
156
+ * @returns `true` when the v6 balances endpoint should be used.
157
+ */
158
+ #isBalanceV6Enabled() {
159
+ try {
160
+ const { remoteFeatureFlags } = this.#messenger.call('RemoteFeatureFlagController:getState');
161
+ const flag = remoteFeatureFlags?.assetsAccountsApiV6;
162
+ return (typeof flag === 'object' &&
163
+ flag !== null &&
164
+ Boolean(flag.value));
165
+ }
166
+ catch {
167
+ return false;
168
+ }
169
+ }
149
170
  // ============================================================================
150
171
  // INITIALIZATION
151
172
  // ============================================================================
@@ -243,39 +264,11 @@ export class AccountsApiDataSource extends AbstractDataSource {
243
264
  ...(request.bypassServerCache ? { bypassServerCache: true } : {}),
244
265
  }
245
266
  : undefined;
246
- const isV6 = this.#isBalanceV6Enabled();
247
- let fetchResult;
248
- if (isV6) {
249
- // User-hidden assets on the fetched chains, sent to v6 as
250
- // `excludeAssetIds`.
251
- const excludeAssetIds = this.#getExcludeAssetIds(request, chainsToFetch);
252
- // User-pinned assets on the fetched chains, sent to v6 as
253
- // `includeAssetIds` so the backend returns them even at zero balance.
254
- // A hide wins over a pin, so hidden assets are never fetched.
255
- const includeAssetIds = this.#getIncludeAssetIds(request, chainsToFetch, excludeAssetIds);
256
- fetchResult = await this.#fetchV6Balances(accountIds, fetchOptions, request, includeAssetIds, excludeAssetIds);
257
- const validUnprocessedAssetIds = fetchResult.unprocessedIncludeAssetIds.filter((assetId) => {
258
- try {
259
- parseCaipAssetType(assetId);
260
- return true;
261
- }
262
- catch {
263
- return false;
264
- }
265
- });
266
- if (validUnprocessedAssetIds.length > 0) {
267
- response.unprocessedCustomAssets = [
268
- ...(response.unprocessedCustomAssets ?? []),
269
- ...validUnprocessedAssetIds,
270
- ];
271
- }
272
- response.updateMode = 'full';
273
- }
274
- else {
275
- fetchResult = await this.#fetchV5Balances(accountIds, fetchOptions, request);
276
- response.updateMode = 'merge';
277
- }
278
- const { unprocessedNetworks, assetsBalance } = fetchResult;
267
+ // Feature-flagged: v6 endpoint with a fallback to legacy v5. The flag is
268
+ // read here (not cached) so a runtime toggle can revert v6 -> v5.
269
+ const { unprocessedNetworks, assetsBalance } = this.#isBalanceV6Enabled()
270
+ ? await this.#fetchV6Balances(accountIds, fetchOptions, request)
271
+ : await this.#fetchV5Balances(accountIds, fetchOptions, request);
279
272
  // Handle unprocessed networks - these will be passed to next middleware
280
273
  if (unprocessedNetworks.length > 0) {
281
274
  const unprocessedChainIds = unprocessedNetworks.map(caipChainIdToChainId);
@@ -286,6 +279,7 @@ export class AccountsApiDataSource extends AbstractDataSource {
286
279
  }
287
280
  }
288
281
  response.assetsBalance = assetsBalance;
282
+ response.updateMode = 'merge';
289
283
  }
290
284
  catch (error) {
291
285
  log('Fetch FAILED', { error, chains: chainsToFetch });
@@ -309,70 +303,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
309
303
  }
310
304
  return response;
311
305
  }
312
- /**
313
- * Collect the pinned EVM assets on the fetched chains to send to the v6
314
- * endpoint as `includeAssetIds`; malformed IDs are skipped and hidden
315
- * assets are left out (a hide wins).
316
- *
317
- * @param request - The data request (carries `customAssets`).
318
- * @param chainsToFetch - Chains being requested this fetch.
319
- * @param excludeAssetIds - Hidden asset IDs that must not be included.
320
- * @returns Deduplicated asset IDs, or `undefined` when none.
321
- */
322
- #getIncludeAssetIds(request, chainsToFetch, excludeAssetIds) {
323
- if (!request.customAssets || request.customAssets.length === 0) {
324
- return undefined;
325
- }
326
- const chainsToFetchSet = new Set(chainsToFetch);
327
- const excludeSet = new Set(excludeAssetIds ?? []);
328
- const includeAssetIds = new Set();
329
- for (const assetId of request.customAssets) {
330
- if (excludeSet.has(assetId)) {
331
- continue;
332
- }
333
- let chainId;
334
- try {
335
- chainId = parseCaipAssetType(assetId).chainId;
336
- }
337
- catch {
338
- continue;
339
- }
340
- if (chainId.startsWith(`${KnownCaipNamespace.Eip155}:`) &&
341
- chainsToFetchSet.has(chainId)) {
342
- includeAssetIds.add(assetId);
343
- }
344
- }
345
- return includeAssetIds.size > 0 ? [...includeAssetIds] : undefined;
346
- }
347
- /**
348
- * Collect the hidden EVM assets on the fetched chains to send to the v6
349
- * endpoint as `excludeAssetIds`; malformed IDs are skipped.
350
- *
351
- * @param request - The data request (carries `excludeAssetIds`).
352
- * @param chainsToFetch - Chains being requested this fetch.
353
- * @returns Deduplicated asset IDs, or `undefined` when none.
354
- */
355
- #getExcludeAssetIds(request, chainsToFetch) {
356
- if (!request.excludeAssetIds || request.excludeAssetIds.length === 0) {
357
- return undefined;
358
- }
359
- const chainsToFetchSet = new Set(chainsToFetch);
360
- const excludeAssetIds = new Set();
361
- for (const assetId of request.excludeAssetIds) {
362
- let chainId;
363
- try {
364
- chainId = parseCaipAssetType(assetId).chainId;
365
- }
366
- catch {
367
- continue;
368
- }
369
- if (chainId.startsWith(`${KnownCaipNamespace.Eip155}:`) &&
370
- chainsToFetchSet.has(chainId)) {
371
- excludeAssetIds.add(assetId);
372
- }
373
- }
374
- return excludeAssetIds.size > 0 ? [...excludeAssetIds] : undefined;
375
- }
376
306
  /**
377
307
  * Fetch balances from the legacy v5 endpoint and process them.
378
308
  *
@@ -386,8 +316,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
386
316
  const { assetsBalance } = this.#processV5Balances(apiResponse.balances, request);
387
317
  return {
388
318
  unprocessedNetworks: apiResponse.unprocessedNetworks,
389
- // v5 has no `includeAssetIds` support.
390
- unprocessedIncludeAssetIds: [],
391
319
  assetsBalance,
392
320
  };
393
321
  }
@@ -397,23 +325,13 @@ export class AccountsApiDataSource extends AbstractDataSource {
397
325
  * @param accountIds - CAIP-10 account IDs to fetch balances for.
398
326
  * @param fetchOptions - Cache/fetch options (e.g. force update settings).
399
327
  * @param request - The original data request containing accounts to map.
400
- * @param includeAssetIds - Pinned asset IDs the backend must always return.
401
- * @param excludeAssetIds - Hidden asset IDs the backend must drop.
402
- * @returns Unprocessed networks, unprocessed pinned assets, and processed
403
- * asset balances by account.
328
+ * @returns Unprocessed networks and processed asset balances by account.
404
329
  */
405
- async #fetchV6Balances(accountIds, fetchOptions, request, includeAssetIds, excludeAssetIds) {
406
- const params = includeAssetIds || excludeAssetIds
407
- ? {
408
- ...(includeAssetIds && { includeAssetIds }),
409
- ...(excludeAssetIds && { excludeAssetIds }),
410
- }
411
- : undefined;
412
- const apiResponse = await fetchWithTimeout(() => this.#apiClient.accounts.fetchV6MultiAccountBalances(accountIds, params, fetchOptions), this.#fetchTimeoutMs);
330
+ async #fetchV6Balances(accountIds, fetchOptions, request) {
331
+ const apiResponse = await fetchWithTimeout(() => this.#apiClient.accounts.fetchV6MultiAccountBalances(accountIds, undefined, fetchOptions), this.#fetchTimeoutMs);
413
332
  const { assetsBalance } = this.#processV6Balances(apiResponse.balances, request);
414
333
  return {
415
334
  unprocessedNetworks: apiResponse.unprocessedNetworks,
416
- unprocessedIncludeAssetIds: apiResponse.unprocessedIncludeAssetIds,
417
335
  assetsBalance,
418
336
  };
419
337
  }
@@ -469,9 +387,11 @@ export class AccountsApiDataSource extends AbstractDataSource {
469
387
  if (isStakingContractAssetId(normalizedAssetId)) {
470
388
  continue;
471
389
  }
472
- // Store balance as returned by API
390
+ // Store balance as returned by API, along with any network-specific
391
+ // metadata (e.g. Stellar trustline / native reserve fields).
473
392
  assetsBalance[accountId][normalizedAssetId] = {
474
393
  amount: item.balance,
394
+ ...(item.metadata ? { metadata: item.metadata } : {}),
475
395
  };
476
396
  }
477
397
  return { assetsBalance };
@@ -518,9 +438,11 @@ export class AccountsApiDataSource extends AbstractDataSource {
518
438
  if (isStakingContractAssetId(normalizedAssetId)) {
519
439
  continue;
520
440
  }
521
- // Store balance as returned by API
441
+ // Store balance as returned by API, along with any network-specific
442
+ // metadata (e.g. Stellar trustline / native reserve fields).
522
443
  assetsBalance[accountId][normalizedAssetId] = {
523
444
  amount: item.balance,
445
+ ...(item.metadata ? { metadata: item.metadata } : {}),
524
446
  };
525
447
  }
526
448
  return { assetsBalance };
@@ -562,27 +484,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
562
484
  };
563
485
  }
564
486
  }
565
- if (response.updateMode === 'full') {
566
- context.response = {
567
- ...context.response,
568
- updateMode: 'full',
569
- };
570
- }
571
- else if (response.updateMode === 'merge' &&
572
- context.response.updateMode !== 'full') {
573
- context.response = {
574
- ...context.response,
575
- updateMode: 'merge',
576
- };
577
- }
578
- // Forward the asset-axis signal so the RPC fallback recovers these pins.
579
- if (response.unprocessedCustomAssets &&
580
- response.unprocessedCustomAssets.length > 0) {
581
- context.response.unprocessedCustomAssets = [
582
- ...(context.response.unprocessedCustomAssets ?? []),
583
- ...response.unprocessedCustomAssets,
584
- ];
585
- }
586
487
  // Determine successfully handled chains (exclude unprocessed/error chains)
587
488
  const unprocessedChains = new Set(Object.keys(response.errors ?? {}));
588
489
  successfullyHandledChains = request.chainIds.filter((chainId) => !unprocessedChains.has(chainId));
@@ -617,31 +518,6 @@ export class AccountsApiDataSource extends AbstractDataSource {
617
518
  // ============================================================================
618
519
  // SUBSCRIBE
619
520
  // ============================================================================
620
- /**
621
- * Claim EVM pins on assigned chains (sent to v6 as `includeAssetIds`).
622
- * v5 has no `includeAssetIds`, so with the v6 flag off nothing is claimed
623
- * and pins fall through to RPC.
624
- *
625
- * @param customAssets - Candidate CAIP-19 asset IDs still unclaimed.
626
- * @param assignedChains - Chains assigned to this source in the handoff.
627
- * @returns The claimed subset of `customAssets`.
628
- */
629
- claimCustomAssets(customAssets, assignedChains) {
630
- if (!this.#isBalanceV6Enabled()) {
631
- return [];
632
- }
633
- const assigned = new Set(assignedChains);
634
- return customAssets.filter((assetId) => {
635
- try {
636
- const parsed = parseCaipAssetType(assetId);
637
- return (parsed.chain.namespace === KnownCaipNamespace.Eip155 &&
638
- assigned.has(parsed.chainId));
639
- }
640
- catch {
641
- return false;
642
- }
643
- });
644
- }
645
521
  async subscribe(subscriptionRequest) {
646
522
  const { request, subscriptionId, isUpdate, skipInitialFetch } = subscriptionRequest;
647
523
  // Store state accessor for filtering when tokenDetectionEnabled is false