@metamask-previews/assets-controllers 110.0.1-preview-a1d0bbfa8 → 110.0.2-preview-f48a87ac7

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 (34) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/AccountTrackerController.d.cts +2 -2
  3. package/dist/AccountTrackerController.d.mts +2 -2
  4. package/dist/CurrencyRateController.d.cts +2 -2
  5. package/dist/CurrencyRateController.d.mts +2 -2
  6. package/dist/DeFiPositionsController/DeFiPositionsController.d.cts +2 -2
  7. package/dist/DeFiPositionsController/DeFiPositionsController.d.mts +2 -2
  8. package/dist/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.cjs.map +1 -1
  9. package/dist/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.d.cts +13 -8
  10. package/dist/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.d.cts.map +1 -1
  11. package/dist/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.d.mts +13 -8
  12. package/dist/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.d.mts.map +1 -1
  13. package/dist/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.mjs.map +1 -1
  14. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.cjs +103 -36
  15. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.cjs.map +1 -1
  16. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.d.cts +28 -8
  17. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.d.cts.map +1 -1
  18. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.d.mts +28 -8
  19. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.d.mts.map +1 -1
  20. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.mjs +101 -34
  21. package/dist/DeFiPositionsController/DeFiPositionsControllerV2.mjs.map +1 -1
  22. package/dist/MultichainAssetsController/MultichainAssetsController.d.cts +2 -2
  23. package/dist/MultichainAssetsController/MultichainAssetsController.d.mts +2 -2
  24. package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.cts +2 -2
  25. package/dist/MultichainAssetsRatesController/MultichainAssetsRatesController.d.mts +2 -2
  26. package/dist/TokenBalancesController.d.cts +2 -2
  27. package/dist/TokenBalancesController.d.mts +2 -2
  28. package/dist/TokenDetectionController.d.cts +2 -2
  29. package/dist/TokenDetectionController.d.mts +2 -2
  30. package/dist/TokenListController.d.cts +2 -2
  31. package/dist/TokenListController.d.mts +2 -2
  32. package/dist/TokenRatesController.d.cts +2 -2
  33. package/dist/TokenRatesController.d.mts +2 -2
  34. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+
