@metamask-previews/assets-controllers 56.0.0-preview-9d7b1fa6 → 56.0.0-preview-20c091f4

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 (33) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -0
  3. package/dist/DeFiPositionsController/DeFiPositionsController.cjs +70 -23
  4. package/dist/DeFiPositionsController/DeFiPositionsController.cjs.map +1 -1
  5. package/dist/DeFiPositionsController/DeFiPositionsController.d.cts +32 -15
  6. package/dist/DeFiPositionsController/DeFiPositionsController.d.cts.map +1 -1
  7. package/dist/DeFiPositionsController/DeFiPositionsController.d.mts +32 -15
  8. package/dist/DeFiPositionsController/DeFiPositionsController.d.mts.map +1 -1
  9. package/dist/DeFiPositionsController/DeFiPositionsController.mjs +70 -23
  10. package/dist/DeFiPositionsController/DeFiPositionsController.mjs.map +1 -1
  11. package/dist/DeFiPositionsController/fetch-positions.cjs +4 -5
  12. package/dist/DeFiPositionsController/fetch-positions.cjs.map +1 -1
  13. package/dist/DeFiPositionsController/fetch-positions.d.cts +2 -3
  14. package/dist/DeFiPositionsController/fetch-positions.d.cts.map +1 -1
  15. package/dist/DeFiPositionsController/fetch-positions.d.mts +2 -3
  16. package/dist/DeFiPositionsController/fetch-positions.d.mts.map +1 -1
  17. package/dist/DeFiPositionsController/fetch-positions.mjs +3 -4
  18. package/dist/DeFiPositionsController/fetch-positions.mjs.map +1 -1
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.cts.map +1 -1
  22. package/dist/index.d.mts +1 -1
  23. package/dist/index.d.mts.map +1 -1
  24. package/dist/index.mjs.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/DeFiPositionsController/mocks/mock-responses.cjs +0 -600
  27. package/dist/DeFiPositionsController/mocks/mock-responses.cjs.map +0 -1
  28. package/dist/DeFiPositionsController/mocks/mock-responses.d.cts +0 -22
  29. package/dist/DeFiPositionsController/mocks/mock-responses.d.cts.map +0 -1
  30. package/dist/DeFiPositionsController/mocks/mock-responses.d.mts +0 -22
  31. package/dist/DeFiPositionsController/mocks/mock-responses.d.mts.map +0 -1
  32. package/dist/DeFiPositionsController/mocks/mock-responses.mjs +0 -597
  33. package/dist/DeFiPositionsController/mocks/mock-responses.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add a new DeFiPositionsController that keeps an update list of DeFi positions for EVM accounts ([#5400](https://github.com/MetaMask/core/pull/5400))
13
+
10
14
  ## [56.0.0]
11
15
 
12
16
  ### Changed
package/README.md CHANGED
@@ -19,6 +19,7 @@ This package features the following controllers:
19
19
  - [**CollectibleDetectionController**](src/CollectibleDetectionController.ts) keeps a periodically updated list of ERC-721 tokens assigned to the currently selected address.
20
20
  - [**CollectiblesController**](src/CollectiblesController.ts) tracks ERC-721 and ERC-1155 tokens assigned to the currently selected address, using OpenSea to retrieve token information.
21
21
  - [**CurrencyRateController**](src/CurrencyRateController.ts) keeps a periodically updated value of the exchange rate from the currently selected "native" currency to another (handling testnet tokens specially).
22
+ - [**DeFiPositionsController**](src/DeFiPositionsController/DeFiPositionsController.ts.ts) keeps a periodically updated value of the DeFi positions for the owner EVM addresses.
22
23
  - [**RatesController**](src/RatesController/RatesController.ts) keeps a periodically updated value for the exchange rates for different cryptocurrencies. The difference between the `RatesController` and `CurrencyRateController` is that the second one is coupled to the `NetworksController` and is EVM specific, whilst the first one can handle different blockchain currencies like BTC and SOL.
23
24
  - [**TokenBalancesController**](src/TokenBalancesController.ts) keeps a periodically updated set of balances for the current set of ERC-20 tokens.
24
25
  - [**TokenDetectionController**](src/TokenDetectionController.ts) keeps a periodically updated list of ERC-20 tokens assigned to the currently selected address.
@@ -10,12 +10,15 @@ 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 _DeFiPositionsController_instances, _DeFiPositionsController_fetchPositions, _DeFiPositionsController_updateAccountPositions;
13
+ var _DeFiPositionsController_instances, _DeFiPositionsController_fetchPositions, _DeFiPositionsController_isEnabled, _DeFiPositionsController_updateAccountPositions, _DeFiPositionsController_fetchAccountPositions;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.DeFiPositionsController = exports.getDefaultDefiPositionsControllerState = void 0;
16
- const base_controller_1 = require("@metamask/base-controller");
16
+ const polling_controller_1 = require("@metamask/polling-controller");
17
17
  const fetch_positions_1 = require("./fetch-positions.cjs");
18
18
  const group_positions_1 = require("./group-positions.cjs");
19
+ const assetsUtil_1 = require("../assetsUtil.cjs");
20
+ const TEN_MINUTES_IN_MS = 60000;
21
+ const FETCH_POSITIONS_BATCH_SIZE = 10;
19
22
  const controllerName = 'DeFiPositionsController';
20
23
  const controllerMetadata = {
21
24
  allDeFiPositions: {
@@ -32,48 +35,92 @@ exports.getDefaultDefiPositionsControllerState = getDefaultDefiPositionsControll
32
35
  /**
33
36
  * Controller that stores assets and exposes convenience methods
34
37
  */
35
- class DeFiPositionsController extends base_controller_1.BaseController {
38
+ class DeFiPositionsController extends (0, polling_controller_1.StaticIntervalPollingController)() {
36
39
  /**
37
40
  * Tokens controller options
38
41
  *
39
42
  * @param options - Constructor options.
40
43
  * @param options.messenger - The controller messenger.
41
- * @param options.state - Initial state to set on this controller.
42
- * @param options.apiUrl - Override for theAPI URL to use for fetching DeFi positions.
44
+ * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)
43
45
  */
44
- constructor({ messenger, state, apiUrl, }) {
46
+ constructor({ messenger, isEnabled = () => true, }) {
45
47
  super({
46
48
  name: controllerName,
47
49
  metadata: controllerMetadata,
48
50
  messenger,
49
- state: {
50
- ...(0, exports.getDefaultDefiPositionsControllerState)(),
51
- ...state,
52
- },
51
+ state: (0, exports.getDefaultDefiPositionsControllerState)(),
53
52
  });
54
53
  _DeFiPositionsController_instances.add(this);
55
54
  _DeFiPositionsController_fetchPositions.set(this, void 0);
56
- __classPrivateFieldSet(this, _DeFiPositionsController_fetchPositions, (0, fetch_positions_1.buildPositionFetcher)(apiUrl), "f");
57
- this.messagingSystem.subscribe('AccountsController:selectedAccountChange', async (selectedAccount) => {
58
- await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, selectedAccount.address);
55
+ _DeFiPositionsController_isEnabled.set(this, void 0);
56
+ this.setIntervalLength(TEN_MINUTES_IN_MS);
57
+ __classPrivateFieldSet(this, _DeFiPositionsController_fetchPositions, (0, fetch_positions_1.buildPositionFetcher)(), "f");
58
+ __classPrivateFieldSet(this, _DeFiPositionsController_isEnabled, isEnabled, "f");
59
+ this.messagingSystem.subscribe('KeyringController:unlock', async () => {
60
+ this.startPolling(null);
61
+ });
62
+ this.messagingSystem.subscribe('KeyringController:lock', () => {
63
+ this.stopAllPolling();
64
+ });
65
+ this.messagingSystem.subscribe('TransactionController:transactionConfirmed', async (transactionMeta) => {
66
+ if (!__classPrivateFieldGet(this, _DeFiPositionsController_isEnabled, "f").call(this)) {
67
+ return;
68
+ }
69
+ await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, transactionMeta.txParams.from);
59
70
  });
60
- this.messagingSystem.subscribe('NetworkController:stateChange', async () => {
61
- const { address } = this.messagingSystem.call('AccountsController:getSelectedAccount');
62
- if (address) {
63
- await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, address);
71
+ this.messagingSystem.subscribe('AccountsController:accountAdded', async (account) => {
72
+ if (!__classPrivateFieldGet(this, _DeFiPositionsController_isEnabled, "f").call(this) || !account.type.startsWith('eip155:')) {
73
+ return;
64
74
  }
75
+ await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, account.address);
76
+ });
77
+ }
78
+ async _executePoll() {
79
+ if (!__classPrivateFieldGet(this, _DeFiPositionsController_isEnabled, "f").call(this)) {
80
+ return;
81
+ }
82
+ const accounts = this.messagingSystem.call('AccountsController:listAccounts');
83
+ const results = await (0, assetsUtil_1.reduceInBatchesSerially)({
84
+ initialResult: [],
85
+ values: accounts,
86
+ batchSize: FETCH_POSITIONS_BATCH_SIZE,
87
+ eachBatch: async (workingResult, batch) => {
88
+ const batchResults = (await Promise.all(batch.map(async ({ address: accountAddress, type }) => {
89
+ if (type.startsWith('eip155:')) {
90
+ const positions = await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_fetchAccountPositions).call(this, accountAddress);
91
+ return {
92
+ accountAddress,
93
+ positions,
94
+ };
95
+ }
96
+ return undefined;
97
+ }))).filter(Boolean);
98
+ return [...workingResult, ...batchResults];
99
+ },
100
+ });
101
+ const allDefiPositions = results.reduce((acc, { accountAddress, positions }) => {
102
+ acc[accountAddress] = positions;
103
+ return acc;
104
+ }, {});
105
+ this.update((state) => {
106
+ state.allDeFiPositions = allDefiPositions;
65
107
  });
66
108
  }
67
109
  }
68
110
  exports.DeFiPositionsController = DeFiPositionsController;
69
- _DeFiPositionsController_fetchPositions = new WeakMap(), _DeFiPositionsController_instances = new WeakSet(), _DeFiPositionsController_updateAccountPositions = async function _DeFiPositionsController_updateAccountPositions(accountAddress) {
70
- this.update((state) => {
71
- state.allDeFiPositions[accountAddress] = null;
72
- });
73
- const defiPositionsResponse = await __classPrivateFieldGet(this, _DeFiPositionsController_fetchPositions, "f").call(this, accountAddress);
74
- const accountPositionsPerChain = (0, group_positions_1.groupPositions)(defiPositionsResponse);
111
+ _DeFiPositionsController_fetchPositions = new WeakMap(), _DeFiPositionsController_isEnabled = new WeakMap(), _DeFiPositionsController_instances = new WeakSet(), _DeFiPositionsController_updateAccountPositions = async function _DeFiPositionsController_updateAccountPositions(accountAddress) {
112
+ const accountPositionsPerChain = await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_fetchAccountPositions).call(this, accountAddress);
75
113
  this.update((state) => {
76
114
  state.allDeFiPositions[accountAddress] = accountPositionsPerChain;
77
115
  });
116
+ }, _DeFiPositionsController_fetchAccountPositions = async function _DeFiPositionsController_fetchAccountPositions(accountAddress) {
117
+ try {
118
+ const defiPositionsResponse = await __classPrivateFieldGet(this, _DeFiPositionsController_fetchPositions, "f").call(this, accountAddress);
119
+ return (0, group_positions_1.groupPositions)(defiPositionsResponse);
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ }
122
+ catch (error) {
123
+ return null;
124
+ }
78
125
  };
79
126
  //# sourceMappingURL=DeFiPositionsController.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsController.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAUA,+DAA2D;AAK3D,2DAAyD;AACzD,2DAA0E;AAE1E,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAWjD,MAAM,kBAAkB,GAAgD;IACtE,gBAAgB,EAAE;QAChB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEK,MAAM,sCAAsC,GACjD,GAAiC,EAAE;IACjC,OAAO;QACL,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC,CAAC;AALS,QAAA,sCAAsC,0CAK/C;AA0CJ;;GAEG;AACH,MAAa,uBAAwB,SAAQ,gCAI5C;IAKC;;;;;;;OAOG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,MAAM,GAKP;QACC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,kBAAkB;YAC5B,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,IAAA,8CAAsC,GAAE;gBAC3C,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QA7BI,0DAE4B;QA6BnC,uBAAA,IAAI,2CAAmB,IAAA,sCAAoB,EAAC,MAAM,CAAC,MAAA,CAAC;QAEpD,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,0CAA0C,EAC1C,KAAK,EAAE,eAAe,EAAE,EAAE;YACxB,MAAM,uBAAA,IAAI,2FAAwB,MAA5B,IAAI,EAAyB,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,+BAA+B,EAC/B,KAAK,IAAI,EAAE;YACT,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3C,uCAAuC,CACxC,CAAC;YAEF,IAAI,OAAO,EAAE;gBACX,MAAM,uBAAA,IAAI,2FAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CAAC;aAC7C;QACH,CAAC,CACF,CAAC;IACJ,CAAC;CAeF;AAxED,0DAwEC;+JAbC,KAAK,0DAAyB,cAAsB;IAClD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,MAAM,qBAAqB,GAAG,MAAM,uBAAA,IAAI,+CAAgB,MAApB,IAAI,EAAiB,cAAc,CAAC,CAAC;IAEzE,MAAM,wBAAwB,GAAG,IAAA,gCAAc,EAAC,qBAAqB,CAAC,CAAC;IAEvE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,GAAG,wBAAwB,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { NetworkControllerStateChangeEvent } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { DefiPositionResponse } from './fetch-positions';\nimport { buildPositionFetcher } from './fetch-positions';\nimport { groupPositions, type GroupedPositions } from './group-positions';\n\nconst controllerName = 'DeFiPositionsController';\n\nexport type DeFiPositionsControllerState = {\n /**\n * Object containing DeFi positions per account and network\n */\n allDeFiPositions: {\n [accountAddress: string]: { [chain: Hex]: GroupedPositions } | null;\n };\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerState> = {\n allDeFiPositions: {\n persist: false,\n anonymous: false,\n },\n};\n\nexport const getDefaultDefiPositionsControllerState =\n (): DeFiPositionsControllerState => {\n return {\n allDeFiPositions: {},\n };\n };\n\nexport type DeFiPositionsControllerActions =\n DeFiPositionsControllerGetStateAction;\n\nexport type DeFiPositionsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerState\n>;\n\nexport type DeFiPositionsControllerEvents =\n DeFiPositionsControllerStateChangeEvent;\n\nexport type DeFiPositionsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n DeFiPositionsControllerState\n >;\n\n/**\n * The external actions available to the {@link DeFiPositionsController}.\n */\nexport type AllowedActions = AccountsControllerGetSelectedAccountAction;\n\n/**\n * The external events available to the {@link DeFiPositionsController}.\n */\nexport type AllowedEvents =\n | NetworkControllerStateChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n DeFiPositionsControllerActions | AllowedActions,\n DeFiPositionsControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class DeFiPositionsController extends BaseController<\n typeof controllerName,\n DeFiPositionsControllerState,\n DeFiPositionsControllerMessenger\n> {\n readonly #fetchPositions: (\n accountAddress: string,\n ) => Promise<DefiPositionResponse[]>;\n\n /**\n * Tokens controller options\n *\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.apiUrl - Override for theAPI URL to use for fetching DeFi positions.\n */\n constructor({\n messenger,\n state,\n apiUrl,\n }: {\n messenger: DeFiPositionsControllerMessenger;\n state?: Partial<DeFiPositionsControllerState>;\n apiUrl?: string;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: {\n ...getDefaultDefiPositionsControllerState(),\n ...state,\n },\n });\n\n this.#fetchPositions = buildPositionFetcher(apiUrl);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n async (selectedAccount) => {\n await this.#updateAccountPositions(selectedAccount.address);\n },\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n async () => {\n const { address } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n if (address) {\n await this.#updateAccountPositions(address);\n }\n },\n );\n }\n\n async #updateAccountPositions(accountAddress: string) {\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = null;\n });\n\n const defiPositionsResponse = await this.#fetchPositions(accountAddress);\n\n const accountPositionsPerChain = groupPositions(defiPositionsResponse);\n\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = accountPositionsPerChain;\n });\n }\n}\n"]}
1
+ {"version":3,"file":"DeFiPositionsController.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { KeyringControllerUnlockEvent } from '@metamask/keyring-controller';\nimport type { KeyringControllerLockEvent } from '@metamask/keyring-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { DefiPositionResponse } from './fetch-positions';\nimport { buildPositionFetcher } from './fetch-positions';\nimport { groupPositions, type GroupedPositions } from './group-positions';\nimport { reduceInBatchesSerially } from '../assetsUtil';\n\nconst TEN_MINUTES_IN_MS = 60_000;\n\nconst FETCH_POSITIONS_BATCH_SIZE = 10;\n\nconst controllerName = 'DeFiPositionsController';\n\ntype GroupedPositionsPerChain = {\n [chain: Hex]: GroupedPositions;\n};\n\nexport type DeFiPositionsControllerState = {\n /**\n * Object containing DeFi positions per account and network\n */\n allDeFiPositions: {\n [accountAddress: string]: GroupedPositionsPerChain | null;\n };\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerState> = {\n allDeFiPositions: {\n persist: false,\n anonymous: false,\n },\n};\n\nexport const getDefaultDefiPositionsControllerState =\n (): DeFiPositionsControllerState => {\n return {\n allDeFiPositions: {},\n };\n };\n\nexport type DeFiPositionsControllerActions =\n DeFiPositionsControllerGetStateAction;\n\nexport type DeFiPositionsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerState\n>;\n\nexport type DeFiPositionsControllerEvents =\n DeFiPositionsControllerStateChangeEvent;\n\nexport type DeFiPositionsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n DeFiPositionsControllerState\n >;\n\n/**\n * The external actions available to the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerAllowedActions =\n AccountsControllerListAccountsAction;\n\n/**\n * The external events available to the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerAllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | TransactionControllerTransactionConfirmedEvent\n | AccountsControllerAccountAddedEvent;\n\n/**\n * The messenger of the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n DeFiPositionsControllerActions | DeFiPositionsControllerAllowedActions,\n DeFiPositionsControllerEvents | DeFiPositionsControllerAllowedEvents,\n DeFiPositionsControllerAllowedActions['type'],\n DeFiPositionsControllerAllowedEvents['type']\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class DeFiPositionsController extends StaticIntervalPollingController()<\n typeof controllerName,\n DeFiPositionsControllerState,\n DeFiPositionsControllerMessenger\n> {\n readonly #fetchPositions: (\n accountAddress: string,\n ) => Promise<DefiPositionResponse[]>;\n\n readonly #isEnabled: () => boolean;\n\n /**\n * Tokens controller options\n *\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)\n */\n constructor({\n messenger,\n isEnabled = () => true,\n }: {\n messenger: DeFiPositionsControllerMessenger;\n isEnabled?: () => boolean;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: getDefaultDefiPositionsControllerState(),\n });\n\n this.setIntervalLength(TEN_MINUTES_IN_MS);\n\n this.#fetchPositions = buildPositionFetcher();\n this.#isEnabled = isEnabled;\n\n this.messagingSystem.subscribe('KeyringController:unlock', async () => {\n this.startPolling(null);\n });\n\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.stopAllPolling();\n });\n\n this.messagingSystem.subscribe(\n 'TransactionController:transactionConfirmed',\n async (transactionMeta) => {\n if (!this.#isEnabled()) {\n return;\n }\n\n await this.#updateAccountPositions(transactionMeta.txParams.from);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n async (account) => {\n if (!this.#isEnabled() || !account.type.startsWith('eip155:')) {\n return;\n }\n\n await this.#updateAccountPositions(account.address);\n },\n );\n }\n\n async _executePoll(): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const accounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const results = await reduceInBatchesSerially({\n initialResult: [] as {\n accountAddress: string;\n positions: GroupedPositionsPerChain | null;\n }[],\n values: accounts,\n batchSize: FETCH_POSITIONS_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const batchResults = (\n await Promise.all(\n batch.map(async ({ address: accountAddress, type }) => {\n if (type.startsWith('eip155:')) {\n const positions =\n await this.#fetchAccountPositions(accountAddress);\n\n return {\n accountAddress,\n positions,\n };\n }\n\n return undefined;\n }),\n )\n ).filter(Boolean) as {\n accountAddress: string;\n positions: GroupedPositionsPerChain | null;\n }[];\n\n return [...workingResult, ...batchResults];\n },\n });\n\n const allDefiPositions = results.reduce(\n (acc, { accountAddress, positions }) => {\n acc[accountAddress] = positions;\n return acc;\n },\n {} as DeFiPositionsControllerState['allDeFiPositions'],\n );\n\n this.update((state) => {\n state.allDeFiPositions = allDefiPositions;\n });\n }\n\n async #updateAccountPositions(accountAddress: string): Promise<void> {\n const accountPositionsPerChain =\n await this.#fetchAccountPositions(accountAddress);\n\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = accountPositionsPerChain;\n });\n }\n\n async #fetchAccountPositions(\n accountAddress: string,\n ): Promise<GroupedPositionsPerChain | null> {\n try {\n const defiPositionsResponse = await this.#fetchPositions(accountAddress);\n\n return groupPositions(defiPositionsResponse);\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error) {\n return null;\n }\n }\n}\n"]}
@@ -1,18 +1,21 @@
1
- import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent } from "@metamask/accounts-controller";
1
+ /// <reference types="node" />
2
+ import type { AccountsControllerAccountAddedEvent, AccountsControllerListAccountsAction } from "@metamask/accounts-controller";
2
3
  import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedMessenger } from "@metamask/base-controller";
3
- import { BaseController } from "@metamask/base-controller";
4
- import type { NetworkControllerStateChangeEvent } from "@metamask/network-controller";
4
+ import type { KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
5
+ import type { KeyringControllerLockEvent } from "@metamask/keyring-controller";
6
+ import type { TransactionControllerTransactionConfirmedEvent } from "@metamask/transaction-controller";
5
7
  import type { Hex } from "@metamask/utils";
6
8
  import { type GroupedPositions } from "./group-positions.cjs";
7
9
  declare const controllerName = "DeFiPositionsController";
10
+ type GroupedPositionsPerChain = {
11
+ [chain: Hex]: GroupedPositions;
12
+ };
8
13
  export type DeFiPositionsControllerState = {
9
14
  /**
10
15
  * Object containing DeFi positions per account and network
11
16
  */
12
17
  allDeFiPositions: {
13
- [accountAddress: string]: {
14
- [chain: Hex]: GroupedPositions;
15
- } | null;
18
+ [accountAddress: string]: GroupedPositionsPerChain | null;
16
19
  };
17
20
  };
18
21
  export declare const getDefaultDefiPositionsControllerState: () => DeFiPositionsControllerState;
@@ -23,33 +26,47 @@ export type DeFiPositionsControllerStateChangeEvent = ControllerStateChangeEvent
23
26
  /**
24
27
  * The external actions available to the {@link DeFiPositionsController}.
25
28
  */
26
- export type AllowedActions = AccountsControllerGetSelectedAccountAction;
29
+ export type DeFiPositionsControllerAllowedActions = AccountsControllerListAccountsAction;
27
30
  /**
28
31
  * The external events available to the {@link DeFiPositionsController}.
29
32
  */
30
- export type AllowedEvents = NetworkControllerStateChangeEvent | AccountsControllerSelectedAccountChangeEvent;
33
+ export type DeFiPositionsControllerAllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent | TransactionControllerTransactionConfirmedEvent | AccountsControllerAccountAddedEvent;
31
34
  /**
32
35
  * The messenger of the {@link DeFiPositionsController}.
33
36
  */
34
- export type DeFiPositionsControllerMessenger = RestrictedMessenger<typeof controllerName, DeFiPositionsControllerActions | AllowedActions, DeFiPositionsControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
37
+ export type DeFiPositionsControllerMessenger = RestrictedMessenger<typeof controllerName, DeFiPositionsControllerActions | DeFiPositionsControllerAllowedActions, DeFiPositionsControllerEvents | DeFiPositionsControllerAllowedEvents, DeFiPositionsControllerAllowedActions['type'], DeFiPositionsControllerAllowedEvents['type']>;
38
+ declare const DeFiPositionsController_base: (abstract new (...args: any[]) => {
39
+ readonly "__#14@#intervalIds": Record<string, NodeJS.Timeout>;
40
+ "__#14@#intervalLength": number | undefined;
41
+ setIntervalLength(intervalLength: number): void;
42
+ getIntervalLength(): number | undefined;
43
+ _startPolling(input: import("@metamask/utils").Json): void;
44
+ _stopPollingByPollingTokenSetId(key: string): void;
45
+ readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
46
+ "__#3@#callbacks": Map<string, Set<(input: import("@metamask/utils").Json) => void>>;
47
+ _executePoll(input: import("@metamask/utils").Json): Promise<void>;
48
+ startPolling(input: import("@metamask/utils").Json): string;
49
+ stopAllPolling(): void;
50
+ stopPollingByPollingToken(pollingToken: string): void;
51
+ onPollingComplete(input: import("@metamask/utils").Json, callback: (input: import("@metamask/utils").Json) => void): void;
52
+ }) & typeof import("@metamask/base-controller").BaseController;
35
53
  /**
36
54
  * Controller that stores assets and exposes convenience methods
37
55
  */
38
- export declare class DeFiPositionsController extends BaseController<typeof controllerName, DeFiPositionsControllerState, DeFiPositionsControllerMessenger> {
56
+ export declare class DeFiPositionsController extends DeFiPositionsController_base<typeof controllerName, DeFiPositionsControllerState, DeFiPositionsControllerMessenger> {
39
57
  #private;
40
58
  /**
41
59
  * Tokens controller options
42
60
  *
43
61
  * @param options - Constructor options.
44
62
  * @param options.messenger - The controller messenger.
45
- * @param options.state - Initial state to set on this controller.
46
- * @param options.apiUrl - Override for theAPI URL to use for fetching DeFi positions.
63
+ * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)
47
64
  */
48
- constructor({ messenger, state, apiUrl, }: {
65
+ constructor({ messenger, isEnabled, }: {
49
66
  messenger: DeFiPositionsControllerMessenger;
50
- state?: Partial<DeFiPositionsControllerState>;
51
- apiUrl?: string;
67
+ isEnabled?: () => boolean;
52
68
  });
69
+ _executePoll(): Promise<void>;
53
70
  }
54
71
  export {};
55
72
  //# sourceMappingURL=DeFiPositionsController.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsController.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0CAA0C,EAC1C,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EAEpB,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,iCAAiC,EAAE,qCAAqC;AACtF,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAI3C,OAAO,EAAkB,KAAK,gBAAgB,EAAE,8BAA0B;AAE1E,QAAA,MAAM,cAAc,4BAA4B,CAAC;AAEjD,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,gBAAgB,EAAE;QAChB,CAAC,cAAc,EAAE,MAAM,GAAG;YAAE,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAA;SAAE,GAAG,IAAI,CAAC;KACrE,CAAC;CACH,CAAC;AASF,eAAO,MAAM,sCAAsC,QAC7C,4BAIH,CAAC;AAEJ,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACvC,uCAAuC,CAAC;AAE1C,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CACxB,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,0CAA0C,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,iCAAiC,GACjC,4CAA4C,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAChE,OAAO,cAAc,EACrB,8BAA8B,GAAG,cAAc,EAC/C,6BAA6B,GAAG,aAAa,EAC7C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,cAAc,CACzD,OAAO,cAAc,EACrB,4BAA4B,EAC5B,gCAAgC,CACjC;;IAKC;;;;;;;OAOG;gBACS,EACV,SAAS,EACT,KAAK,EACL,MAAM,GACP,EAAE;QACD,SAAS,EAAE,gCAAgC,CAAC;QAC5C,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;CA+CF"}
1
+ {"version":3,"file":"DeFiPositionsController.d.cts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,oCAAoC,EACrC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EAEpB,kCAAkC;AACnC,OAAO,KAAK,EAAE,4BAA4B,EAAE,qCAAqC;AACjF,OAAO,KAAK,EAAE,0BAA0B,EAAE,qCAAqC;AAE/E,OAAO,KAAK,EAAE,8CAA8C,EAAE,yCAAyC;AACvG,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAI3C,OAAO,EAAkB,KAAK,gBAAgB,EAAE,8BAA0B;AAO1E,QAAA,MAAM,cAAc,4BAA4B,CAAC;AAEjD,KAAK,wBAAwB,GAAG;IAC9B,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,gBAAgB,EAAE;QAChB,CAAC,cAAc,EAAE,MAAM,GAAG,wBAAwB,GAAG,IAAI,CAAC;KAC3D,CAAC;CACH,CAAC;AASF,eAAO,MAAM,sCAAsC,QAC7C,4BAIH,CAAC;AAEJ,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACvC,uCAAuC,CAAC;AAE1C,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CACxB,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,qCAAqC,GAC/C,oCAAoC,CAAC;AAEvC;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAC5C,4BAA4B,GAC5B,0BAA0B,GAC1B,8CAA8C,GAC9C,mCAAmC,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAChE,OAAO,cAAc,EACrB,8BAA8B,GAAG,qCAAqC,EACtE,6BAA6B,GAAG,oCAAoC,EACpE,qCAAqC,CAAC,MAAM,CAAC,EAC7C,oCAAoC,CAAC,MAAM,CAAC,CAC7C,CAAC;;;;;;;;;;;;;;;;AAEF;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,6BAC3C,OAAO,cAAc,EACrB,4BAA4B,EAC5B,gCAAgC,CACjC;;IAOC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,SAAsB,GACvB,EAAE;QACD,SAAS,EAAE,gCAAgC,CAAC;QAC5C,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;KAC3B;IA4CK,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CA4EpC"}
@@ -1,18 +1,21 @@
1
- import type { AccountsControllerGetSelectedAccountAction, AccountsControllerSelectedAccountChangeEvent } from "@metamask/accounts-controller";
1
+ /// <reference types="node" />
2
+ import type { AccountsControllerAccountAddedEvent, AccountsControllerListAccountsAction } from "@metamask/accounts-controller";
2
3
  import type { ControllerGetStateAction, ControllerStateChangeEvent, RestrictedMessenger } from "@metamask/base-controller";
3
- import { BaseController } from "@metamask/base-controller";
4
- import type { NetworkControllerStateChangeEvent } from "@metamask/network-controller";
4
+ import type { KeyringControllerUnlockEvent } from "@metamask/keyring-controller";
5
+ import type { KeyringControllerLockEvent } from "@metamask/keyring-controller";
6
+ import type { TransactionControllerTransactionConfirmedEvent } from "@metamask/transaction-controller";
5
7
  import type { Hex } from "@metamask/utils";
6
8
  import { type GroupedPositions } from "./group-positions.mjs";
7
9
  declare const controllerName = "DeFiPositionsController";
10
+ type GroupedPositionsPerChain = {
11
+ [chain: Hex]: GroupedPositions;
12
+ };
8
13
  export type DeFiPositionsControllerState = {
9
14
  /**
10
15
  * Object containing DeFi positions per account and network
11
16
  */
12
17
  allDeFiPositions: {
13
- [accountAddress: string]: {
14
- [chain: Hex]: GroupedPositions;
15
- } | null;
18
+ [accountAddress: string]: GroupedPositionsPerChain | null;
16
19
  };
17
20
  };
18
21
  export declare const getDefaultDefiPositionsControllerState: () => DeFiPositionsControllerState;
@@ -23,33 +26,47 @@ export type DeFiPositionsControllerStateChangeEvent = ControllerStateChangeEvent
23
26
  /**
24
27
  * The external actions available to the {@link DeFiPositionsController}.
25
28
  */
26
- export type AllowedActions = AccountsControllerGetSelectedAccountAction;
29
+ export type DeFiPositionsControllerAllowedActions = AccountsControllerListAccountsAction;
27
30
  /**
28
31
  * The external events available to the {@link DeFiPositionsController}.
29
32
  */
30
- export type AllowedEvents = NetworkControllerStateChangeEvent | AccountsControllerSelectedAccountChangeEvent;
33
+ export type DeFiPositionsControllerAllowedEvents = KeyringControllerUnlockEvent | KeyringControllerLockEvent | TransactionControllerTransactionConfirmedEvent | AccountsControllerAccountAddedEvent;
31
34
  /**
32
35
  * The messenger of the {@link DeFiPositionsController}.
33
36
  */
34
- export type DeFiPositionsControllerMessenger = RestrictedMessenger<typeof controllerName, DeFiPositionsControllerActions | AllowedActions, DeFiPositionsControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
37
+ export type DeFiPositionsControllerMessenger = RestrictedMessenger<typeof controllerName, DeFiPositionsControllerActions | DeFiPositionsControllerAllowedActions, DeFiPositionsControllerEvents | DeFiPositionsControllerAllowedEvents, DeFiPositionsControllerAllowedActions['type'], DeFiPositionsControllerAllowedEvents['type']>;
38
+ declare const DeFiPositionsController_base: (abstract new (...args: any[]) => {
39
+ readonly "__#14@#intervalIds": Record<string, NodeJS.Timeout>;
40
+ "__#14@#intervalLength": number | undefined;
41
+ setIntervalLength(intervalLength: number): void;
42
+ getIntervalLength(): number | undefined;
43
+ _startPolling(input: import("@metamask/utils").Json): void;
44
+ _stopPollingByPollingTokenSetId(key: string): void;
45
+ readonly "__#3@#pollingTokenSets": Map<string, Set<string>>;
46
+ "__#3@#callbacks": Map<string, Set<(input: import("@metamask/utils").Json) => void>>;
47
+ _executePoll(input: import("@metamask/utils").Json): Promise<void>;
48
+ startPolling(input: import("@metamask/utils").Json): string;
49
+ stopAllPolling(): void;
50
+ stopPollingByPollingToken(pollingToken: string): void;
51
+ onPollingComplete(input: import("@metamask/utils").Json, callback: (input: import("@metamask/utils").Json) => void): void;
52
+ }) & typeof import("@metamask/base-controller").BaseController;
35
53
  /**
36
54
  * Controller that stores assets and exposes convenience methods
37
55
  */
38
- export declare class DeFiPositionsController extends BaseController<typeof controllerName, DeFiPositionsControllerState, DeFiPositionsControllerMessenger> {
56
+ export declare class DeFiPositionsController extends DeFiPositionsController_base<typeof controllerName, DeFiPositionsControllerState, DeFiPositionsControllerMessenger> {
39
57
  #private;
40
58
  /**
41
59
  * Tokens controller options
42
60
  *
43
61
  * @param options - Constructor options.
44
62
  * @param options.messenger - The controller messenger.
45
- * @param options.state - Initial state to set on this controller.
46
- * @param options.apiUrl - Override for theAPI URL to use for fetching DeFi positions.
63
+ * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)
47
64
  */
48
- constructor({ messenger, state, apiUrl, }: {
65
+ constructor({ messenger, isEnabled, }: {
49
66
  messenger: DeFiPositionsControllerMessenger;
50
- state?: Partial<DeFiPositionsControllerState>;
51
- apiUrl?: string;
67
+ isEnabled?: () => boolean;
52
68
  });
69
+ _executePoll(): Promise<void>;
53
70
  }
54
71
  export {};
55
72
  //# sourceMappingURL=DeFiPositionsController.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsController.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0CAA0C,EAC1C,4CAA4C,EAC7C,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EAEpB,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,iCAAiC,EAAE,qCAAqC;AACtF,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAI3C,OAAO,EAAkB,KAAK,gBAAgB,EAAE,8BAA0B;AAE1E,QAAA,MAAM,cAAc,4BAA4B,CAAC;AAEjD,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,gBAAgB,EAAE;QAChB,CAAC,cAAc,EAAE,MAAM,GAAG;YAAE,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAA;SAAE,GAAG,IAAI,CAAC;KACrE,CAAC;CACH,CAAC;AASF,eAAO,MAAM,sCAAsC,QAC7C,4BAIH,CAAC;AAEJ,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACvC,uCAAuC,CAAC;AAE1C,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CACxB,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,0CAA0C,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,iCAAiC,GACjC,4CAA4C,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAChE,OAAO,cAAc,EACrB,8BAA8B,GAAG,cAAc,EAC/C,6BAA6B,GAAG,aAAa,EAC7C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,cAAc,CACzD,OAAO,cAAc,EACrB,4BAA4B,EAC5B,gCAAgC,CACjC;;IAKC;;;;;;;OAOG;gBACS,EACV,SAAS,EACT,KAAK,EACL,MAAM,GACP,EAAE;QACD,SAAS,EAAE,gCAAgC,CAAC;QAC5C,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;CA+CF"}
1
+ {"version":3,"file":"DeFiPositionsController.d.mts","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EACV,mCAAmC,EACnC,oCAAoC,EACrC,sCAAsC;AACvC,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,mBAAmB,EAEpB,kCAAkC;AACnC,OAAO,KAAK,EAAE,4BAA4B,EAAE,qCAAqC;AACjF,OAAO,KAAK,EAAE,0BAA0B,EAAE,qCAAqC;AAE/E,OAAO,KAAK,EAAE,8CAA8C,EAAE,yCAAyC;AACvG,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAI3C,OAAO,EAAkB,KAAK,gBAAgB,EAAE,8BAA0B;AAO1E,QAAA,MAAM,cAAc,4BAA4B,CAAC;AAEjD,KAAK,wBAAwB,GAAG;IAC9B,CAAC,KAAK,EAAE,GAAG,GAAG,gBAAgB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,gBAAgB,EAAE;QAChB,CAAC,cAAc,EAAE,MAAM,GAAG,wBAAwB,GAAG,IAAI,CAAC;KAC3D,CAAC;CACH,CAAC;AASF,eAAO,MAAM,sCAAsC,QAC7C,4BAIH,CAAC;AAEJ,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,6BAA6B,GACvC,uCAAuC,CAAC;AAE1C,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CACxB,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,qCAAqC,GAC/C,oCAAoC,CAAC;AAEvC;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAC5C,4BAA4B,GAC5B,0BAA0B,GAC1B,8CAA8C,GAC9C,mCAAmC,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG,mBAAmB,CAChE,OAAO,cAAc,EACrB,8BAA8B,GAAG,qCAAqC,EACtE,6BAA6B,GAAG,oCAAoC,EACpE,qCAAqC,CAAC,MAAM,CAAC,EAC7C,oCAAoC,CAAC,MAAM,CAAC,CAC7C,CAAC;;;;;;;;;;;;;;;;AAEF;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,6BAC3C,OAAO,cAAc,EACrB,4BAA4B,EAC5B,gCAAgC,CACjC;;IAOC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,SAAsB,GACvB,EAAE;QACD,SAAS,EAAE,gCAAgC,CAAC;QAC5C,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;KAC3B;IA4CK,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CA4EpC"}
@@ -9,10 +9,13 @@ 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 _DeFiPositionsController_instances, _DeFiPositionsController_fetchPositions, _DeFiPositionsController_updateAccountPositions;
13
- import { BaseController } from "@metamask/base-controller";
12
+ var _DeFiPositionsController_instances, _DeFiPositionsController_fetchPositions, _DeFiPositionsController_isEnabled, _DeFiPositionsController_updateAccountPositions, _DeFiPositionsController_fetchAccountPositions;
13
+ import { StaticIntervalPollingController } from "@metamask/polling-controller";
14
14
  import { buildPositionFetcher } from "./fetch-positions.mjs";
15
15
  import { groupPositions } from "./group-positions.mjs";
16
+ import { reduceInBatchesSerially } from "../assetsUtil.mjs";
17
+ const TEN_MINUTES_IN_MS = 60000;
18
+ const FETCH_POSITIONS_BATCH_SIZE = 10;
16
19
  const controllerName = 'DeFiPositionsController';
17
20
  const controllerMetadata = {
18
21
  allDeFiPositions: {
@@ -28,47 +31,91 @@ export const getDefaultDefiPositionsControllerState = () => {
28
31
  /**
29
32
  * Controller that stores assets and exposes convenience methods
30
33
  */
31
- export class DeFiPositionsController extends BaseController {
34
+ export class DeFiPositionsController extends StaticIntervalPollingController() {
32
35
  /**
33
36
  * Tokens controller options
34
37
  *
35
38
  * @param options - Constructor options.
36
39
  * @param options.messenger - The controller messenger.
37
- * @param options.state - Initial state to set on this controller.
38
- * @param options.apiUrl - Override for theAPI URL to use for fetching DeFi positions.
40
+ * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)
39
41
  */
40
- constructor({ messenger, state, apiUrl, }) {
42
+ constructor({ messenger, isEnabled = () => true, }) {
41
43
  super({
42
44
  name: controllerName,
43
45
  metadata: controllerMetadata,
44
46
  messenger,
45
- state: {
46
- ...getDefaultDefiPositionsControllerState(),
47
- ...state,
48
- },
47
+ state: getDefaultDefiPositionsControllerState(),
49
48
  });
50
49
  _DeFiPositionsController_instances.add(this);
51
50
  _DeFiPositionsController_fetchPositions.set(this, void 0);
52
- __classPrivateFieldSet(this, _DeFiPositionsController_fetchPositions, buildPositionFetcher(apiUrl), "f");
53
- this.messagingSystem.subscribe('AccountsController:selectedAccountChange', async (selectedAccount) => {
54
- await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, selectedAccount.address);
51
+ _DeFiPositionsController_isEnabled.set(this, void 0);
52
+ this.setIntervalLength(TEN_MINUTES_IN_MS);
53
+ __classPrivateFieldSet(this, _DeFiPositionsController_fetchPositions, buildPositionFetcher(), "f");
54
+ __classPrivateFieldSet(this, _DeFiPositionsController_isEnabled, isEnabled, "f");
55
+ this.messagingSystem.subscribe('KeyringController:unlock', async () => {
56
+ this.startPolling(null);
57
+ });
58
+ this.messagingSystem.subscribe('KeyringController:lock', () => {
59
+ this.stopAllPolling();
60
+ });
61
+ this.messagingSystem.subscribe('TransactionController:transactionConfirmed', async (transactionMeta) => {
62
+ if (!__classPrivateFieldGet(this, _DeFiPositionsController_isEnabled, "f").call(this)) {
63
+ return;
64
+ }
65
+ await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, transactionMeta.txParams.from);
55
66
  });
56
- this.messagingSystem.subscribe('NetworkController:stateChange', async () => {
57
- const { address } = this.messagingSystem.call('AccountsController:getSelectedAccount');
58
- if (address) {
59
- await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, address);
67
+ this.messagingSystem.subscribe('AccountsController:accountAdded', async (account) => {
68
+ if (!__classPrivateFieldGet(this, _DeFiPositionsController_isEnabled, "f").call(this) || !account.type.startsWith('eip155:')) {
69
+ return;
60
70
  }
71
+ await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_updateAccountPositions).call(this, account.address);
72
+ });
73
+ }
74
+ async _executePoll() {
75
+ if (!__classPrivateFieldGet(this, _DeFiPositionsController_isEnabled, "f").call(this)) {
76
+ return;
77
+ }
78
+ const accounts = this.messagingSystem.call('AccountsController:listAccounts');
79
+ const results = await reduceInBatchesSerially({
80
+ initialResult: [],
81
+ values: accounts,
82
+ batchSize: FETCH_POSITIONS_BATCH_SIZE,
83
+ eachBatch: async (workingResult, batch) => {
84
+ const batchResults = (await Promise.all(batch.map(async ({ address: accountAddress, type }) => {
85
+ if (type.startsWith('eip155:')) {
86
+ const positions = await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_fetchAccountPositions).call(this, accountAddress);
87
+ return {
88
+ accountAddress,
89
+ positions,
90
+ };
91
+ }
92
+ return undefined;
93
+ }))).filter(Boolean);
94
+ return [...workingResult, ...batchResults];
95
+ },
96
+ });
97
+ const allDefiPositions = results.reduce((acc, { accountAddress, positions }) => {
98
+ acc[accountAddress] = positions;
99
+ return acc;
100
+ }, {});
101
+ this.update((state) => {
102
+ state.allDeFiPositions = allDefiPositions;
61
103
  });
62
104
  }
63
105
  }
64
- _DeFiPositionsController_fetchPositions = new WeakMap(), _DeFiPositionsController_instances = new WeakSet(), _DeFiPositionsController_updateAccountPositions = async function _DeFiPositionsController_updateAccountPositions(accountAddress) {
65
- this.update((state) => {
66
- state.allDeFiPositions[accountAddress] = null;
67
- });
68
- const defiPositionsResponse = await __classPrivateFieldGet(this, _DeFiPositionsController_fetchPositions, "f").call(this, accountAddress);
69
- const accountPositionsPerChain = groupPositions(defiPositionsResponse);
106
+ _DeFiPositionsController_fetchPositions = new WeakMap(), _DeFiPositionsController_isEnabled = new WeakMap(), _DeFiPositionsController_instances = new WeakSet(), _DeFiPositionsController_updateAccountPositions = async function _DeFiPositionsController_updateAccountPositions(accountAddress) {
107
+ const accountPositionsPerChain = await __classPrivateFieldGet(this, _DeFiPositionsController_instances, "m", _DeFiPositionsController_fetchAccountPositions).call(this, accountAddress);
70
108
  this.update((state) => {
71
109
  state.allDeFiPositions[accountAddress] = accountPositionsPerChain;
72
110
  });
111
+ }, _DeFiPositionsController_fetchAccountPositions = async function _DeFiPositionsController_fetchAccountPositions(accountAddress) {
112
+ try {
113
+ const defiPositionsResponse = await __classPrivateFieldGet(this, _DeFiPositionsController_fetchPositions, "f").call(this, accountAddress);
114
+ return groupPositions(defiPositionsResponse);
115
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
116
+ }
117
+ catch (error) {
118
+ return null;
119
+ }
73
120
  };
74
121
  //# sourceMappingURL=DeFiPositionsController.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeFiPositionsController.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":";;;;;;;;;;;;AAUA,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAK3D,OAAO,EAAE,oBAAoB,EAAE,8BAA0B;AACzD,OAAO,EAAE,cAAc,EAAyB,8BAA0B;AAE1E,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAWjD,MAAM,kBAAkB,GAAgD;IACtE,gBAAgB,EAAE;QAChB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,sCAAsC,GACjD,GAAiC,EAAE;IACjC,OAAO;QACL,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC,CAAC;AA0CJ;;GAEG;AACH,MAAM,OAAO,uBAAwB,SAAQ,cAI5C;IAKC;;;;;;;OAOG;IACH,YAAY,EACV,SAAS,EACT,KAAK,EACL,MAAM,GAKP;QACC,KAAK,CAAC;YACJ,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,kBAAkB;YAC5B,SAAS;YACT,KAAK,EAAE;gBACL,GAAG,sCAAsC,EAAE;gBAC3C,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QA7BI,0DAE4B;QA6BnC,uBAAA,IAAI,2CAAmB,oBAAoB,CAAC,MAAM,CAAC,MAAA,CAAC;QAEpD,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,0CAA0C,EAC1C,KAAK,EAAE,eAAe,EAAE,EAAE;YACxB,MAAM,uBAAA,IAAI,2FAAwB,MAA5B,IAAI,EAAyB,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,+BAA+B,EAC/B,KAAK,IAAI,EAAE;YACT,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAC3C,uCAAuC,CACxC,CAAC;YAEF,IAAI,OAAO,EAAE;gBACX,MAAM,uBAAA,IAAI,2FAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CAAC;aAC7C;QACH,CAAC,CACF,CAAC;IACJ,CAAC;CAeF;+JAbC,KAAK,0DAAyB,cAAsB;IAClD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,MAAM,qBAAqB,GAAG,MAAM,uBAAA,IAAI,+CAAgB,MAApB,IAAI,EAAiB,cAAc,CAAC,CAAC;IAEzE,MAAM,wBAAwB,GAAG,cAAc,CAAC,qBAAqB,CAAC,CAAC;IAEvE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,GAAG,wBAAwB,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n AccountsControllerGetSelectedAccountAction,\n AccountsControllerSelectedAccountChangeEvent,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { NetworkControllerStateChangeEvent } from '@metamask/network-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { DefiPositionResponse } from './fetch-positions';\nimport { buildPositionFetcher } from './fetch-positions';\nimport { groupPositions, type GroupedPositions } from './group-positions';\n\nconst controllerName = 'DeFiPositionsController';\n\nexport type DeFiPositionsControllerState = {\n /**\n * Object containing DeFi positions per account and network\n */\n allDeFiPositions: {\n [accountAddress: string]: { [chain: Hex]: GroupedPositions } | null;\n };\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerState> = {\n allDeFiPositions: {\n persist: false,\n anonymous: false,\n },\n};\n\nexport const getDefaultDefiPositionsControllerState =\n (): DeFiPositionsControllerState => {\n return {\n allDeFiPositions: {},\n };\n };\n\nexport type DeFiPositionsControllerActions =\n DeFiPositionsControllerGetStateAction;\n\nexport type DeFiPositionsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerState\n>;\n\nexport type DeFiPositionsControllerEvents =\n DeFiPositionsControllerStateChangeEvent;\n\nexport type DeFiPositionsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n DeFiPositionsControllerState\n >;\n\n/**\n * The external actions available to the {@link DeFiPositionsController}.\n */\nexport type AllowedActions = AccountsControllerGetSelectedAccountAction;\n\n/**\n * The external events available to the {@link DeFiPositionsController}.\n */\nexport type AllowedEvents =\n | NetworkControllerStateChangeEvent\n | AccountsControllerSelectedAccountChangeEvent;\n\n/**\n * The messenger of the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n DeFiPositionsControllerActions | AllowedActions,\n DeFiPositionsControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class DeFiPositionsController extends BaseController<\n typeof controllerName,\n DeFiPositionsControllerState,\n DeFiPositionsControllerMessenger\n> {\n readonly #fetchPositions: (\n accountAddress: string,\n ) => Promise<DefiPositionResponse[]>;\n\n /**\n * Tokens controller options\n *\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.apiUrl - Override for theAPI URL to use for fetching DeFi positions.\n */\n constructor({\n messenger,\n state,\n apiUrl,\n }: {\n messenger: DeFiPositionsControllerMessenger;\n state?: Partial<DeFiPositionsControllerState>;\n apiUrl?: string;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: {\n ...getDefaultDefiPositionsControllerState(),\n ...state,\n },\n });\n\n this.#fetchPositions = buildPositionFetcher(apiUrl);\n\n this.messagingSystem.subscribe(\n 'AccountsController:selectedAccountChange',\n async (selectedAccount) => {\n await this.#updateAccountPositions(selectedAccount.address);\n },\n );\n\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n async () => {\n const { address } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n if (address) {\n await this.#updateAccountPositions(address);\n }\n },\n );\n }\n\n async #updateAccountPositions(accountAddress: string) {\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = null;\n });\n\n const defiPositionsResponse = await this.#fetchPositions(accountAddress);\n\n const accountPositionsPerChain = groupPositions(defiPositionsResponse);\n\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = accountPositionsPerChain;\n });\n }\n}\n"]}
1
+ {"version":3,"file":"DeFiPositionsController.mjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/DeFiPositionsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountsControllerAccountAddedEvent,\n AccountsControllerListAccountsAction,\n} from '@metamask/accounts-controller';\nimport type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedMessenger,\n StateMetadata,\n} from '@metamask/base-controller';\nimport type { KeyringControllerUnlockEvent } from '@metamask/keyring-controller';\nimport type { KeyringControllerLockEvent } from '@metamask/keyring-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type { TransactionControllerTransactionConfirmedEvent } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { DefiPositionResponse } from './fetch-positions';\nimport { buildPositionFetcher } from './fetch-positions';\nimport { groupPositions, type GroupedPositions } from './group-positions';\nimport { reduceInBatchesSerially } from '../assetsUtil';\n\nconst TEN_MINUTES_IN_MS = 60_000;\n\nconst FETCH_POSITIONS_BATCH_SIZE = 10;\n\nconst controllerName = 'DeFiPositionsController';\n\ntype GroupedPositionsPerChain = {\n [chain: Hex]: GroupedPositions;\n};\n\nexport type DeFiPositionsControllerState = {\n /**\n * Object containing DeFi positions per account and network\n */\n allDeFiPositions: {\n [accountAddress: string]: GroupedPositionsPerChain | null;\n };\n};\n\nconst controllerMetadata: StateMetadata<DeFiPositionsControllerState> = {\n allDeFiPositions: {\n persist: false,\n anonymous: false,\n },\n};\n\nexport const getDefaultDefiPositionsControllerState =\n (): DeFiPositionsControllerState => {\n return {\n allDeFiPositions: {},\n };\n };\n\nexport type DeFiPositionsControllerActions =\n DeFiPositionsControllerGetStateAction;\n\nexport type DeFiPositionsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n DeFiPositionsControllerState\n>;\n\nexport type DeFiPositionsControllerEvents =\n DeFiPositionsControllerStateChangeEvent;\n\nexport type DeFiPositionsControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n DeFiPositionsControllerState\n >;\n\n/**\n * The external actions available to the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerAllowedActions =\n AccountsControllerListAccountsAction;\n\n/**\n * The external events available to the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerAllowedEvents =\n | KeyringControllerUnlockEvent\n | KeyringControllerLockEvent\n | TransactionControllerTransactionConfirmedEvent\n | AccountsControllerAccountAddedEvent;\n\n/**\n * The messenger of the {@link DeFiPositionsController}.\n */\nexport type DeFiPositionsControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n DeFiPositionsControllerActions | DeFiPositionsControllerAllowedActions,\n DeFiPositionsControllerEvents | DeFiPositionsControllerAllowedEvents,\n DeFiPositionsControllerAllowedActions['type'],\n DeFiPositionsControllerAllowedEvents['type']\n>;\n\n/**\n * Controller that stores assets and exposes convenience methods\n */\nexport class DeFiPositionsController extends StaticIntervalPollingController()<\n typeof controllerName,\n DeFiPositionsControllerState,\n DeFiPositionsControllerMessenger\n> {\n readonly #fetchPositions: (\n accountAddress: string,\n ) => Promise<DefiPositionResponse[]>;\n\n readonly #isEnabled: () => boolean;\n\n /**\n * Tokens controller options\n *\n * @param options - Constructor options.\n * @param options.messenger - The controller messenger.\n * @param options.isEnabled - Function that returns whether the controller is enabled. (default: () => true)\n */\n constructor({\n messenger,\n isEnabled = () => true,\n }: {\n messenger: DeFiPositionsControllerMessenger;\n isEnabled?: () => boolean;\n }) {\n super({\n name: controllerName,\n metadata: controllerMetadata,\n messenger,\n state: getDefaultDefiPositionsControllerState(),\n });\n\n this.setIntervalLength(TEN_MINUTES_IN_MS);\n\n this.#fetchPositions = buildPositionFetcher();\n this.#isEnabled = isEnabled;\n\n this.messagingSystem.subscribe('KeyringController:unlock', async () => {\n this.startPolling(null);\n });\n\n this.messagingSystem.subscribe('KeyringController:lock', () => {\n this.stopAllPolling();\n });\n\n this.messagingSystem.subscribe(\n 'TransactionController:transactionConfirmed',\n async (transactionMeta) => {\n if (!this.#isEnabled()) {\n return;\n }\n\n await this.#updateAccountPositions(transactionMeta.txParams.from);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n async (account) => {\n if (!this.#isEnabled() || !account.type.startsWith('eip155:')) {\n return;\n }\n\n await this.#updateAccountPositions(account.address);\n },\n );\n }\n\n async _executePoll(): Promise<void> {\n if (!this.#isEnabled()) {\n return;\n }\n\n const accounts = this.messagingSystem.call(\n 'AccountsController:listAccounts',\n );\n\n const results = await reduceInBatchesSerially({\n initialResult: [] as {\n accountAddress: string;\n positions: GroupedPositionsPerChain | null;\n }[],\n values: accounts,\n batchSize: FETCH_POSITIONS_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const batchResults = (\n await Promise.all(\n batch.map(async ({ address: accountAddress, type }) => {\n if (type.startsWith('eip155:')) {\n const positions =\n await this.#fetchAccountPositions(accountAddress);\n\n return {\n accountAddress,\n positions,\n };\n }\n\n return undefined;\n }),\n )\n ).filter(Boolean) as {\n accountAddress: string;\n positions: GroupedPositionsPerChain | null;\n }[];\n\n return [...workingResult, ...batchResults];\n },\n });\n\n const allDefiPositions = results.reduce(\n (acc, { accountAddress, positions }) => {\n acc[accountAddress] = positions;\n return acc;\n },\n {} as DeFiPositionsControllerState['allDeFiPositions'],\n );\n\n this.update((state) => {\n state.allDeFiPositions = allDefiPositions;\n });\n }\n\n async #updateAccountPositions(accountAddress: string): Promise<void> {\n const accountPositionsPerChain =\n await this.#fetchAccountPositions(accountAddress);\n\n this.update((state) => {\n state.allDeFiPositions[accountAddress] = accountPositionsPerChain;\n });\n }\n\n async #fetchAccountPositions(\n accountAddress: string,\n ): Promise<GroupedPositionsPerChain | null> {\n try {\n const defiPositionsResponse = await this.#fetchPositions(accountAddress);\n\n return groupPositions(defiPositionsResponse);\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error) {\n return null;\n }\n }\n}\n"]}
@@ -1,17 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildPositionFetcher = exports.DEFAULT_DEFI_POSITIONS_API_URL = void 0;
3
+ exports.buildPositionFetcher = exports.DEFI_POSITIONS_API_URL = void 0;
4
4
  // TODO: Update with prod API URL when available
5
- exports.DEFAULT_DEFI_POSITIONS_API_URL = 'https://defiadapters.dev-api.cx.metamask.io';
5
+ exports.DEFI_POSITIONS_API_URL = 'https://defiadapters.dev-api.cx.metamask.io';
6
6
  /**
7
7
  * Builds a function that fetches DeFi positions for a given account address
8
8
  *
9
- * @param apiUrl - The URL of the API to fetch the DeFi positions from
10
9
  * @returns A function that fetches DeFi positions for a given account address
11
10
  */
12
- function buildPositionFetcher(apiUrl = exports.DEFAULT_DEFI_POSITIONS_API_URL) {
11
+ function buildPositionFetcher() {
13
12
  return async (accountAddress) => {
14
- const defiPositionsResponse = await fetch(`${apiUrl}/positions/${accountAddress}`);
13
+ const defiPositionsResponse = await fetch(`${exports.DEFI_POSITIONS_API_URL}/positions/${accountAddress}`);
15
14
  if (defiPositionsResponse.status !== 200) {
16
15
  throw new Error(`Unable to fetch defi positions - HTTP ${defiPositionsResponse.status}`);
17
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-positions.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/fetch-positions.ts"],"names":[],"mappings":";;;AAwDA,gDAAgD;AACnC,QAAA,8BAA8B,GACzC,6CAA6C,CAAC;AAEhD;;;;;GAKG;AACH,SAAgB,oBAAoB,CAClC,SAAiB,sCAA8B;IAE/C,OAAO,KAAK,EAAE,cAAsB,EAAmC,EAAE;QACvE,MAAM,qBAAqB,GAAG,MAAM,KAAK,CACvC,GAAG,MAAM,cAAc,cAAc,EAAE,CACxC,CAAC;QAEF,IAAI,qBAAqB,CAAC,MAAM,KAAK,GAAG,EAAE;YACxC,MAAM,IAAI,KAAK,CACb,yCAAyC,qBAAqB,CAAC,MAAM,EAAE,CACxE,CAAC;SACH;QAED,OAAO,CAAC,MAAM,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IACnD,CAAC,CAAC;AACJ,CAAC;AAhBD,oDAgBC","sourcesContent":["export type DefiPositionResponse = AdapterResponse<{\n tokens: ProtocolToken[];\n}>;\n\ntype ProtocolDetails = {\n chainId: number;\n protocolId: string;\n productId: string;\n name: string;\n description: string;\n iconUrl: string;\n siteUrl: string;\n positionType: PositionType;\n metadata?: {\n groupPositions?: boolean;\n };\n};\n\ntype AdapterResponse<ProtocolResponse> =\n | (ProtocolDetails & {\n chainName: string;\n } & (\n | (ProtocolResponse & { success: true })\n | (AdapterErrorResponse & { success: false })\n ))\n | (AdapterErrorResponse & { success: false });\n\ntype AdapterErrorResponse = {\n error: {\n message: string;\n };\n};\n\nexport type PositionType = 'supply' | 'borrow' | 'stake' | 'reward';\n\nexport type ProtocolToken = Balance & {\n type: 'protocol';\n tokenId?: string;\n};\n\nexport type Underlying = Balance & {\n type: 'underlying' | 'underlying-claimable';\n iconUrl: string;\n};\n\nexport type Balance = {\n address: string;\n name: string;\n symbol: string;\n decimals: number;\n balanceRaw: string;\n balance: number;\n price?: number;\n tokens?: Underlying[];\n};\n\n// TODO: Update with prod API URL when available\nexport const DEFAULT_DEFI_POSITIONS_API_URL =\n 'https://defiadapters.dev-api.cx.metamask.io';\n\n/**\n * Builds a function that fetches DeFi positions for a given account address\n *\n * @param apiUrl - The URL of the API to fetch the DeFi positions from\n * @returns A function that fetches DeFi positions for a given account address\n */\nexport function buildPositionFetcher(\n apiUrl: string = DEFAULT_DEFI_POSITIONS_API_URL,\n) {\n return async (accountAddress: string): Promise<DefiPositionResponse[]> => {\n const defiPositionsResponse = await fetch(\n `${apiUrl}/positions/${accountAddress}`,\n );\n\n if (defiPositionsResponse.status !== 200) {\n throw new Error(\n `Unable to fetch defi positions - HTTP ${defiPositionsResponse.status}`,\n );\n }\n\n return (await defiPositionsResponse.json()).data;\n };\n}\n"]}
1
+ {"version":3,"file":"fetch-positions.cjs","sourceRoot":"","sources":["../../src/DeFiPositionsController/fetch-positions.ts"],"names":[],"mappings":";;;AAwDA,gDAAgD;AACnC,QAAA,sBAAsB,GACjC,6CAA6C,CAAC;AAEhD;;;;GAIG;AACH,SAAgB,oBAAoB;IAClC,OAAO,KAAK,EAAE,cAAsB,EAAmC,EAAE;QACvE,MAAM,qBAAqB,GAAG,MAAM,KAAK,CACvC,GAAG,8BAAsB,cAAc,cAAc,EAAE,CACxD,CAAC;QAEF,IAAI,qBAAqB,CAAC,MAAM,KAAK,GAAG,EAAE;YACxC,MAAM,IAAI,KAAK,CACb,yCAAyC,qBAAqB,CAAC,MAAM,EAAE,CACxE,CAAC;SACH;QAED,OAAO,CAAC,MAAM,qBAAqB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IACnD,CAAC,CAAC;AACJ,CAAC;AAdD,oDAcC","sourcesContent":["export type DefiPositionResponse = AdapterResponse<{\n tokens: ProtocolToken[];\n}>;\n\ntype ProtocolDetails = {\n chainId: number;\n protocolId: string;\n productId: string;\n name: string;\n description: string;\n iconUrl: string;\n siteUrl: string;\n positionType: PositionType;\n metadata?: {\n groupPositions?: boolean;\n };\n};\n\ntype AdapterResponse<ProtocolResponse> =\n | (ProtocolDetails & {\n chainName: string;\n } & (\n | (ProtocolResponse & { success: true })\n | (AdapterErrorResponse & { success: false })\n ))\n | (AdapterErrorResponse & { success: false });\n\ntype AdapterErrorResponse = {\n error: {\n message: string;\n };\n};\n\nexport type PositionType = 'supply' | 'borrow' | 'stake' | 'reward';\n\nexport type ProtocolToken = Balance & {\n type: 'protocol';\n tokenId?: string;\n};\n\nexport type Underlying = Balance & {\n type: 'underlying' | 'underlying-claimable';\n iconUrl: string;\n};\n\nexport type Balance = {\n address: string;\n name: string;\n symbol: string;\n decimals: number;\n balanceRaw: string;\n balance: number;\n price?: number;\n tokens?: Underlying[];\n};\n\n// TODO: Update with prod API URL when available\nexport const DEFI_POSITIONS_API_URL =\n 'https://defiadapters.dev-api.cx.metamask.io';\n\n/**\n * Builds a function that fetches DeFi positions for a given account address\n *\n * @returns A function that fetches DeFi positions for a given account address\n */\nexport function buildPositionFetcher() {\n return async (accountAddress: string): Promise<DefiPositionResponse[]> => {\n const defiPositionsResponse = await fetch(\n `${DEFI_POSITIONS_API_URL}/positions/${accountAddress}`,\n );\n\n if (defiPositionsResponse.status !== 200) {\n throw new Error(\n `Unable to fetch defi positions - HTTP ${defiPositionsResponse.status}`,\n );\n }\n\n return (await defiPositionsResponse.json()).data;\n };\n}\n"]}