12
+ - `DeFiPositionsControllerV2.fetchDeFiPositions` now polls while any selected
13
+ account has `processingDefiPositions: true`, applying ready accounts
14
+ immediately, invalidating the balances cache between attempts, sharing one
15
+ in-flight promise per selected-account key (so fast switches can join an
16
+ earlier poll), and stopping on request failure or the max attempt limit
17
+
18
+ ## [110.0.2]
19
+
20
+ ### Changed
21
+
22
+ - Bump `@metamask/network-enablement-controller` from `^5.6.0` to `^6.0.0` ([#9706](https://github.com/MetaMask/core/pull/9706))
23
+
10
24
  ## [110.0.1]
11
25
 
12
26
  ### Changed
@@ -3347,7 +3361,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3347
3361
 
3348
3362
  - Use Ethers for AssetsContractController ([#845](https://github.com/MetaMask/core/pull/845))
3349
3363
 
3350
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@110.0.1...HEAD
3364
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@110.0.2...HEAD
3365
+ [110.0.2]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@110.0.1...@metamask/assets-controllers@110.0.2
3351
3366
  [110.0.1]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@110.0.0...@metamask/assets-controllers@110.0.1
3352
3367
  [110.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@109.4.1...@metamask/assets-controllers@110.0.0
3353
3368
  [109.4.1]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@109.4.0...@metamask/assets-controllers@109.4.1
@@ -78,8 +78,8 @@ type AccountTrackerPollingInput = {
78
78
  queryAllAccounts?: boolean;
79
79
  };
80
80
  declare const AccountTrackerController_base: (abstract new (...args: any[]) => {
81
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
82
- "__#17@#intervalLength": number | undefined;
81
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
82
+ "__#20@#intervalLength": number | undefined;
83
83
  setIntervalLength(intervalLength: number): void;
84
84
  getIntervalLength(): number | undefined;
85
85
  _startPolling(input: AccountTrackerPollingInput): void;
@@ -78,8 +78,8 @@ type AccountTrackerPollingInput = {
78
78
  queryAllAccounts?: boolean;
79
79
  };
80
80
  declare const AccountTrackerController_base: (abstract new (...args: any[]) => {
81
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
82
- "__#17@#intervalLength": number | undefined;
81
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
82
+ "__#20@#intervalLength": number | undefined;
83
83
  setIntervalLength(intervalLength: number): void;
84
84
  getIntervalLength(): number | undefined;
85
85
  _startPolling(input: AccountTrackerPollingInput): void;
@@ -34,8 +34,8 @@ type CurrencyRatePollingInput = {
34
34
  nativeCurrencies: string[];
35
35
  };
36
36
  declare const CurrencyRateController_base: (abstract new (...args: any[]) => {
37
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
38
- "__#17@#intervalLength": number | undefined;
37
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
38
+ "__#20@#intervalLength": number | undefined;
39
39
  setIntervalLength(intervalLength: number): void;
40
40
  getIntervalLength(): number | undefined;
41
41
  _startPolling(input: CurrencyRatePollingInput): void;
@@ -34,8 +34,8 @@ type CurrencyRatePollingInput = {
34
34
  nativeCurrencies: string[];
35
35
  };
36
36
  declare const CurrencyRateController_base: (abstract new (...args: any[]) => {
37
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
38
- "__#17@#intervalLength": number | undefined;
37
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
38
+ "__#20@#intervalLength": number | undefined;
39
39
  setIntervalLength(intervalLength: number): void;
40
40
  getIntervalLength(): number | undefined;
41
41
  _startPolling(input: CurrencyRatePollingInput): void;
@@ -56,8 +56,8 @@ export type AllowedEvents = KeyringControllerLockEvent | TransactionControllerTr
56
56
  */
57
57
  export type DeFiPositionsControllerMessenger = Messenger<typeof controllerName, DeFiPositionsControllerActions | AllowedActions, DeFiPositionsControllerEvents | AllowedEvents>;
58
58
  declare const DeFiPositionsController_base: (abstract new (...args: any[]) => {
59
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
60
- "__#17@#intervalLength": number | undefined;
59
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
60
+ "__#20@#intervalLength": number | undefined;
61
61
  setIntervalLength(intervalLength: number): void;
62
62
  getIntervalLength(): number | undefined;
63
63
  _startPolling(input: import("@metamask/utils").Json): void;
@@ -56,8 +56,8 @@ export type AllowedEvents = KeyringControllerLockEvent | TransactionControllerTr
56
56
  */
57
57
  export type DeFiPositionsControllerMessenger = Messenger<typeof controllerName, DeFiPositionsControllerActions | AllowedActions, DeFiPositionsControllerEvents | AllowedEvents>;
58
58
  declare const DeFiPositionsController_base: (abstract new (...args: any[]) => {
59
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
60
- "__#17@#intervalLength": number | undefined;
59
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
60
+ "__#20@#intervalLength": number | undefined;
61
61
  setIntervalLength(intervalLength: number): void;
62
62
  getIntervalLength(): number | undefined;
63
63
  _startPolling(input: import("@metamask/utils").Json): void;
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { DeFiPositionsControllerV2 } from './DeFiPositionsControllerV2.js';\n\n/**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) are skipped so prior\n * state is kept for them. No-ops when disabled or when the group has no\n * supported accounts. Caching / spam prevention is handled by the apiClient\n * TanStack Query cache (keyed by accounts + query options including\n * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.\n * pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache and\n * fetch immediately.\n */\nexport type DeFiPositionsControllerV2FetchDeFiPositionsAction = {\n type: `DeFiPositionsControllerV2:fetchDeFiPositions`;\n handler: DeFiPositionsControllerV2['fetchDeFiPositions'];\n};\n\n/**\n * Union of all DeFiPositionsControllerV2 action types.\n */\nexport type DeFiPositionsControllerV2MethodActions =\n DeFiPositionsControllerV2FetchDeFiPositionsAction;\n"]}
1
+ {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { DeFiPositionsControllerV2 } from './DeFiPositionsControllerV2.js';\n\n/**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) keep prior state; the\n * method polls (invalidating the balances cache between attempts) until all\n * selected accounts are ready, the attempt limit is reached, or a request\n * fails. Concurrent calls for the same selected accounts share one in-flight\n * promise; calls for a different selection start a new fetch and leave prior\n * polls running so a later switch back can join them. No-ops when disabled\n * or when the group has no supported accounts. Caching / spam prevention is\n * handled by the apiClient TanStack Query cache (keyed by accounts + query\n * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass\n * the cache on the first attempt (e.g. pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache on the\n * first attempt and fetch immediately.\n * @returns Resolves when the fetch (and any processing polls) finish.\n */\nexport type DeFiPositionsControllerV2FetchDeFiPositionsAction = {\n type: `DeFiPositionsControllerV2:fetchDeFiPositions`;\n handler: DeFiPositionsControllerV2['fetchDeFiPositions'];\n};\n\n/**\n * Union of all DeFiPositionsControllerV2 action types.\n */\nexport type DeFiPositionsControllerV2MethodActions =\n DeFiPositionsControllerV2FetchDeFiPositionsAction;\n"]}
@@ -6,16 +6,21 @@ import type { DeFiPositionsControllerV2 } from "./DeFiPositionsControllerV2.cjs"
6
6
  /**
7
7
  * Fetches DeFi positions for the selected account group. Each account key in
8
8
  * a ready response replaces that account's state (other accounts stay).
9
- * Accounts still indexing (`processingDefiPositions`) are skipped so prior
10
- * state is kept for them. No-ops when disabled or when the group has no
11
- * supported accounts. Caching / spam prevention is handled by the apiClient
12
- * TanStack Query cache (keyed by accounts + query options including
13
- * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.
14
- * pull-to-refresh).
9
+ * Accounts still indexing (`processingDefiPositions`) keep prior state; the
10
+ * method polls (invalidating the balances cache between attempts) until all
11
+ * selected accounts are ready, the attempt limit is reached, or a request
12
+ * fails. Concurrent calls for the same selected accounts share one in-flight
13
+ * promise; calls for a different selection start a new fetch and leave prior
14
+ * polls running so a later switch back can join them. No-ops when disabled
15
+ * or when the group has no supported accounts. Caching / spam prevention is
16
+ * handled by the apiClient TanStack Query cache (keyed by accounts + query
17
+ * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass
18
+ * the cache on the first attempt (e.g. pull-to-refresh).
15
19
  *
16
20
  * @param options - Optional fetch modifiers.
17
- * @param options.forceRefresh - When true, bypass the apiClient cache and
18
- * fetch immediately.
21
+ * @param options.forceRefresh - When true, bypass the apiClient cache on the
22
+ * first attempt and fetch immediately.
23
+ * @returns Resolves when the fetch (and any processing polls) finish.
19
24
  */
20
25
  export type DeFiPositionsControllerV2FetchDeFiPositionsAction = {
21
26
  type: `DeFiPositionsControllerV2:fetchDeFiPositions`;
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,wCAAuC;AAEhF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,yBAAyB,CAAC,oBAAoB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAChD,iDAAiD,CAAC"}
1
+ {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,wCAAuC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,yBAAyB,CAAC,oBAAoB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAChD,iDAAiD,CAAC"}
@@ -6,16 +6,21 @@ import type { DeFiPositionsControllerV2 } from "./DeFiPositionsControllerV2.mjs"
6
6
  /**
7
7
  * Fetches DeFi positions for the selected account group. Each account key in
8
8
  * a ready response replaces that account's state (other accounts stay).
9
- * Accounts still indexing (`processingDefiPositions`) are skipped so prior
10
- * state is kept for them. No-ops when disabled or when the group has no
11
- * supported accounts. Caching / spam prevention is handled by the apiClient
12
- * TanStack Query cache (keyed by accounts + query options including
13
- * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.
14
- * pull-to-refresh).
9
+ * Accounts still indexing (`processingDefiPositions`) keep prior state; the
10
+ * method polls (invalidating the balances cache between attempts) until all
11
+ * selected accounts are ready, the attempt limit is reached, or a request
12
+ * fails. Concurrent calls for the same selected accounts share one in-flight
13
+ * promise; calls for a different selection start a new fetch and leave prior
14
+ * polls running so a later switch back can join them. No-ops when disabled
15
+ * or when the group has no supported accounts. Caching / spam prevention is
16
+ * handled by the apiClient TanStack Query cache (keyed by accounts + query
17
+ * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass
18
+ * the cache on the first attempt (e.g. pull-to-refresh).
15
19
  *
16
20
  * @param options - Optional fetch modifiers.
17
- * @param options.forceRefresh - When true, bypass the apiClient cache and
18
- * fetch immediately.
21
+ * @param options.forceRefresh - When true, bypass the apiClient cache on the
22
+ * first attempt and fetch immediately.
23
+ * @returns Resolves when the fetch (and any processing polls) finish.
19
24
  */
20
25
  export type DeFiPositionsControllerV2FetchDeFiPositionsAction = {
21
26
  type: `DeFiPositionsControllerV2:fetchDeFiPositions`;
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,wCAAuC;AAEhF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,yBAAyB,CAAC,oBAAoB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAChD,iDAAiD,CAAC"}
1
+ {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,yBAAyB,EAAE,wCAAuC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,iDAAiD,GAAG;IAC9D,IAAI,EAAE,8CAA8C,CAAC;IACrD,OAAO,EAAE,yBAAyB,CAAC,oBAAoB,CAAC,CAAC;CAC1D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sCAAsC,GAChD,iDAAiD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { DeFiPositionsControllerV2 } from './DeFiPositionsControllerV2.js';\n\n/**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) are skipped so prior\n * state is kept for them. No-ops when disabled or when the group has no\n * supported accounts. Caching / spam prevention is handled by the apiClient\n * TanStack Query cache (keyed by accounts + query options including\n * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.\n * pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache and\n * fetch immediately.\n */\nexport type DeFiPositionsControllerV2FetchDeFiPositionsAction = {\n type: `DeFiPositionsControllerV2:fetchDeFiPositions`;\n handler: DeFiPositionsControllerV2['fetchDeFiPositions'];\n};\n\n/**\n * Union of all DeFiPositionsControllerV2 action types.\n */\nexport type DeFiPositionsControllerV2MethodActions =\n DeFiPositionsControllerV2FetchDeFiPositionsAction;\n"]}
1
+ {"version":3,"file":"DeFiPositionsControllerV2-method-action-types.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { DeFiPositionsControllerV2 } from './DeFiPositionsControllerV2.js';\n\n/**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) keep prior state; the\n * method polls (invalidating the balances cache between attempts) until all\n * selected accounts are ready, the attempt limit is reached, or a request\n * fails. Concurrent calls for the same selected accounts share one in-flight\n * promise; calls for a different selection start a new fetch and leave prior\n * polls running so a later switch back can join them. No-ops when disabled\n * or when the group has no supported accounts. Caching / spam prevention is\n * handled by the apiClient TanStack Query cache (keyed by accounts + query\n * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass\n * the cache on the first attempt (e.g. pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache on the\n * first attempt and fetch immediately.\n * @returns Resolves when the fetch (and any processing polls) finish.\n */\nexport type DeFiPositionsControllerV2FetchDeFiPositionsAction = {\n type: `DeFiPositionsControllerV2:fetchDeFiPositions`;\n handler: DeFiPositionsControllerV2['fetchDeFiPositions'];\n};\n\n/**\n * Union of all DeFiPositionsControllerV2 action types.\n */\nexport type DeFiPositionsControllerV2MethodActions =\n DeFiPositionsControllerV2FetchDeFiPositionsAction;\n"]}
@@ -10,14 +10,29 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _DeFiPositionsControllerV2_apiClient, _DeFiPositionsControllerV2_isEnabled, _DeFiPositionsControllerV2_getVsCurrency;
13
+ var _DeFiPositionsControllerV2_instances, _DeFiPositionsControllerV2_apiClient, _DeFiPositionsControllerV2_isEnabled, _DeFiPositionsControllerV2_getVsCurrency, _DeFiPositionsControllerV2_inFlightFetches, _DeFiPositionsControllerV2_fetchDeFiPositions;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.DeFiPositionsControllerV2 = exports.getDefaultDeFiPositionsControllerV2State = void 0;
15
+ exports.DeFiPositionsControllerV2 = exports.getDefaultDeFiPositionsControllerV2State = exports.DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = exports.DEFAULT_PROCESSING_POLL_INTERVAL_MS = void 0;
16
16
  const base_controller_1 = require("@metamask/base-controller");
17
17
  const build_defi_balances_query_js_1 = require("./build-defi-balances-query.cjs");
18
18
  const group_defi_positions_v6_js_1 = require("./group-defi-positions-v6.cjs");
19
19
  const controllerName = 'DeFiPositionsControllerV2';
20
20
  const MESSENGER_EXPOSED_METHODS = ['fetchDeFiPositions'];
21
+ /** Delay between polls while Accounts API reports DeFi indexing in progress. */
22
+ exports.DEFAULT_PROCESSING_POLL_INTERVAL_MS = 5000;
23
+ /**
24
+ * Maximum fetch attempts (including the first) while any account still has
25
+ * `processingDefiPositions: true`. After this, the call resolves and keeps
26
+ * prior state for accounts that never became ready.
27
+ */
28
+ exports.DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = 5;
29
+ /**
30
+ * @param ms - Milliseconds to wait.
31
+ * @returns A promise that resolves after `ms`.
32
+ */
33
+ function delay(ms) {
34
+ return new Promise((resolve) => setTimeout(resolve, ms));
35
+ }
21
36
  const controllerMetadata = {
22
37
  allDeFiPositionsV2: {
23
38
  includeInStateLogs: false,
@@ -40,6 +55,13 @@ exports.getDefaultDeFiPositionsControllerV2State = getDefaultDeFiPositionsContro
40
55
  * Deduplication and freshness are handled by the shared TanStack Query cache on
41
56
  * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass
42
57
  * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.
58
+ *
59
+ * When the API reports `processingDefiPositions` for any account, this
60
+ * controller polls until indexing finishes or the attempt limit is reached.
61
+ * Concurrent calls for the same selected accounts share one in-flight promise
62
+ * so the UI can treat the pending promise as a loading signal. Calls for a
63
+ * different selection start their own fetch and leave any prior poll running,
64
+ * so switching back can join an in-flight fetch for that group.
43
65
  */
44
66
  class DeFiPositionsControllerV2 extends base_controller_1.BaseController {
45
67
  /**
@@ -60,9 +82,16 @@ class DeFiPositionsControllerV2 extends base_controller_1.BaseController {
60
82
  ...state,
61
83
  },
62
84
  });
85
+ _DeFiPositionsControllerV2_instances.add(this);
63
86
  _DeFiPositionsControllerV2_apiClient.set(this, void 0);
64
87
  _DeFiPositionsControllerV2_isEnabled.set(this, void 0);
65
88
  _DeFiPositionsControllerV2_getVsCurrency.set(this, void 0);
89
+ /**
90
+ * In-flight fetches keyed by selected DeFi-queryable account IDs. Concurrent
91
+ * callers for the same selection share a promise; different selections keep
92
+ * independent fetches so fast account switching can join an earlier poll.
93
+ */
94
+ _DeFiPositionsControllerV2_inFlightFetches.set(this, new Map());
66
95
  __classPrivateFieldSet(this, _DeFiPositionsControllerV2_apiClient, apiClient, "f");
67
96
  __classPrivateFieldSet(this, _DeFiPositionsControllerV2_isEnabled, isEnabled, "f");
68
97
  __classPrivateFieldSet(this, _DeFiPositionsControllerV2_getVsCurrency, getVsCurrency, "f");
@@ -71,58 +100,96 @@ class DeFiPositionsControllerV2 extends base_controller_1.BaseController {
71
100
  /**
72
101
  * Fetches DeFi positions for the selected account group. Each account key in
73
102
  * a ready response replaces that account's state (other accounts stay).
74
- * Accounts still indexing (`processingDefiPositions`) are skipped so prior
75
- * state is kept for them. No-ops when disabled or when the group has no
76
- * supported accounts. Caching / spam prevention is handled by the apiClient
77
- * TanStack Query cache (keyed by accounts + query options including
78
- * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.
79
- * pull-to-refresh).
103
+ * Accounts still indexing (`processingDefiPositions`) keep prior state; the
104
+ * method polls (invalidating the balances cache between attempts) until all
105
+ * selected accounts are ready, the attempt limit is reached, or a request
106
+ * fails. Concurrent calls for the same selected accounts share one in-flight
107
+ * promise; calls for a different selection start a new fetch and leave prior
108
+ * polls running so a later switch back can join them. No-ops when disabled
109
+ * or when the group has no supported accounts. Caching / spam prevention is
110
+ * handled by the apiClient TanStack Query cache (keyed by accounts + query
111
+ * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass
112
+ * the cache on the first attempt (e.g. pull-to-refresh).
80
113
  *
81
114
  * @param options - Optional fetch modifiers.
82
- * @param options.forceRefresh - When true, bypass the apiClient cache and
83
- * fetch immediately.
115
+ * @param options.forceRefresh - When true, bypass the apiClient cache on the
116
+ * first attempt and fetch immediately.
117
+ * @returns Resolves when the fetch (and any processing polls) finish.
84
118
  */
85
119
  async fetchDeFiPositions(options) {
86
120
  if (!__classPrivateFieldGet(this, _DeFiPositionsControllerV2_isEnabled, "f").call(this)) {
87
121
  return;
88
122
  }
89
123
  const selectedAccounts = this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
90
- const { networks, internalAccountIdByCaip } = (0, build_defi_balances_query_js_1.buildDeFiBalancesQuery)(selectedAccounts);
91
- if (internalAccountIdByCaip.size === 0 || networks.length === 0) {
124
+ const query = (0, build_defi_balances_query_js_1.buildDeFiBalancesQuery)(selectedAccounts);
125
+ const accountIdsKey = [...query.internalAccountIdByCaip.keys()]
126
+ .sort()
127
+ .join('\0');
128
+ const existing = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").get(accountIdsKey);
129
+ if (existing) {
130
+ await existing;
92
131
  return;
93
132
  }
94
- const accountIds = [...internalAccountIdByCaip.keys()];
95
- const vsCurrency = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_getVsCurrency, "f").call(this).toLowerCase();
133
+ const fetchPromise = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_instances, "m", _DeFiPositionsControllerV2_fetchDeFiPositions).call(this, options, query).finally(() => {
134
+ if (__classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").get(accountIdsKey) === fetchPromise) {
135
+ __classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").delete(accountIdsKey);
136
+ }
137
+ });
138
+ __classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").set(accountIdsKey, fetchPromise);
139
+ await fetchPromise;
140
+ }
141
+ }
142
+ exports.DeFiPositionsControllerV2 = DeFiPositionsControllerV2;
143
+ _DeFiPositionsControllerV2_apiClient = new WeakMap(), _DeFiPositionsControllerV2_isEnabled = new WeakMap(), _DeFiPositionsControllerV2_getVsCurrency = new WeakMap(), _DeFiPositionsControllerV2_inFlightFetches = new WeakMap(), _DeFiPositionsControllerV2_instances = new WeakSet(), _DeFiPositionsControllerV2_fetchDeFiPositions = async function _DeFiPositionsControllerV2_fetchDeFiPositions(options, { networks, internalAccountIdByCaip }) {
144
+ if (internalAccountIdByCaip.size === 0 || networks.length === 0) {
145
+ return;
146
+ }
147
+ const accountIds = [...internalAccountIdByCaip.keys()];
148
+ const vsCurrency = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_getVsCurrency, "f").call(this).toLowerCase();
149
+ const queryOptions = {
150
+ networks,
151
+ includeDeFiBalances: true,
152
+ forceFetchDeFiPositions: true,
153
+ includePrices: true,
154
+ vsCurrency,
155
+ };
156
+ for (let attempt = 0; attempt < exports.DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS; attempt++) {
157
+ // First attempt respects forceRefresh; later polls always bypass cache
158
+ // so we do not spin on a stale processing snapshot.
159
+ const fetchOptions = {
160
+ ...(options?.forceRefresh || attempt > 0 ? { staleTime: 0 } : {}),
161
+ };
96
162
  try {
97
- const response = await __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.fetchV6MultiAccountBalances(accountIds, {
98
- networks,
99
- includeDeFiBalances: true,
100
- forceFetchDeFiPositions: true,
101
- includePrices: true,
102
- vsCurrency,
103
- }, {
104
- // staleTime: 0 makes TanStack treat the cache as stale for this call.
105
- ...(options?.forceRefresh ? { staleTime: 0 } : {}),
106
- });
107
- // Skip accounts still indexing — their balances are not a valid snapshot.
163
+ const response = await __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.fetchV6MultiAccountBalances(accountIds, queryOptions, fetchOptions);
164
+ const processingAccounts = response.accounts.filter((account) => account.processingDefiPositions);
108
165
  const readyAccounts = response.accounts.filter((account) => !account.processingDefiPositions);
109
- if (readyAccounts.length === 0) {
166
+ if (readyAccounts.length > 0) {
167
+ const positionsByAccount = (0, group_defi_positions_v6_js_1.groupDeFiPositionsV6)({ ...response, accounts: readyAccounts }, internalAccountIdByCaip);
168
+ // Last valid response wins per ready account; still-indexing accounts
169
+ // stay untouched.
170
+ this.update((state) => {
171
+ for (const [accountId, positions] of Object.entries(positionsByAccount)) {
172
+ state.allDeFiPositionsV2[accountId] = positions;
173
+ }
174
+ });
175
+ }
176
+ if (processingAccounts.length === 0) {
110
177
  return;
111
178
  }
112
- const positionsByAccount = (0, group_defi_positions_v6_js_1.groupDeFiPositionsV6)({ ...response, accounts: readyAccounts }, internalAccountIdByCaip);
113
- // Last valid response wins per ready account; processing / other accounts
114
- // stay untouched.
115
- this.update((state) => {
116
- for (const [accountId, positions] of Object.entries(positionsByAccount)) {
117
- state.allDeFiPositionsV2[accountId] = positions;
118
- }
179
+ const { queryKey } = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.getV6MultiAccountBalancesQueryOptions(accountIds, queryOptions, fetchOptions);
180
+ await __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.queryClient.invalidateQueries({
181
+ queryKey,
119
182
  });
183
+ const isLastAttempt = attempt >= exports.DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS - 1;
184
+ if (isLastAttempt) {
185
+ return;
186
+ }
187
+ await delay(exports.DEFAULT_PROCESSING_POLL_INTERVAL_MS);
120
188
  }
121
189
  catch (error) {
122
190
  console.error('Failed to fetch DeFi positions', error);
191
+ return;
123
192
  }
124
193
  }
125
- }
126
- exports.DeFiPositionsControllerV2 = DeFiPositionsControllerV2;
127
- _DeFiPositionsControllerV2_apiClient = new WeakMap(), _DeFiPositionsControllerV2_isEnabled = new WeakMap(), _DeFiPositionsControllerV2_getVsCurrency = new WeakMap();
194
+ };
128
195
  //# sourceMappingURL=DeFiPositionsControllerV2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,+DAA2D;AAS3D,kFAAwE;AAGxE,8EAAoE;AAEpE,MAAM,cAAc,GAAG,2BAA2B,CAAC;AAEnD,MAAM,yBAAyB,GAAG,CAAC,oBAAoB,CAAU,CAAC;AAmBlE,MAAM,kBAAkB,GAAkD;IACxE,kBAAkB,EAAE;QAClB,kBAAkB,EAAE,KAAK;QACzB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,KAAK;QAC7B,QAAQ,EAAE,IAAI;KACf;CACF,CAAC;AAEK,MAAM,wCAAwC,GACnD,GAAmC,EAAE;IACnC,OAAO;QACL,kBAAkB,EAAE,EAAE;KACvB,CAAC;AACJ,CAAC,CAAC;AALS,QAAA,wCAAwC,4CAKjD;AA0CJ;;;;;;;;GAQG;AACH,MAAa,yBAA0B,SAAQ,gCAI9C;IAOC;;;;;;;OAOG;IACH,YAAY,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,aAAa,EACb,KAAK,GAON;QACC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,kBAAkB;YAC5B,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,IAAA,gDAAwC,GAAE;gBAC7C,GAAG,KAAK;aACT;SACF,CAAC,CAAC;QAnCI,uDAA8B;QAE9B,uDAA0B;QAE1B,2DAA6B;QAiCpC,uBAAA,IAAI,wCAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,wCAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,4CAAkB,aAAa,MAAA,CAAC;QAEpC,IAAI,CAAC,SAAS,CAAC,4BAA4B,CACzC,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,kBAAkB,CAAC,OAExB;QACC,IAAI,CAAC,uBAAA,IAAI,4CAAW,MAAf,IAAI,CAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAC1C,2DAA2D,CAC5D,CAAC;QAEF,MAAM,EAAE,QAAQ,EAAE,uBAAuB,EAAE,GACzC,IAAA,qDAAsB,EAAC,gBAAgB,CAAC,CAAC;QAE3C,IAAI,uBAAuB,CAAC,IAAI,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChE,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,GAAG,uBAAuB,CAAC,IAAI,EAAE,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,uBAAA,IAAI,gDAAe,MAAnB,IAAI,CAAiB,CAAC,WAAW,EAAE,CAAC;QAEvD,IAAI,CAAC;YACH,MAAM,QAAQ,GACZ,MAAM,uBAAA,IAAI,4CAAW,CAAC,QAAQ,CAAC,2BAA2B,CACxD,UAAU,EACV;gBACE,QAAQ;gBACR,mBAAmB,EAAE,IAAI;gBACzB,uBAAuB,EAAE,IAAI;gBAC7B,aAAa,EAAE,IAAI;gBACnB,UAAU;aACX,EACD;gBACE,sEAAsE;gBACtE,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnD,CACF,CAAC;YAEJ,0EAA0E;YAC1E,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAC5C,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAC9C,CAAC;YACF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YAED,MAAM,kBAAkB,GAAG,IAAA,iDAAoB,EAC7C,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,EACxC,uBAAuB,CACxB,CAAC;YAEF,0EAA0E;YAC1E,kBAAkB;YAClB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CACjD,kBAAkB,CACnB,EAAE,CAAC;oBACF,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC;gBAClD,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;CACF;AAlID,8DAkIC","sourcesContent":["import type { AccountTreeControllerGetAccountsFromSelectedAccountGroupAction } from '@metamask/account-tree-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { buildDeFiBalancesQuery } from './build-defi-balances-query.js';\nimport type { DeFiPositionsControllerV2MethodActions } from './DeFiPositionsControllerV2-method-action-types.js';\nimport type { DeFiPositionsByAccount } from './group-defi-positions-v6.js';\nimport { groupDeFiPositionsV6 } from './group-defi-positions-v6.js';\n\nconst controllerName = 'DeFiPositionsControllerV2';\n\nconst MESSENGER_EXPOSED_METHODS = ['fetchDeFiPositions'] as const;\n\nexport type DeFiPositionsControllerV2State = {\n /**\n * DeFi positions keyed by internal MetaMask account ID (`InternalAccount.id`,\n * the same key AssetsController uses). Each account maps to a flat list of\n * protocol groups shown in the DeFi tab, each carrying its own `chainId` for\n * filtering plus the details-page sections embedded inside it. This is\n * exactly the shape the client consumes, so no further transformation is\n * needed on read.\n *\n * Named `allDeFiPositionsV2` (rather than `allDeFiPositions`) so it can live\n * alongside the legacy `DeFiPositionsController` in clients that flatten every\n * controller's state into a single object (e.g. the extension background),\n * without colliding on the shared `allDeFiPositions` key.\n */\n allDeFiPositionsV2: DeFiPositionsByAccount;\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerV2State> = {\n allDeFiPositionsV2: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\nexport const getDefaultDeFiPositionsControllerV2State =\n (): DeFiPositionsControllerV2State => {\n return {\n allDeFiPositionsV2: {},\n };\n };\n\nexport type DeFiPositionsControllerV2GetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerV2State\n>;\n\nexport type DeFiPositionsControllerV2Actions =\n | DeFiPositionsControllerV2GetStateAction\n | DeFiPositionsControllerV2MethodActions;\n\nexport type DeFiPositionsControllerV2StateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n DeFiPositionsControllerV2State\n >;\n\nexport type DeFiPositionsControllerV2Events =\n DeFiPositionsControllerV2StateChangedEvent;\n\n/**\n * The external actions available to the {@link DeFiPositionsControllerV2}.\n */\nexport type AllowedActions =\n AccountTreeControllerGetAccountsFromSelectedAccountGroupAction;\n\n/**\n * The external events available to the {@link DeFiPositionsControllerV2}.\n *\n * None yet — clients must call `fetchDeFiPositions` (and optionally\n * `{ forceRefresh: true }`) on their own triggers. Likely future subscriptions:\n * `AccountTreeController:selectedAccountGroupChange`,\n * `TransactionController:transactionConfirmed`, and `KeyringController:lock`.\n */\nexport type AllowedEvents = never;\n\nexport type DeFiPositionsControllerV2Messenger = Messenger<\n typeof controllerName,\n DeFiPositionsControllerV2Actions | AllowedActions,\n DeFiPositionsControllerV2Events | AllowedEvents\n>;\n\n/**\n * Controller that fetches DeFi positions for the selected account group from\n * the Accounts API (v6 multiaccount balances) and stores them in the shape the\n * client consumes directly.\n *\n * Deduplication and freshness are handled by the shared TanStack Query cache on\n * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass\n * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.\n */\nexport class DeFiPositionsControllerV2 extends BaseController<\n typeof controllerName,\n DeFiPositionsControllerV2State,\n DeFiPositionsControllerV2Messenger\n> {\n readonly #apiClient: ApiPlatformClient;\n\n readonly #isEnabled: () => boolean;\n\n readonly #getVsCurrency: () => string;\n\n /**\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.apiClient - Accounts API client used to fetch balances/positions. Auth is handled by the client.\n * @param options.isEnabled - Returns whether fetching is enabled (default: () => false).\n * @param options.getVsCurrency - Returns the fiat currency for prices (default: () => 'usd').\n * @param options.state - Initial controller state.\n */\n constructor({\n messenger,\n apiClient,\n isEnabled,\n getVsCurrency,\n state,\n }: {\n messenger: DeFiPositionsControllerV2Messenger;\n apiClient: ApiPlatformClient;\n isEnabled: () => boolean;\n getVsCurrency: () => string;\n state?: Partial<DeFiPositionsControllerV2State>;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: {\n ...getDefaultDeFiPositionsControllerV2State(),\n ...state,\n },\n });\n\n this.#apiClient = apiClient;\n this.#isEnabled = isEnabled;\n this.#getVsCurrency = getVsCurrency;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) are skipped so prior\n * state is kept for them. No-ops when disabled or when the group has no\n * supported accounts. Caching / spam prevention is handled by the apiClient\n * TanStack Query cache (keyed by accounts + query options including\n * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.\n * pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache and\n * fetch immediately.\n */\n async fetchDeFiPositions(options?: {\n forceRefresh?: boolean;\n }): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const selectedAccounts = this.messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n\n const { networks, internalAccountIdByCaip } =\n buildDeFiBalancesQuery(selectedAccounts);\n\n if (internalAccountIdByCaip.size === 0 || networks.length === 0) {\n return;\n }\n\n const accountIds = [...internalAccountIdByCaip.keys()];\n const vsCurrency = this.#getVsCurrency().toLowerCase();\n\n try {\n const response =\n await this.#apiClient.accounts.fetchV6MultiAccountBalances(\n accountIds,\n {\n networks,\n includeDeFiBalances: true,\n forceFetchDeFiPositions: true,\n includePrices: true,\n vsCurrency,\n },\n {\n // staleTime: 0 makes TanStack treat the cache as stale for this call.\n ...(options?.forceRefresh ? { staleTime: 0 } : {}),\n },\n );\n\n // Skip accounts still indexing — their balances are not a valid snapshot.\n const readyAccounts = response.accounts.filter(\n (account) => !account.processingDefiPositions,\n );\n if (readyAccounts.length === 0) {\n return;\n }\n\n const positionsByAccount = groupDeFiPositionsV6(\n { ...response, accounts: readyAccounts },\n internalAccountIdByCaip,\n );\n\n // Last valid response wins per ready account; processing / other accounts\n // stay untouched.\n this.update((state) => {\n for (const [accountId, positions] of Object.entries(\n positionsByAccount,\n )) {\n state.allDeFiPositionsV2[accountId] = positions;\n }\n });\n } catch (error) {\n console.error('Failed to fetch DeFi positions', error);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"DeFiPositionsControllerV2.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountTreeControllerGetAccountsFromSelectedAccountGroupAction } from '@metamask/account-tree-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { buildDeFiBalancesQuery } from './build-defi-balances-query.js';\nimport type { DeFiBalancesQuery } from './build-defi-balances-query.js';\nimport type { DeFiPositionsControllerV2MethodActions } from './DeFiPositionsControllerV2-method-action-types.js';\nimport type { DeFiPositionsByAccount } from './group-defi-positions-v6.js';\nimport { groupDeFiPositionsV6 } from './group-defi-positions-v6.js';\n\nconst controllerName = 'DeFiPositionsControllerV2';\n\nconst MESSENGER_EXPOSED_METHODS = ['fetchDeFiPositions'] as const;\n\n/** Delay between polls while Accounts API reports DeFi indexing in progress. */\nexport const DEFAULT_PROCESSING_POLL_INTERVAL_MS = 5_000;\n\n/**\n * Maximum fetch attempts (including the first) while any account still has\n * `processingDefiPositions: true`. After this, the call resolves and keeps\n * prior state for accounts that never became ready.\n */\nexport const DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = 5;\n\n/**\n * @param ms - Milliseconds to wait.\n * @returns A promise that resolves after `ms`.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport type DeFiPositionsControllerV2State = {\n /**\n * DeFi positions keyed by internal MetaMask account ID (`InternalAccount.id`,\n * the same key AssetsController uses). Each account maps to a flat list of\n * protocol groups shown in the DeFi tab, each carrying its own `chainId` for\n * filtering plus the details-page sections embedded inside it. This is\n * exactly the shape the client consumes, so no further transformation is\n * needed on read.\n *\n * Named `allDeFiPositionsV2` (rather than `allDeFiPositions`) so it can live\n * alongside the legacy `DeFiPositionsController` in clients that flatten every\n * controller's state into a single object (e.g. the extension background),\n * without colliding on the shared `allDeFiPositions` key.\n */\n allDeFiPositionsV2: DeFiPositionsByAccount;\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerV2State> = {\n allDeFiPositionsV2: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\nexport const getDefaultDeFiPositionsControllerV2State =\n (): DeFiPositionsControllerV2State => {\n return {\n allDeFiPositionsV2: {},\n };\n };\n\nexport type DeFiPositionsControllerV2GetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerV2State\n>;\n\nexport type DeFiPositionsControllerV2Actions =\n | DeFiPositionsControllerV2GetStateAction\n | DeFiPositionsControllerV2MethodActions;\n\nexport type DeFiPositionsControllerV2StateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n DeFiPositionsControllerV2State\n >;\n\nexport type DeFiPositionsControllerV2Events =\n DeFiPositionsControllerV2StateChangedEvent;\n\n/**\n * The external actions available to the {@link DeFiPositionsControllerV2}.\n */\nexport type AllowedActions =\n AccountTreeControllerGetAccountsFromSelectedAccountGroupAction;\n\n/**\n * The external events available to the {@link DeFiPositionsControllerV2}.\n *\n * None yet — clients must call `fetchDeFiPositions` (and optionally\n * `{ forceRefresh: true }`) on their own triggers. Likely future subscriptions:\n * `AccountTreeController:selectedAccountGroupChange`,\n * `TransactionController:transactionConfirmed`, and `KeyringController:lock`.\n */\nexport type AllowedEvents = never;\n\nexport type DeFiPositionsControllerV2Messenger = Messenger<\n typeof controllerName,\n DeFiPositionsControllerV2Actions | AllowedActions,\n DeFiPositionsControllerV2Events | AllowedEvents\n>;\n\n/**\n * Controller that fetches DeFi positions for the selected account group from\n * the Accounts API (v6 multiaccount balances) and stores them in the shape the\n * client consumes directly.\n *\n * Deduplication and freshness are handled by the shared TanStack Query cache on\n * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass\n * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.\n *\n * When the API reports `processingDefiPositions` for any account, this\n * controller polls until indexing finishes or the attempt limit is reached.\n * Concurrent calls for the same selected accounts share one in-flight promise\n * so the UI can treat the pending promise as a loading signal. Calls for a\n * different selection start their own fetch and leave any prior poll running,\n * so switching back can join an in-flight fetch for that group.\n */\nexport class DeFiPositionsControllerV2 extends BaseController<\n typeof controllerName,\n DeFiPositionsControllerV2State,\n DeFiPositionsControllerV2Messenger\n> {\n readonly #apiClient: ApiPlatformClient;\n\n readonly #isEnabled: () => boolean;\n\n readonly #getVsCurrency: () => string;\n\n /**\n * In-flight fetches keyed by selected DeFi-queryable account IDs. Concurrent\n * callers for the same selection share a promise; different selections keep\n * independent fetches so fast account switching can join an earlier poll.\n */\n readonly #inFlightFetches = new Map<string, Promise<void>>();\n\n /**\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.apiClient - Accounts API client used to fetch balances/positions. Auth is handled by the client.\n * @param options.isEnabled - Returns whether fetching is enabled (default: () => false).\n * @param options.getVsCurrency - Returns the fiat currency for prices (default: () => 'usd').\n * @param options.state - Initial controller state.\n */\n constructor({\n messenger,\n apiClient,\n isEnabled,\n getVsCurrency,\n state,\n }: {\n messenger: DeFiPositionsControllerV2Messenger;\n apiClient: ApiPlatformClient;\n isEnabled: () => boolean;\n getVsCurrency: () => string;\n state?: Partial<DeFiPositionsControllerV2State>;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: {\n ...getDefaultDeFiPositionsControllerV2State(),\n ...state,\n },\n });\n\n this.#apiClient = apiClient;\n this.#isEnabled = isEnabled;\n this.#getVsCurrency = getVsCurrency;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) keep prior state; the\n * method polls (invalidating the balances cache between attempts) until all\n * selected accounts are ready, the attempt limit is reached, or a request\n * fails. Concurrent calls for the same selected accounts share one in-flight\n * promise; calls for a different selection start a new fetch and leave prior\n * polls running so a later switch back can join them. No-ops when disabled\n * or when the group has no supported accounts. Caching / spam prevention is\n * handled by the apiClient TanStack Query cache (keyed by accounts + query\n * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass\n * the cache on the first attempt (e.g. pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache on the\n * first attempt and fetch immediately.\n * @returns Resolves when the fetch (and any processing polls) finish.\n */\n async fetchDeFiPositions(options?: {\n forceRefresh?: boolean;\n }): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const selectedAccounts = this.messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n const query = buildDeFiBalancesQuery(selectedAccounts);\n const accountIdsKey = [...query.internalAccountIdByCaip.keys()]\n .sort()\n .join('\\0');\n\n const existing = this.#inFlightFetches.get(accountIdsKey);\n if (existing) {\n await existing;\n return;\n }\n\n const fetchPromise = this.#fetchDeFiPositions(options, query).finally(\n () => {\n if (this.#inFlightFetches.get(accountIdsKey) === fetchPromise) {\n this.#inFlightFetches.delete(accountIdsKey);\n }\n },\n );\n this.#inFlightFetches.set(accountIdsKey, fetchPromise);\n\n await fetchPromise;\n }\n\n async #fetchDeFiPositions(\n options: { forceRefresh?: boolean } | undefined,\n { networks, internalAccountIdByCaip }: DeFiBalancesQuery,\n ): Promise<void> {\n if (internalAccountIdByCaip.size === 0 || networks.length === 0) {\n return;\n }\n\n const accountIds = [...internalAccountIdByCaip.keys()];\n const vsCurrency = this.#getVsCurrency().toLowerCase();\n const queryOptions = {\n networks,\n includeDeFiBalances: true,\n forceFetchDeFiPositions: true,\n includePrices: true,\n vsCurrency,\n };\n\n for (\n let attempt = 0;\n attempt < DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS;\n attempt++\n ) {\n // First attempt respects forceRefresh; later polls always bypass cache\n // so we do not spin on a stale processing snapshot.\n const fetchOptions = {\n ...(options?.forceRefresh || attempt > 0 ? { staleTime: 0 } : {}),\n };\n\n try {\n const response =\n await this.#apiClient.accounts.fetchV6MultiAccountBalances(\n accountIds,\n queryOptions,\n fetchOptions,\n );\n\n const processingAccounts = response.accounts.filter(\n (account) => account.processingDefiPositions,\n );\n const readyAccounts = response.accounts.filter(\n (account) => !account.processingDefiPositions,\n );\n\n if (readyAccounts.length > 0) {\n const positionsByAccount = groupDeFiPositionsV6(\n { ...response, accounts: readyAccounts },\n internalAccountIdByCaip,\n );\n\n // Last valid response wins per ready account; still-indexing accounts\n // stay untouched.\n this.update((state) => {\n for (const [accountId, positions] of Object.entries(\n positionsByAccount,\n )) {\n state.allDeFiPositionsV2[accountId] = positions;\n }\n });\n }\n\n if (processingAccounts.length === 0) {\n return;\n }\n\n const { queryKey } =\n this.#apiClient.accounts.getV6MultiAccountBalancesQueryOptions(\n accountIds,\n queryOptions,\n fetchOptions,\n );\n await this.#apiClient.accounts.queryClient.invalidateQueries({\n queryKey,\n });\n\n const isLastAttempt =\n attempt >= DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS - 1;\n if (isLastAttempt) {\n return;\n }\n\n await delay(DEFAULT_PROCESSING_POLL_INTERVAL_MS);\n } catch (error) {\n console.error('Failed to fetch DeFi positions', error);\n return;\n }\n }\n }\n}\n"]}
@@ -6,6 +6,14 @@ import type { Messenger } from "@metamask/messenger";
6
6
  import type { DeFiPositionsControllerV2MethodActions } from "./DeFiPositionsControllerV2-method-action-types.cjs";
7
7
  import type { DeFiPositionsByAccount } from "./group-defi-positions-v6.cjs";
8
8
  declare const controllerName = "DeFiPositionsControllerV2";
9
+ /** Delay between polls while Accounts API reports DeFi indexing in progress. */
10
+ export declare const DEFAULT_PROCESSING_POLL_INTERVAL_MS = 5000;
11
+ /**
12
+ * Maximum fetch attempts (including the first) while any account still has
13
+ * `processingDefiPositions: true`. After this, the call resolves and keeps
14
+ * prior state for accounts that never became ready.
15
+ */
16
+ export declare const DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = 5;
9
17
  export type DeFiPositionsControllerV2State = {
10
18
  /**
11
19
  * DeFi positions keyed by internal MetaMask account ID (`InternalAccount.id`,
@@ -49,6 +57,13 @@ export type DeFiPositionsControllerV2Messenger = Messenger<typeof controllerName
49
57
  * Deduplication and freshness are handled by the shared TanStack Query cache on
50
58
  * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass
51
59
  * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.
60
+ *
61
+ * When the API reports `processingDefiPositions` for any account, this
62
+ * controller polls until indexing finishes or the attempt limit is reached.
63
+ * Concurrent calls for the same selected accounts share one in-flight promise
64
+ * so the UI can treat the pending promise as a loading signal. Calls for a
65
+ * different selection start their own fetch and leave any prior poll running,
66
+ * so switching back can join an in-flight fetch for that group.
52
67
  */
53
68
  export declare class DeFiPositionsControllerV2 extends BaseController<typeof controllerName, DeFiPositionsControllerV2State, DeFiPositionsControllerV2Messenger> {
54
69
  #private;
@@ -70,16 +85,21 @@ export declare class DeFiPositionsControllerV2 extends BaseController<typeof con
70
85
  /**
71
86
  * Fetches DeFi positions for the selected account group. Each account key in
72
87
  * a ready response replaces that account's state (other accounts stay).
73
- * Accounts still indexing (`processingDefiPositions`) are skipped so prior
74
- * state is kept for them. No-ops when disabled or when the group has no
75
- * supported accounts. Caching / spam prevention is handled by the apiClient
76
- * TanStack Query cache (keyed by accounts + query options including
77
- * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.
78
- * pull-to-refresh).
88
+ * Accounts still indexing (`processingDefiPositions`) keep prior state; the
89
+ * method polls (invalidating the balances cache between attempts) until all
90
+ * selected accounts are ready, the attempt limit is reached, or a request
91
+ * fails. Concurrent calls for the same selected accounts share one in-flight
92
+ * promise; calls for a different selection start a new fetch and leave prior
93
+ * polls running so a later switch back can join them. No-ops when disabled
94
+ * or when the group has no supported accounts. Caching / spam prevention is
95
+ * handled by the apiClient TanStack Query cache (keyed by accounts + query
96
+ * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass
97
+ * the cache on the first attempt (e.g. pull-to-refresh).
79
98
  *
80
99
  * @param options - Optional fetch modifiers.
81
- * @param options.forceRefresh - When true, bypass the apiClient cache and
82
- * fetch immediately.
100
+ * @param options.forceRefresh - When true, bypass the apiClient cache on the
101
+ * first attempt and fetch immediately.
102
+ * @returns Resolves when the fetch (and any processing polls) finish.
83
103
  */
84
104
  fetchDeFiPositions(options?: {
85
105
  forceRefresh?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8DAA8D,EAAE,0CAA0C;AACxH,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAE5B,kCAAkC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,+BAA+B;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAGrD,OAAO,KAAK,EAAE,sCAAsC,EAAE,4DAA2D;AACjH,OAAO,KAAK,EAAE,sBAAsB,EAAE,sCAAqC;AAG3E,QAAA,MAAM,cAAc,8BAA8B,CAAC;AAInD,MAAM,MAAM,8BAA8B,GAAG;IAC3C;;;;;;;;;;;;OAYG;IACH,kBAAkB,EAAE,sBAAsB,CAAC;CAC5C,CAAC;AAWF,eAAO,MAAM,wCAAwC,QAC/C,8BAIH,CAAC;AAEJ,MAAM,MAAM,uCAAuC,GAAG,wBAAwB,CAC5E,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEF,MAAM,MAAM,gCAAgC,GACxC,uCAAuC,GACvC,sCAAsC,CAAC;AAE3C,MAAM,MAAM,0CAA0C,GACpD,2BAA2B,CACzB,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEJ,MAAM,MAAM,+BAA+B,GACzC,0CAA0C,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,cAAc,GACxB,8DAA8D,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,kCAAkC,GAAG,SAAS,CACxD,OAAO,cAAc,EACrB,gCAAgC,GAAG,cAAc,EACjD,+BAA+B,GAAG,aAAa,CAChD,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,SAAQ,cAAc,CAC3D,OAAO,cAAc,EACrB,8BAA8B,EAC9B,kCAAkC,CACnC;;IAOC;;;;;;;OAOG;gBACS,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,aAAa,EACb,KAAK,GACN,EAAE;QACD,SAAS,EAAE,kCAAkC,CAAC;QAC9C,SAAS,EAAE,iBAAiB,CAAC;QAC7B,SAAS,EAAE,MAAM,OAAO,CAAC;QACzB,aAAa,EAAE,MAAM,MAAM,CAAC;QAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;KACjD;IAqBD;;;;;;;;;;;;;OAaG;IACG,kBAAkB,CAAC,OAAO,CAAC,EAAE;QACjC,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;CA8DlB"}
1
+ {"version":3,"file":"DeFiPositionsControllerV2.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8DAA8D,EAAE,0CAA0C;AACxH,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAE5B,kCAAkC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,+BAA+B;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAAE,sCAAsC,EAAE,4DAA2D;AACjH,OAAO,KAAK,EAAE,sBAAsB,EAAE,sCAAqC;AAG3E,QAAA,MAAM,cAAc,8BAA8B,CAAC;AAInD,gFAAgF;AAChF,eAAO,MAAM,mCAAmC,OAAQ,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAUtD,MAAM,MAAM,8BAA8B,GAAG;IAC3C;;;;;;;;;;;;OAYG;IACH,kBAAkB,EAAE,sBAAsB,CAAC;CAC5C,CAAC;AAWF,eAAO,MAAM,wCAAwC,QAC/C,8BAIH,CAAC;AAEJ,MAAM,MAAM,uCAAuC,GAAG,wBAAwB,CAC5E,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEF,MAAM,MAAM,gCAAgC,GACxC,uCAAuC,GACvC,sCAAsC,CAAC;AAE3C,MAAM,MAAM,0CAA0C,GACpD,2BAA2B,CACzB,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEJ,MAAM,MAAM,+BAA+B,GACzC,0CAA0C,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,cAAc,GACxB,8DAA8D,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,kCAAkC,GAAG,SAAS,CACxD,OAAO,cAAc,EACrB,gCAAgC,GAAG,cAAc,EACjD,+BAA+B,GAAG,aAAa,CAChD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,yBAA0B,SAAQ,cAAc,CAC3D,OAAO,cAAc,EACrB,8BAA8B,EAC9B,kCAAkC,CACnC;;IAcC;;;;;;;OAOG;gBACS,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,aAAa,EACb,KAAK,GACN,EAAE;QACD,SAAS,EAAE,kCAAkC,CAAC;QAC9C,SAAS,EAAE,iBAAiB,CAAC;QAC7B,SAAS,EAAE,MAAM,OAAO,CAAC;QACzB,aAAa,EAAE,MAAM,MAAM,CAAC;QAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;KACjD;IAqBD;;;;;;;;;;;;;;;;;;OAkBG;IACG,kBAAkB,CAAC,OAAO,CAAC,EAAE;QACjC,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;CAuHlB"}
@@ -6,6 +6,14 @@ import type { Messenger } from "@metamask/messenger";
6
6
  import type { DeFiPositionsControllerV2MethodActions } from "./DeFiPositionsControllerV2-method-action-types.mjs";
7
7
  import type { DeFiPositionsByAccount } from "./group-defi-positions-v6.mjs";
8
8
  declare const controllerName = "DeFiPositionsControllerV2";
9
+ /** Delay between polls while Accounts API reports DeFi indexing in progress. */
10
+ export declare const DEFAULT_PROCESSING_POLL_INTERVAL_MS = 5000;
11
+ /**
12
+ * Maximum fetch attempts (including the first) while any account still has
13
+ * `processingDefiPositions: true`. After this, the call resolves and keeps
14
+ * prior state for accounts that never became ready.
15
+ */
16
+ export declare const DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = 5;
9
17
  export type DeFiPositionsControllerV2State = {
10
18
  /**
11
19
  * DeFi positions keyed by internal MetaMask account ID (`InternalAccount.id`,
@@ -49,6 +57,13 @@ export type DeFiPositionsControllerV2Messenger = Messenger<typeof controllerName
49
57
  * Deduplication and freshness are handled by the shared TanStack Query cache on
50
58
  * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass
51
59
  * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.
60
+ *
61
+ * When the API reports `processingDefiPositions` for any account, this
62
+ * controller polls until indexing finishes or the attempt limit is reached.
63
+ * Concurrent calls for the same selected accounts share one in-flight promise
64
+ * so the UI can treat the pending promise as a loading signal. Calls for a
65
+ * different selection start their own fetch and leave any prior poll running,
66
+ * so switching back can join an in-flight fetch for that group.
52
67
  */
53
68
  export declare class DeFiPositionsControllerV2 extends BaseController<typeof controllerName, DeFiPositionsControllerV2State, DeFiPositionsControllerV2Messenger> {
54
69
  #private;
@@ -70,16 +85,21 @@ export declare class DeFiPositionsControllerV2 extends BaseController<typeof con
70
85
  /**
71
86
  * Fetches DeFi positions for the selected account group. Each account key in
72
87
  * a ready response replaces that account's state (other accounts stay).
73
- * Accounts still indexing (`processingDefiPositions`) are skipped so prior
74
- * state is kept for them. No-ops when disabled or when the group has no
75
- * supported accounts. Caching / spam prevention is handled by the apiClient
76
- * TanStack Query cache (keyed by accounts + query options including
77
- * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.
78
- * pull-to-refresh).
88
+ * Accounts still indexing (`processingDefiPositions`) keep prior state; the
89
+ * method polls (invalidating the balances cache between attempts) until all
90
+ * selected accounts are ready, the attempt limit is reached, or a request
91
+ * fails. Concurrent calls for the same selected accounts share one in-flight
92
+ * promise; calls for a different selection start a new fetch and leave prior
93
+ * polls running so a later switch back can join them. No-ops when disabled
94
+ * or when the group has no supported accounts. Caching / spam prevention is
95
+ * handled by the apiClient TanStack Query cache (keyed by accounts + query
96
+ * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass
97
+ * the cache on the first attempt (e.g. pull-to-refresh).
79
98
  *
80
99
  * @param options - Optional fetch modifiers.
81
- * @param options.forceRefresh - When true, bypass the apiClient cache and
82
- * fetch immediately.
100
+ * @param options.forceRefresh - When true, bypass the apiClient cache on the
101
+ * first attempt and fetch immediately.
102
+ * @returns Resolves when the fetch (and any processing polls) finish.
83
103
  */
84
104
  fetchDeFiPositions(options?: {
85
105
  forceRefresh?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8DAA8D,EAAE,0CAA0C;AACxH,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAE5B,kCAAkC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,+BAA+B;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAGrD,OAAO,KAAK,EAAE,sCAAsC,EAAE,4DAA2D;AACjH,OAAO,KAAK,EAAE,sBAAsB,EAAE,sCAAqC;AAG3E,QAAA,MAAM,cAAc,8BAA8B,CAAC;AAInD,MAAM,MAAM,8BAA8B,GAAG;IAC3C;;;;;;;;;;;;OAYG;IACH,kBAAkB,EAAE,sBAAsB,CAAC;CAC5C,CAAC;AAWF,eAAO,MAAM,wCAAwC,QAC/C,8BAIH,CAAC;AAEJ,MAAM,MAAM,uCAAuC,GAAG,wBAAwB,CAC5E,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEF,MAAM,MAAM,gCAAgC,GACxC,uCAAuC,GACvC,sCAAsC,CAAC;AAE3C,MAAM,MAAM,0CAA0C,GACpD,2BAA2B,CACzB,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEJ,MAAM,MAAM,+BAA+B,GACzC,0CAA0C,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,cAAc,GACxB,8DAA8D,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,kCAAkC,GAAG,SAAS,CACxD,OAAO,cAAc,EACrB,gCAAgC,GAAG,cAAc,EACjD,+BAA+B,GAAG,aAAa,CAChD,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,SAAQ,cAAc,CAC3D,OAAO,cAAc,EACrB,8BAA8B,EAC9B,kCAAkC,CACnC;;IAOC;;;;;;;OAOG;gBACS,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,aAAa,EACb,KAAK,GACN,EAAE;QACD,SAAS,EAAE,kCAAkC,CAAC;QAC9C,SAAS,EAAE,iBAAiB,CAAC;QAC7B,SAAS,EAAE,MAAM,OAAO,CAAC;QACzB,aAAa,EAAE,MAAM,MAAM,CAAC;QAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;KACjD;IAqBD;;;;;;;;;;;;;OAaG;IACG,kBAAkB,CAAC,OAAO,CAAC,EAAE;QACjC,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;CA8DlB"}
1
+ {"version":3,"file":"DeFiPositionsControllerV2.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8DAA8D,EAAE,0CAA0C;AACxH,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EACV,wBAAwB,EACxB,2BAA2B,EAE5B,kCAAkC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,+BAA+B;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAIrD,OAAO,KAAK,EAAE,sCAAsC,EAAE,4DAA2D;AACjH,OAAO,KAAK,EAAE,sBAAsB,EAAE,sCAAqC;AAG3E,QAAA,MAAM,cAAc,8BAA8B,CAAC;AAInD,gFAAgF;AAChF,eAAO,MAAM,mCAAmC,OAAQ,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAUtD,MAAM,MAAM,8BAA8B,GAAG;IAC3C;;;;;;;;;;;;OAYG;IACH,kBAAkB,EAAE,sBAAsB,CAAC;CAC5C,CAAC;AAWF,eAAO,MAAM,wCAAwC,QAC/C,8BAIH,CAAC;AAEJ,MAAM,MAAM,uCAAuC,GAAG,wBAAwB,CAC5E,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEF,MAAM,MAAM,gCAAgC,GACxC,uCAAuC,GACvC,sCAAsC,CAAC;AAE3C,MAAM,MAAM,0CAA0C,GACpD,2BAA2B,CACzB,OAAO,cAAc,EACrB,8BAA8B,CAC/B,CAAC;AAEJ,MAAM,MAAM,+BAA+B,GACzC,0CAA0C,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,cAAc,GACxB,8DAA8D,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC;AAElC,MAAM,MAAM,kCAAkC,GAAG,SAAS,CACxD,OAAO,cAAc,EACrB,gCAAgC,GAAG,cAAc,EACjD,+BAA+B,GAAG,aAAa,CAChD,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,yBAA0B,SAAQ,cAAc,CAC3D,OAAO,cAAc,EACrB,8BAA8B,EAC9B,kCAAkC,CACnC;;IAcC;;;;;;;OAOG;gBACS,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,aAAa,EACb,KAAK,GACN,EAAE;QACD,SAAS,EAAE,kCAAkC,CAAC;QAC9C,SAAS,EAAE,iBAAiB,CAAC;QAC7B,SAAS,EAAE,MAAM,OAAO,CAAC;QACzB,aAAa,EAAE,MAAM,MAAM,CAAC;QAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;KACjD;IAqBD;;;;;;;;;;;;;;;;;;OAkBG;IACG,kBAAkB,CAAC,OAAO,CAAC,EAAE;QACjC,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;CAuHlB"}
@@ -9,12 +9,27 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _DeFiPositionsControllerV2_apiClient, _DeFiPositionsControllerV2_isEnabled, _DeFiPositionsControllerV2_getVsCurrency;
12
+ var _DeFiPositionsControllerV2_instances, _DeFiPositionsControllerV2_apiClient, _DeFiPositionsControllerV2_isEnabled, _DeFiPositionsControllerV2_getVsCurrency, _DeFiPositionsControllerV2_inFlightFetches, _DeFiPositionsControllerV2_fetchDeFiPositions;
13
13
  import { BaseController } from "@metamask/base-controller";
14
14
  import { buildDeFiBalancesQuery } from "./build-defi-balances-query.mjs";
15
15
  import { groupDeFiPositionsV6 } from "./group-defi-positions-v6.mjs";
16
16
  const controllerName = 'DeFiPositionsControllerV2';
17
17
  const MESSENGER_EXPOSED_METHODS = ['fetchDeFiPositions'];
18
+ /** Delay between polls while Accounts API reports DeFi indexing in progress. */
19
+ export const DEFAULT_PROCESSING_POLL_INTERVAL_MS = 5000;
20
+ /**
21
+ * Maximum fetch attempts (including the first) while any account still has
22
+ * `processingDefiPositions: true`. After this, the call resolves and keeps
23
+ * prior state for accounts that never became ready.
24
+ */
25
+ export const DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = 5;
26
+ /**
27
+ * @param ms - Milliseconds to wait.
28
+ * @returns A promise that resolves after `ms`.
29
+ */
30
+ function delay(ms) {
31
+ return new Promise((resolve) => setTimeout(resolve, ms));
32
+ }
18
33
  const controllerMetadata = {
19
34
  allDeFiPositionsV2: {
20
35
  includeInStateLogs: false,
@@ -36,6 +51,13 @@ export const getDefaultDeFiPositionsControllerV2State = () => {
36
51
  * Deduplication and freshness are handled by the shared TanStack Query cache on
37
52
  * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass
38
53
  * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.
54
+ *
55
+ * When the API reports `processingDefiPositions` for any account, this
56
+ * controller polls until indexing finishes or the attempt limit is reached.
57
+ * Concurrent calls for the same selected accounts share one in-flight promise
58
+ * so the UI can treat the pending promise as a loading signal. Calls for a
59
+ * different selection start their own fetch and leave any prior poll running,
60
+ * so switching back can join an in-flight fetch for that group.
39
61
  */
40
62
  export class DeFiPositionsControllerV2 extends BaseController {
41
63
  /**
@@ -56,9 +78,16 @@ export class DeFiPositionsControllerV2 extends BaseController {
56
78
  ...state,
57
79
  },
58
80
  });
81
+ _DeFiPositionsControllerV2_instances.add(this);
59
82
  _DeFiPositionsControllerV2_apiClient.set(this, void 0);
60
83
  _DeFiPositionsControllerV2_isEnabled.set(this, void 0);
61
84
  _DeFiPositionsControllerV2_getVsCurrency.set(this, void 0);
85
+ /**
86
+ * In-flight fetches keyed by selected DeFi-queryable account IDs. Concurrent
87
+ * callers for the same selection share a promise; different selections keep
88
+ * independent fetches so fast account switching can join an earlier poll.
89
+ */
90
+ _DeFiPositionsControllerV2_inFlightFetches.set(this, new Map());
62
91
  __classPrivateFieldSet(this, _DeFiPositionsControllerV2_apiClient, apiClient, "f");
63
92
  __classPrivateFieldSet(this, _DeFiPositionsControllerV2_isEnabled, isEnabled, "f");
64
93
  __classPrivateFieldSet(this, _DeFiPositionsControllerV2_getVsCurrency, getVsCurrency, "f");
@@ -67,57 +96,95 @@ export class DeFiPositionsControllerV2 extends BaseController {
67
96
  /**
68
97
  * Fetches DeFi positions for the selected account group. Each account key in
69
98
  * a ready response replaces that account's state (other accounts stay).
70
- * Accounts still indexing (`processingDefiPositions`) are skipped so prior
71
- * state is kept for them. No-ops when disabled or when the group has no
72
- * supported accounts. Caching / spam prevention is handled by the apiClient
73
- * TanStack Query cache (keyed by accounts + query options including
74
- * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.
75
- * pull-to-refresh).
99
+ * Accounts still indexing (`processingDefiPositions`) keep prior state; the
100
+ * method polls (invalidating the balances cache between attempts) until all
101
+ * selected accounts are ready, the attempt limit is reached, or a request
102
+ * fails. Concurrent calls for the same selected accounts share one in-flight
103
+ * promise; calls for a different selection start a new fetch and leave prior
104
+ * polls running so a later switch back can join them. No-ops when disabled
105
+ * or when the group has no supported accounts. Caching / spam prevention is
106
+ * handled by the apiClient TanStack Query cache (keyed by accounts + query
107
+ * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass
108
+ * the cache on the first attempt (e.g. pull-to-refresh).
76
109
  *
77
110
  * @param options - Optional fetch modifiers.
78
- * @param options.forceRefresh - When true, bypass the apiClient cache and
79
- * fetch immediately.
111
+ * @param options.forceRefresh - When true, bypass the apiClient cache on the
112
+ * first attempt and fetch immediately.
113
+ * @returns Resolves when the fetch (and any processing polls) finish.
80
114
  */
81
115
  async fetchDeFiPositions(options) {
82
116
  if (!__classPrivateFieldGet(this, _DeFiPositionsControllerV2_isEnabled, "f").call(this)) {
83
117
  return;
84
118
  }
85
119
  const selectedAccounts = this.messenger.call('AccountTreeController:getAccountsFromSelectedAccountGroup');
86
- const { networks, internalAccountIdByCaip } = buildDeFiBalancesQuery(selectedAccounts);
87
- if (internalAccountIdByCaip.size === 0 || networks.length === 0) {
120
+ const query = buildDeFiBalancesQuery(selectedAccounts);
121
+ const accountIdsKey = [...query.internalAccountIdByCaip.keys()]
122
+ .sort()
123
+ .join('\0');
124
+ const existing = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").get(accountIdsKey);
125
+ if (existing) {
126
+ await existing;
88
127
  return;
89
128
  }
90
- const accountIds = [...internalAccountIdByCaip.keys()];
91
- const vsCurrency = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_getVsCurrency, "f").call(this).toLowerCase();
129
+ const fetchPromise = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_instances, "m", _DeFiPositionsControllerV2_fetchDeFiPositions).call(this, options, query).finally(() => {
130
+ if (__classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").get(accountIdsKey) === fetchPromise) {
131
+ __classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").delete(accountIdsKey);
132
+ }
133
+ });
134
+ __classPrivateFieldGet(this, _DeFiPositionsControllerV2_inFlightFetches, "f").set(accountIdsKey, fetchPromise);
135
+ await fetchPromise;
136
+ }
137
+ }
138
+ _DeFiPositionsControllerV2_apiClient = new WeakMap(), _DeFiPositionsControllerV2_isEnabled = new WeakMap(), _DeFiPositionsControllerV2_getVsCurrency = new WeakMap(), _DeFiPositionsControllerV2_inFlightFetches = new WeakMap(), _DeFiPositionsControllerV2_instances = new WeakSet(), _DeFiPositionsControllerV2_fetchDeFiPositions = async function _DeFiPositionsControllerV2_fetchDeFiPositions(options, { networks, internalAccountIdByCaip }) {
139
+ if (internalAccountIdByCaip.size === 0 || networks.length === 0) {
140
+ return;
141
+ }
142
+ const accountIds = [...internalAccountIdByCaip.keys()];
143
+ const vsCurrency = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_getVsCurrency, "f").call(this).toLowerCase();
144
+ const queryOptions = {
145
+ networks,
146
+ includeDeFiBalances: true,
147
+ forceFetchDeFiPositions: true,
148
+ includePrices: true,
149
+ vsCurrency,
150
+ };
151
+ for (let attempt = 0; attempt < DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS; attempt++) {
152
+ // First attempt respects forceRefresh; later polls always bypass cache
153
+ // so we do not spin on a stale processing snapshot.
154
+ const fetchOptions = {
155
+ ...(options?.forceRefresh || attempt > 0 ? { staleTime: 0 } : {}),
156
+ };
92
157
  try {
93
- const response = await __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.fetchV6MultiAccountBalances(accountIds, {
94
- networks,
95
- includeDeFiBalances: true,
96
- forceFetchDeFiPositions: true,
97
- includePrices: true,
98
- vsCurrency,
99
- }, {
100
- // staleTime: 0 makes TanStack treat the cache as stale for this call.
101
- ...(options?.forceRefresh ? { staleTime: 0 } : {}),
102
- });
103
- // Skip accounts still indexing — their balances are not a valid snapshot.
158
+ const response = await __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.fetchV6MultiAccountBalances(accountIds, queryOptions, fetchOptions);
159
+ const processingAccounts = response.accounts.filter((account) => account.processingDefiPositions);
104
160
  const readyAccounts = response.accounts.filter((account) => !account.processingDefiPositions);
105
- if (readyAccounts.length === 0) {
161
+ if (readyAccounts.length > 0) {
162
+ const positionsByAccount = groupDeFiPositionsV6({ ...response, accounts: readyAccounts }, internalAccountIdByCaip);
163
+ // Last valid response wins per ready account; still-indexing accounts
164
+ // stay untouched.
165
+ this.update((state) => {
166
+ for (const [accountId, positions] of Object.entries(positionsByAccount)) {
167
+ state.allDeFiPositionsV2[accountId] = positions;
168
+ }
169
+ });
170
+ }
171
+ if (processingAccounts.length === 0) {
106
172
  return;
107
173
  }
108
- const positionsByAccount = groupDeFiPositionsV6({ ...response, accounts: readyAccounts }, internalAccountIdByCaip);
109
- // Last valid response wins per ready account; processing / other accounts
110
- // stay untouched.
111
- this.update((state) => {
112
- for (const [accountId, positions] of Object.entries(positionsByAccount)) {
113
- state.allDeFiPositionsV2[accountId] = positions;
114
- }
174
+ const { queryKey } = __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.getV6MultiAccountBalancesQueryOptions(accountIds, queryOptions, fetchOptions);
175
+ await __classPrivateFieldGet(this, _DeFiPositionsControllerV2_apiClient, "f").accounts.queryClient.invalidateQueries({
176
+ queryKey,
115
177
  });
178
+ const isLastAttempt = attempt >= DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS - 1;
179
+ if (isLastAttempt) {
180
+ return;
181
+ }
182
+ await delay(DEFAULT_PROCESSING_POLL_INTERVAL_MS);
116
183
  }
117
184
  catch (error) {
118
185
  console.error('Failed to fetch DeFi positions', error);
186
+ return;
119
187
  }
120
188
  }
121
- }
122
- _DeFiPositionsControllerV2_apiClient = new WeakMap(), _DeFiPositionsControllerV2_isEnabled = new WeakMap(), _DeFiPositionsControllerV2_getVsCurrency = new WeakMap();
189
+ };
123
190
  //# sourceMappingURL=DeFiPositionsControllerV2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsControllerV2.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountTreeControllerGetAccountsFromSelectedAccountGroupAction } from '@metamask/account-tree-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { buildDeFiBalancesQuery } from './build-defi-balances-query.js';\nimport type { DeFiPositionsControllerV2MethodActions } from './DeFiPositionsControllerV2-method-action-types.js';\nimport type { DeFiPositionsByAccount } from './group-defi-positions-v6.js';\nimport { groupDeFiPositionsV6 } from './group-defi-positions-v6.js';\n\nconst controllerName = 'DeFiPositionsControllerV2';\n\nconst MESSENGER_EXPOSED_METHODS = ['fetchDeFiPositions'] as const;\n\nexport type DeFiPositionsControllerV2State = {\n /**\n * DeFi positions keyed by internal MetaMask account ID (`InternalAccount.id`,\n * the same key AssetsController uses). Each account maps to a flat list of\n * protocol groups shown in the DeFi tab, each carrying its own `chainId` for\n * filtering plus the details-page sections embedded inside it. This is\n * exactly the shape the client consumes, so no further transformation is\n * needed on read.\n *\n * Named `allDeFiPositionsV2` (rather than `allDeFiPositions`) so it can live\n * alongside the legacy `DeFiPositionsController` in clients that flatten every\n * controller's state into a single object (e.g. the extension background),\n * without colliding on the shared `allDeFiPositions` key.\n */\n allDeFiPositionsV2: DeFiPositionsByAccount;\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerV2State> = {\n allDeFiPositionsV2: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\nexport const getDefaultDeFiPositionsControllerV2State =\n (): DeFiPositionsControllerV2State => {\n return {\n allDeFiPositionsV2: {},\n };\n };\n\nexport type DeFiPositionsControllerV2GetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerV2State\n>;\n\nexport type DeFiPositionsControllerV2Actions =\n | DeFiPositionsControllerV2GetStateAction\n | DeFiPositionsControllerV2MethodActions;\n\nexport type DeFiPositionsControllerV2StateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n DeFiPositionsControllerV2State\n >;\n\nexport type DeFiPositionsControllerV2Events =\n DeFiPositionsControllerV2StateChangedEvent;\n\n/**\n * The external actions available to the {@link DeFiPositionsControllerV2}.\n */\nexport type AllowedActions =\n AccountTreeControllerGetAccountsFromSelectedAccountGroupAction;\n\n/**\n * The external events available to the {@link DeFiPositionsControllerV2}.\n *\n * None yet — clients must call `fetchDeFiPositions` (and optionally\n * `{ forceRefresh: true }`) on their own triggers. Likely future subscriptions:\n * `AccountTreeController:selectedAccountGroupChange`,\n * `TransactionController:transactionConfirmed`, and `KeyringController:lock`.\n */\nexport type AllowedEvents = never;\n\nexport type DeFiPositionsControllerV2Messenger = Messenger<\n typeof controllerName,\n DeFiPositionsControllerV2Actions | AllowedActions,\n DeFiPositionsControllerV2Events | AllowedEvents\n>;\n\n/**\n * Controller that fetches DeFi positions for the selected account group from\n * the Accounts API (v6 multiaccount balances) and stores them in the shape the\n * client consumes directly.\n *\n * Deduplication and freshness are handled by the shared TanStack Query cache on\n * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass\n * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.\n */\nexport class DeFiPositionsControllerV2 extends BaseController<\n typeof controllerName,\n DeFiPositionsControllerV2State,\n DeFiPositionsControllerV2Messenger\n> {\n readonly #apiClient: ApiPlatformClient;\n\n readonly #isEnabled: () => boolean;\n\n readonly #getVsCurrency: () => string;\n\n /**\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.apiClient - Accounts API client used to fetch balances/positions. Auth is handled by the client.\n * @param options.isEnabled - Returns whether fetching is enabled (default: () => false).\n * @param options.getVsCurrency - Returns the fiat currency for prices (default: () => 'usd').\n * @param options.state - Initial controller state.\n */\n constructor({\n messenger,\n apiClient,\n isEnabled,\n getVsCurrency,\n state,\n }: {\n messenger: DeFiPositionsControllerV2Messenger;\n apiClient: ApiPlatformClient;\n isEnabled: () => boolean;\n getVsCurrency: () => string;\n state?: Partial<DeFiPositionsControllerV2State>;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: {\n ...getDefaultDeFiPositionsControllerV2State(),\n ...state,\n },\n });\n\n this.#apiClient = apiClient;\n this.#isEnabled = isEnabled;\n this.#getVsCurrency = getVsCurrency;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) are skipped so prior\n * state is kept for them. No-ops when disabled or when the group has no\n * supported accounts. Caching / spam prevention is handled by the apiClient\n * TanStack Query cache (keyed by accounts + query options including\n * `vsCurrency`). Pass `{ forceRefresh: true }` to bypass the cache (e.g.\n * pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache and\n * fetch immediately.\n */\n async fetchDeFiPositions(options?: {\n forceRefresh?: boolean;\n }): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const selectedAccounts = this.messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n\n const { networks, internalAccountIdByCaip } =\n buildDeFiBalancesQuery(selectedAccounts);\n\n if (internalAccountIdByCaip.size === 0 || networks.length === 0) {\n return;\n }\n\n const accountIds = [...internalAccountIdByCaip.keys()];\n const vsCurrency = this.#getVsCurrency().toLowerCase();\n\n try {\n const response =\n await this.#apiClient.accounts.fetchV6MultiAccountBalances(\n accountIds,\n {\n networks,\n includeDeFiBalances: true,\n forceFetchDeFiPositions: true,\n includePrices: true,\n vsCurrency,\n },\n {\n // staleTime: 0 makes TanStack treat the cache as stale for this call.\n ...(options?.forceRefresh ? { staleTime: 0 } : {}),\n },\n );\n\n // Skip accounts still indexing — their balances are not a valid snapshot.\n const readyAccounts = response.accounts.filter(\n (account) => !account.processingDefiPositions,\n );\n if (readyAccounts.length === 0) {\n return;\n }\n\n const positionsByAccount = groupDeFiPositionsV6(\n { ...response, accounts: readyAccounts },\n internalAccountIdByCaip,\n );\n\n // Last valid response wins per ready account; processing / other accounts\n // stay untouched.\n this.update((state) => {\n for (const [accountId, positions] of Object.entries(\n positionsByAccount,\n )) {\n state.allDeFiPositionsV2[accountId] = positions;\n }\n });\n } catch (error) {\n console.error('Failed to fetch DeFi positions', error);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"DeFiPositionsControllerV2.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsControllerV2.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountTreeControllerGetAccountsFromSelectedAccountGroupAction } from '@metamask/account-tree-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangedEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { buildDeFiBalancesQuery } from './build-defi-balances-query.js';\nimport type { DeFiBalancesQuery } from './build-defi-balances-query.js';\nimport type { DeFiPositionsControllerV2MethodActions } from './DeFiPositionsControllerV2-method-action-types.js';\nimport type { DeFiPositionsByAccount } from './group-defi-positions-v6.js';\nimport { groupDeFiPositionsV6 } from './group-defi-positions-v6.js';\n\nconst controllerName = 'DeFiPositionsControllerV2';\n\nconst MESSENGER_EXPOSED_METHODS = ['fetchDeFiPositions'] as const;\n\n/** Delay between polls while Accounts API reports DeFi indexing in progress. */\nexport const DEFAULT_PROCESSING_POLL_INTERVAL_MS = 5_000;\n\n/**\n * Maximum fetch attempts (including the first) while any account still has\n * `processingDefiPositions: true`. After this, the call resolves and keeps\n * prior state for accounts that never became ready.\n */\nexport const DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS = 5;\n\n/**\n * @param ms - Milliseconds to wait.\n * @returns A promise that resolves after `ms`.\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport type DeFiPositionsControllerV2State = {\n /**\n * DeFi positions keyed by internal MetaMask account ID (`InternalAccount.id`,\n * the same key AssetsController uses). Each account maps to a flat list of\n * protocol groups shown in the DeFi tab, each carrying its own `chainId` for\n * filtering plus the details-page sections embedded inside it. This is\n * exactly the shape the client consumes, so no further transformation is\n * needed on read.\n *\n * Named `allDeFiPositionsV2` (rather than `allDeFiPositions`) so it can live\n * alongside the legacy `DeFiPositionsController` in clients that flatten every\n * controller's state into a single object (e.g. the extension background),\n * without colliding on the shared `allDeFiPositions` key.\n */\n allDeFiPositionsV2: DeFiPositionsByAccount;\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerV2State> = {\n allDeFiPositionsV2: {\n includeInStateLogs: false,\n persist: true,\n includeInDebugSnapshot: false,\n usedInUi: true,\n },\n};\n\nexport const getDefaultDeFiPositionsControllerV2State =\n (): DeFiPositionsControllerV2State => {\n return {\n allDeFiPositionsV2: {},\n };\n };\n\nexport type DeFiPositionsControllerV2GetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerV2State\n>;\n\nexport type DeFiPositionsControllerV2Actions =\n | DeFiPositionsControllerV2GetStateAction\n | DeFiPositionsControllerV2MethodActions;\n\nexport type DeFiPositionsControllerV2StateChangedEvent =\n ControllerStateChangedEvent<\n typeof controllerName,\n DeFiPositionsControllerV2State\n >;\n\nexport type DeFiPositionsControllerV2Events =\n DeFiPositionsControllerV2StateChangedEvent;\n\n/**\n * The external actions available to the {@link DeFiPositionsControllerV2}.\n */\nexport type AllowedActions =\n AccountTreeControllerGetAccountsFromSelectedAccountGroupAction;\n\n/**\n * The external events available to the {@link DeFiPositionsControllerV2}.\n *\n * None yet — clients must call `fetchDeFiPositions` (and optionally\n * `{ forceRefresh: true }`) on their own triggers. Likely future subscriptions:\n * `AccountTreeController:selectedAccountGroupChange`,\n * `TransactionController:transactionConfirmed`, and `KeyringController:lock`.\n */\nexport type AllowedEvents = never;\n\nexport type DeFiPositionsControllerV2Messenger = Messenger<\n typeof controllerName,\n DeFiPositionsControllerV2Actions | AllowedActions,\n DeFiPositionsControllerV2Events | AllowedEvents\n>;\n\n/**\n * Controller that fetches DeFi positions for the selected account group from\n * the Accounts API (v6 multiaccount balances) and stores them in the shape the\n * client consumes directly.\n *\n * Deduplication and freshness are handled by the shared TanStack Query cache on\n * {@link ApiPlatformClient} (balances default `staleTime` is 1 minute). Pass\n * `{ forceRefresh: true }` to bypass that cache for pull-to-refresh.\n *\n * When the API reports `processingDefiPositions` for any account, this\n * controller polls until indexing finishes or the attempt limit is reached.\n * Concurrent calls for the same selected accounts share one in-flight promise\n * so the UI can treat the pending promise as a loading signal. Calls for a\n * different selection start their own fetch and leave any prior poll running,\n * so switching back can join an in-flight fetch for that group.\n */\nexport class DeFiPositionsControllerV2 extends BaseController<\n typeof controllerName,\n DeFiPositionsControllerV2State,\n DeFiPositionsControllerV2Messenger\n> {\n readonly #apiClient: ApiPlatformClient;\n\n readonly #isEnabled: () => boolean;\n\n readonly #getVsCurrency: () => string;\n\n /**\n * In-flight fetches keyed by selected DeFi-queryable account IDs. Concurrent\n * callers for the same selection share a promise; different selections keep\n * independent fetches so fast account switching can join an earlier poll.\n */\n readonly #inFlightFetches = new Map<string, Promise<void>>();\n\n /**\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.apiClient - Accounts API client used to fetch balances/positions. Auth is handled by the client.\n * @param options.isEnabled - Returns whether fetching is enabled (default: () => false).\n * @param options.getVsCurrency - Returns the fiat currency for prices (default: () => 'usd').\n * @param options.state - Initial controller state.\n */\n constructor({\n messenger,\n apiClient,\n isEnabled,\n getVsCurrency,\n state,\n }: {\n messenger: DeFiPositionsControllerV2Messenger;\n apiClient: ApiPlatformClient;\n isEnabled: () => boolean;\n getVsCurrency: () => string;\n state?: Partial<DeFiPositionsControllerV2State>;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: {\n ...getDefaultDeFiPositionsControllerV2State(),\n ...state,\n },\n });\n\n this.#apiClient = apiClient;\n this.#isEnabled = isEnabled;\n this.#getVsCurrency = getVsCurrency;\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Fetches DeFi positions for the selected account group. Each account key in\n * a ready response replaces that account's state (other accounts stay).\n * Accounts still indexing (`processingDefiPositions`) keep prior state; the\n * method polls (invalidating the balances cache between attempts) until all\n * selected accounts are ready, the attempt limit is reached, or a request\n * fails. Concurrent calls for the same selected accounts share one in-flight\n * promise; calls for a different selection start a new fetch and leave prior\n * polls running so a later switch back can join them. No-ops when disabled\n * or when the group has no supported accounts. Caching / spam prevention is\n * handled by the apiClient TanStack Query cache (keyed by accounts + query\n * options including `vsCurrency`). Pass `{ forceRefresh: true }` to bypass\n * the cache on the first attempt (e.g. pull-to-refresh).\n *\n * @param options - Optional fetch modifiers.\n * @param options.forceRefresh - When true, bypass the apiClient cache on the\n * first attempt and fetch immediately.\n * @returns Resolves when the fetch (and any processing polls) finish.\n */\n async fetchDeFiPositions(options?: {\n forceRefresh?: boolean;\n }): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const selectedAccounts = this.messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n const query = buildDeFiBalancesQuery(selectedAccounts);\n const accountIdsKey = [...query.internalAccountIdByCaip.keys()]\n .sort()\n .join('\\0');\n\n const existing = this.#inFlightFetches.get(accountIdsKey);\n if (existing) {\n await existing;\n return;\n }\n\n const fetchPromise = this.#fetchDeFiPositions(options, query).finally(\n () => {\n if (this.#inFlightFetches.get(accountIdsKey) === fetchPromise) {\n this.#inFlightFetches.delete(accountIdsKey);\n }\n },\n );\n this.#inFlightFetches.set(accountIdsKey, fetchPromise);\n\n await fetchPromise;\n }\n\n async #fetchDeFiPositions(\n options: { forceRefresh?: boolean } | undefined,\n { networks, internalAccountIdByCaip }: DeFiBalancesQuery,\n ): Promise<void> {\n if (internalAccountIdByCaip.size === 0 || networks.length === 0) {\n return;\n }\n\n const accountIds = [...internalAccountIdByCaip.keys()];\n const vsCurrency = this.#getVsCurrency().toLowerCase();\n const queryOptions = {\n networks,\n includeDeFiBalances: true,\n forceFetchDeFiPositions: true,\n includePrices: true,\n vsCurrency,\n };\n\n for (\n let attempt = 0;\n attempt < DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS;\n attempt++\n ) {\n // First attempt respects forceRefresh; later polls always bypass cache\n // so we do not spin on a stale processing snapshot.\n const fetchOptions = {\n ...(options?.forceRefresh || attempt > 0 ? { staleTime: 0 } : {}),\n };\n\n try {\n const response =\n await this.#apiClient.accounts.fetchV6MultiAccountBalances(\n accountIds,\n queryOptions,\n fetchOptions,\n );\n\n const processingAccounts = response.accounts.filter(\n (account) => account.processingDefiPositions,\n );\n const readyAccounts = response.accounts.filter(\n (account) => !account.processingDefiPositions,\n );\n\n if (readyAccounts.length > 0) {\n const positionsByAccount = groupDeFiPositionsV6(\n { ...response, accounts: readyAccounts },\n internalAccountIdByCaip,\n );\n\n // Last valid response wins per ready account; still-indexing accounts\n // stay untouched.\n this.update((state) => {\n for (const [accountId, positions] of Object.entries(\n positionsByAccount,\n )) {\n state.allDeFiPositionsV2[accountId] = positions;\n }\n });\n }\n\n if (processingAccounts.length === 0) {\n return;\n }\n\n const { queryKey } =\n this.#apiClient.accounts.getV6MultiAccountBalancesQueryOptions(\n accountIds,\n queryOptions,\n fetchOptions,\n );\n await this.#apiClient.accounts.queryClient.invalidateQueries({\n queryKey,\n });\n\n const isLastAttempt =\n attempt >= DEFAULT_PROCESSING_POLL_MAX_ATTEMPTS - 1;\n if (isLastAttempt) {\n return;\n }\n\n await delay(DEFAULT_PROCESSING_POLL_INTERVAL_MS);\n } catch (error) {\n console.error('Failed to fetch DeFi positions', error);\n return;\n }\n }\n }\n}\n"]}
@@ -66,8 +66,8 @@ type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAcc
66
66
  */
67
67
  export type MultichainAssetsControllerMessenger = Messenger<typeof controllerName, MultichainAssetsControllerActions | AllowedActions, MultichainAssetsControllerEvents | AllowedEvents>;
68
68
  declare const MultichainAssetsController_base: (abstract new (...args: any[]) => {
69
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
70
- "__#17@#intervalLength": number | undefined;
69
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
70
+ "__#20@#intervalLength": number | undefined;
71
71
  setIntervalLength(intervalLength: number): void;
72
72
  getIntervalLength(): number | undefined;
73
73
  _startPolling(input: null): void;
@@ -66,8 +66,8 @@ type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAcc
66
66
  */
67
67
  export type MultichainAssetsControllerMessenger = Messenger<typeof controllerName, MultichainAssetsControllerActions | AllowedActions, MultichainAssetsControllerEvents | AllowedEvents>;
68
68
  declare const MultichainAssetsController_base: (abstract new (...args: any[]) => {
69
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
70
- "__#17@#intervalLength": number | undefined;
69
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
70
+ "__#20@#intervalLength": number | undefined;
71
71
  setIntervalLength(intervalLength: number): void;
72
72
  getIntervalLength(): number | undefined;
73
73
  _startPolling(input: null): void;
@@ -75,8 +75,8 @@ export type ConversionRatesWithMarketData = {
75
75
  conversionRates: Record<CaipAssetType, Record<CaipAssetType, UnifiedAssetConversion | null>>;
76
76
  };
77
77
  declare const MultichainAssetsRatesController_base: (abstract new (...args: any[]) => {
78
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
79
- "__#17@#intervalLength": number | undefined;
78
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
79
+ "__#20@#intervalLength": number | undefined;
80
80
  setIntervalLength(intervalLength: number): void;
81
81
  getIntervalLength(): number | undefined;
82
82
  _startPolling(input: MultichainAssetsRatesPollingInput): void;
@@ -75,8 +75,8 @@ export type ConversionRatesWithMarketData = {
75
75
  conversionRates: Record<CaipAssetType, Record<CaipAssetType, UnifiedAssetConversion | null>>;
76
76
  };
77
77
  declare const MultichainAssetsRatesController_base: (abstract new (...args: any[]) => {
78
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
79
- "__#17@#intervalLength": number | undefined;
78
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
79
+ "__#20@#intervalLength": number | undefined;
80
80
  setIntervalLength(intervalLength: number): void;
81
81
  getIntervalLength(): number | undefined;
82
82
  _startPolling(input: MultichainAssetsRatesPollingInput): void;
@@ -106,8 +106,8 @@ export declare const parseAssetType: (assetType: string) => [
106
106
  boolean
107
107
  ] | null;
108
108
  declare const TokenBalancesController_base: (abstract new (...args: any[]) => {
109
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
110
- "__#17@#intervalLength": number | undefined;
109
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
110
+ "__#20@#intervalLength": number | undefined;
111
111
  setIntervalLength(intervalLength: number): void;
112
112
  getIntervalLength(): number | undefined;
113
113
  _startPolling(input: {
@@ -106,8 +106,8 @@ export declare const parseAssetType: (assetType: string) => [
106
106
  boolean
107
107
  ] | null;
108
108
  declare const TokenBalancesController_base: (abstract new (...args: any[]) => {
109
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
110
- "__#17@#intervalLength": number | undefined;
109
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
110
+ "__#20@#intervalLength": number | undefined;
111
111
  setIntervalLength(intervalLength: number): void;
112
112
  getIntervalLength(): number | undefined;
113
113
  _startPolling(input: {
@@ -32,8 +32,8 @@ type TokenDetectionPollingInput = {
32
32
  address: string;
33
33
  };
34
34
  declare const TokenDetectionController_base: (abstract new (...args: any[]) => {
35
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
36
- "__#17@#intervalLength": number | undefined;
35
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
36
+ "__#20@#intervalLength": number | undefined;
37
37
  setIntervalLength(intervalLength: number): void;
38
38
  getIntervalLength(): number | undefined;
39
39
  _startPolling(input: TokenDetectionPollingInput): void;
@@ -32,8 +32,8 @@ type TokenDetectionPollingInput = {
32
32
  address: string;
33
33
  };
34
34
  declare const TokenDetectionController_base: (abstract new (...args: any[]) => {
35
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
36
- "__#17@#intervalLength": number | undefined;
35
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
36
+ "__#20@#intervalLength": number | undefined;
37
37
  setIntervalLength(intervalLength: number): void;
38
38
  getIntervalLength(): number | undefined;
39
39
  _startPolling(input: TokenDetectionPollingInput): void;
@@ -39,8 +39,8 @@ type TokenListPollingInput = {
39
39
  chainId: Hex;
40
40
  };
41
41
  declare const TokenListController_base: (abstract new (...args: any[]) => {
42
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
43
- "__#17@#intervalLength": number | undefined;
42
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
43
+ "__#20@#intervalLength": number | undefined;
44
44
  setIntervalLength(intervalLength: number): void;
45
45
  getIntervalLength(): number | undefined;
46
46
  _startPolling(input: TokenListPollingInput): void;
@@ -39,8 +39,8 @@ type TokenListPollingInput = {
39
39
  chainId: Hex;
40
40
  };
41
41
  declare const TokenListController_base: (abstract new (...args: any[]) => {
42
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
43
- "__#17@#intervalLength": number | undefined;
42
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
43
+ "__#20@#intervalLength": number | undefined;
44
44
  setIntervalLength(intervalLength: number): void;
45
45
  getIntervalLength(): number | undefined;
46
46
  _startPolling(input: TokenListPollingInput): void;
@@ -117,8 +117,8 @@ export type TokenRatesPollingInput = {
117
117
  chainIds: Hex[];
118
118
  };
119
119
  declare const TokenRatesController_base: (abstract new (...args: any[]) => {
120
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
121
- "__#17@#intervalLength": number | undefined;
120
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
121
+ "__#20@#intervalLength": number | undefined;
122
122
  setIntervalLength(intervalLength: number): void;
123
123
  getIntervalLength(): number | undefined;
124
124
  _startPolling(input: TokenRatesPollingInput): void;
@@ -117,8 +117,8 @@ export type TokenRatesPollingInput = {
117
117
  chainIds: Hex[];
118
118
  };
119
119
  declare const TokenRatesController_base: (abstract new (...args: any[]) => {
120
- readonly "__#17@#intervalIds": Record<string, NodeJS.Timeout>;
121
- "__#17@#intervalLength": number | undefined;
120
+ readonly "__#20@#intervalIds": Record<string, NodeJS.Timeout>;
121
+ "__#20@#intervalLength": number | undefined;
122
122
  setIntervalLength(intervalLength: number): void;
123
123
  getIntervalLength(): number | undefined;
124
124
  _startPolling(input: TokenRatesPollingInput): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/assets-controllers",
3
- "version": "110.0.1-preview-a1d0bbfa8",
3
+ "version": "110.0.2-preview-f48a87ac7",
4
4
  "description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -76,7 +76,7 @@
76
76
  "@metamask/metamask-eth-abis": "^3.1.1",
77
77
  "@metamask/multichain-account-service": "^13.0.0",
78
78
  "@metamask/network-controller": "^34.0.0",
79
- "@metamask/network-enablement-controller": "^5.6.0",
79
+ "@metamask/network-enablement-controller": "^6.0.0",
80
80
  "@metamask/permission-controller": "^13.1.1",
81
81
  "@metamask/phishing-controller": "^17.3.0",
82
82
  "@metamask/polling-controller": "^16.0.8",