@metamask-previews/assets-controllers 17.0.0-preview.0a6dab6 → 18.0.0-preview.4ae7d84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [18.0.0]
10
+ ### Changed
11
+ - **BREAKING**: `CurrencyRateController` is now keyed by `nativeCurrency` (i.e. ticker) for `conversionDate`, `conversionRate`, and `usdConversionRate` in the `currencyRates` object. `nativeCurrency`, `pendingNativeCurrency`, and `pendingCurrentCurrency` have been removed.
12
+ - ```
13
+ export type CurrencyRateState = {
14
+ currentCurrency: string;
15
+ currencyRates: Record<
16
+ string, // nativeCurrency
17
+ {
18
+ conversionDate: number | null;
19
+ conversionRate: number | null;
20
+ usdConversionRate: number | null;
21
+ }
22
+ >;
23
+ };
24
+ ```
25
+ - **BREAKING**: `CurrencyRateController` now extends `PollingController` ([#1805](https://github.com/MetaMask/core/pull/1805))
26
+ - `start()` and `stop()` methods replaced with `startPollingByNetworkClientId()`, `stopPollingByPollingToken()`, and `stopAllPolling()`
27
+ - **BREAKING**: `CurrencyRateController` now sends the `NetworkController:getNetworkClientById` action via messaging controller ([#1805](https://github.com/MetaMask/core/pull/1805))
28
+
29
+ ### Fixed
30
+ - Parallelize network requests in assets controllers for performance enhancement ([#1801](https://github.com/MetaMask/core/pull/1801))
31
+ - Fix token detection on accounts when user changes account after token detection request is inflight ([#1848](https://github.com/MetaMask/core/pull/1848))
32
+
9
33
  ## [17.0.0]
10
34
  ### Changed
11
35
  - **BREAKING:** Bump dependency on `@metamask/polling-controller` to ^1.0.0
@@ -197,8 +221,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
197
221
  - The `NFTController` now requires an instance of a ControllerMessenger to be passed to its constructor. This is messenger is used to pass the `watchNFT` message to the `ApprovalController`.
198
222
 
199
223
  ### Changed
200
- - Add dependency on `@ethersproject/address` ([#1173](https://github.com/MetaMask/core.git/pull/1173))
201
- - Replace `eth-rpc-errors` with `@metamask/rpc-errors` ([#1173](https://github.com/MetaMask/core.git/pull/1173))
224
+ - Add dependency on `@ethersproject/address` ([#1173](https://github.com/MetaMask/core/pull/1173))
225
+ - Replace `eth-rpc-errors` with `@metamask/rpc-errors` ([#1173](https://github.com/MetaMask/core/pull/1173))
202
226
 
203
227
  ## [8.0.0]
204
228
  ### Added
@@ -341,7 +365,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
341
365
  ### Changed
342
366
  - Use Ethers for AssetsContractController ([#845](https://github.com/MetaMask/core/pull/845))
343
367
 
344
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@17.0.0...HEAD
368
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@18.0.0...HEAD
369
+ [18.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@17.0.0...@metamask/assets-controllers@18.0.0
345
370
  [17.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@16.0.0...@metamask/assets-controllers@17.0.0
346
371
  [16.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@15.0.0...@metamask/assets-controllers@16.0.0
347
372
  [15.0.0]: https://github.com/MetaMask/core/compare/@metamask/assets-controllers@14.0.0...@metamask/assets-controllers@15.0.0
@@ -1,25 +1,23 @@
1
1
  import type { RestrictedControllerMessenger } from '@metamask/base-controller';
2
- import { BaseControllerV2 } from '@metamask/base-controller';
2
+ import type { NetworkClientId, NetworkControllerGetNetworkClientByIdAction } from '@metamask/network-controller';
3
+ import { PollingController } from '@metamask/polling-controller';
3
4
  import type { Patch } from 'immer';
4
5
  import { fetchExchangeRate as defaultFetchExchangeRate } from './crypto-compare';
5
6
  /**
6
7
  * @type CurrencyRateState
7
- * @property conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch
8
- * @property conversionRate - Conversion rate from current base asset to the current currency
8
+ * @property currencyRates - Object keyed by native currency
9
+ * @property currencyRates.conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch
10
+ * @property currencyRates.conversionRate - Conversion rate from current base asset to the current currency
9
11
  * @property currentCurrency - Currently-active ISO 4217 currency code
10
- * @property nativeCurrency - Symbol for the base asset used for conversion
11
- * @property pendingCurrentCurrency - The currency being switched to
12
- * @property pendingNativeCurrency - The base asset currency being switched to
13
12
  * @property usdConversionRate - Conversion rate from usd to the current currency
14
13
  */
15
14
  export declare type CurrencyRateState = {
16
- conversionDate: number | null;
17
- conversionRate: number | null;
18
15
  currentCurrency: string;
19
- nativeCurrency: string;
20
- pendingCurrentCurrency: string | null;
21
- pendingNativeCurrency: string | null;
22
- usdConversionRate: number | null;
16
+ currencyRates: Record<string, {
17
+ conversionDate: number | null;
18
+ conversionRate: number | null;
19
+ usdConversionRate: number | null;
20
+ }>;
23
21
  };
24
22
  declare const name = "CurrencyRateController";
25
23
  export declare type CurrencyRateStateChange = {
@@ -30,16 +28,14 @@ export declare type GetCurrencyRateState = {
30
28
  type: `${typeof name}:getState`;
31
29
  handler: () => CurrencyRateState;
32
30
  };
33
- declare type CurrencyRateMessenger = RestrictedControllerMessenger<typeof name, GetCurrencyRateState, CurrencyRateStateChange, never, never>;
31
+ declare type AllowedActions = NetworkControllerGetNetworkClientByIdAction | GetCurrencyRateState;
32
+ declare type CurrencyRateMessenger = RestrictedControllerMessenger<typeof name, AllowedActions, CurrencyRateStateChange, AllowedActions['type'], never>;
34
33
  /**
35
34
  * Controller that passively polls on a set interval for an exchange rate from the current network
36
35
  * asset to the user's preferred currency.
37
36
  */
38
- export declare class CurrencyRateController extends BaseControllerV2<typeof name, CurrencyRateState, CurrencyRateMessenger> {
39
- #private;
37
+ export declare class CurrencyRateController extends PollingController<typeof name, CurrencyRateState, CurrencyRateMessenger> {
40
38
  private readonly mutex;
41
- private intervalId?;
42
- private readonly intervalDelay;
43
39
  private readonly fetchExchangeRate;
44
40
  private readonly includeUsdRate;
45
41
  /**
@@ -59,20 +55,6 @@ export declare class CurrencyRateController extends BaseControllerV2<typeof name
59
55
  state?: Partial<CurrencyRateState>;
60
56
  fetchExchangeRate?: typeof defaultFetchExchangeRate;
61
57
  });
62
- /**
63
- * Start polling for the currency rate.
64
- */
65
- start(): Promise<void>;
66
- /**
67
- * Stop polling for the currency rate.
68
- */
69
- stop(): void;
70
- /**
71
- * Prepare to discard this controller.
72
- *
73
- * This stops any active polling.
74
- */
75
- destroy(): void;
76
58
  /**
77
59
  * Sets a currency to track.
78
60
  *
@@ -80,22 +62,24 @@ export declare class CurrencyRateController extends BaseControllerV2<typeof name
80
62
  */
81
63
  setCurrentCurrency(currentCurrency: string): Promise<void>;
82
64
  /**
83
- * Sets a new native currency.
65
+ * Updates the exchange rate for the current currency and native currency pair.
84
66
  *
85
- * @param symbol - Symbol for the base asset.
67
+ * @param nativeCurrency - The ticker symbol for the chain.
86
68
  */
87
- setNativeCurrency(symbol: string): Promise<void>;
88
- private stopPolling;
69
+ updateExchangeRate(nativeCurrency: string): Promise<void>;
89
70
  /**
90
- * Starts a new polling interval.
71
+ * Prepare to discard this controller.
72
+ *
73
+ * This stops any active polling.
91
74
  */
92
- private startPolling;
75
+ destroy(): void;
93
76
  /**
94
77
  * Updates exchange rate for the current currency.
95
78
  *
79
+ * @param networkClientId - The network client ID used to get a ticker value.
96
80
  * @returns The controller state.
97
81
  */
98
- updateExchangeRate(): Promise<CurrencyRateState | void>;
82
+ _executePoll(networkClientId: NetworkClientId): Promise<void>;
99
83
  }
100
84
  export default CurrencyRateController;
101
85
  //# sourceMappingURL=CurrencyRateController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CurrencyRateController.d.ts","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAO7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,iBAAiB,IAAI,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAEjF;;;;;;;;;GASG;AACH,oBAAY,iBAAiB,GAAG;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,QAAA,MAAM,IAAI,2BAA2B,CAAC;AAEtC,oBAAY,uBAAuB,GAAG;IACpC,IAAI,EAAE,GAAG,OAAO,IAAI,cAAc,CAAC;IACnC,OAAO,EAAE,CAAC,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC;CACvC,CAAC;AAEF,oBAAY,oBAAoB,GAAG;IACjC,IAAI,EAAE,GAAG,OAAO,IAAI,WAAW,CAAC;IAChC,OAAO,EAAE,MAAM,iBAAiB,CAAC;CAClC,CAAC;AAEF,aAAK,qBAAqB,GAAG,6BAA6B,CACxD,OAAO,IAAI,EACX,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,EACL,KAAK,CACN,CAAC;AAsBF;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,gBAAgB,CAC1D,OAAO,IAAI,EACX,iBAAiB,EACjB,qBAAqB,CACtB;;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,UAAU,CAAC,CAAgC;IAEnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAE/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAOhC;;;;;;;;;OASG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,SAAS,EACT,KAAK,EACL,iBAA4C,GAC7C,EAAE;QACD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,qBAAqB,CAAC;QACjC,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,iBAAiB,CAAC,EAAE,OAAO,wBAAwB,CAAC;KACrD;IAaD;;OAEG;IACG,KAAK;IAMX;;OAEG;IACH,IAAI;IAMJ;;;;OAIG;IACM,OAAO;IAKhB;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;IAOhD;;;;OAIG;IACG,iBAAiB,CAAC,MAAM,EAAE,MAAM;IAOtC,OAAO,CAAC,WAAW;IAMnB;;OAEG;YACW,YAAY;IAW1B;;;;OAIG;IACG,kBAAkB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CA+E9D;AAED,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"CurrencyRateController.d.ts","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAC;AAK/E,OAAO,KAAK,EACV,eAAe,EACf,2CAA2C,EAC5C,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,EAAE,iBAAiB,IAAI,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAEjF;;;;;;;GAOG;AACH,oBAAY,iBAAiB,GAAG;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CACnB,MAAM,EACN;QACE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CACF,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,IAAI,2BAA2B,CAAC;AAEtC,oBAAY,uBAAuB,GAAG;IACpC,IAAI,EAAE,GAAG,OAAO,IAAI,cAAc,CAAC;IACnC,OAAO,EAAE,CAAC,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC;CACvC,CAAC;AAEF,oBAAY,oBAAoB,GAAG;IACjC,IAAI,EAAE,GAAG,OAAO,IAAI,WAAW,CAAC;IAChC,OAAO,EAAE,MAAM,iBAAiB,CAAC;CAClC,CAAC;AAEF,aAAK,cAAc,GACf,2CAA2C,GAC3C,oBAAoB,CAAC;AAEzB,aAAK,qBAAqB,GAAG,6BAA6B,CACxD,OAAO,IAAI,EACX,cAAc,EACd,uBAAuB,EACvB,cAAc,CAAC,MAAM,CAAC,EACtB,KAAK,CACN,CAAC;AAkBF;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,iBAAiB,CAC3D,OAAO,IAAI,EACX,iBAAiB,EACjB,qBAAqB,CACtB;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IAEnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAEhC;;;;;;;;;OASG;gBACS,EACV,cAAsB,EACtB,QAAiB,EACjB,SAAS,EACT,KAAK,EACL,iBAA4C,GAC7C,EAAE;QACD,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,qBAAqB,CAAC;QACjC,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACnC,iBAAiB,CAAC,EAAE,OAAO,wBAAwB,CAAC;KACrD;IAYD;;;;OAIG;IACG,kBAAkB,CAAC,eAAe,EAAE,MAAM;IAgBhD;;;;OAIG;IACG,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgE/D;;;;OAIG;IACM,OAAO;IAKhB;;;;;OAKG;IACG,YAAY,CAAC,eAAe,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;CAOpE;AAED,eAAe,sBAAsB,CAAC"}
@@ -8,48 +8,32 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
12
- if (kind === "m") throw new TypeError("Private method is not writable");
13
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
14
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
15
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
16
- };
17
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
18
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
19
- 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");
20
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
- };
22
- var _CurrencyRateController_enabled;
23
11
  Object.defineProperty(exports, "__esModule", { value: true });
24
12
  exports.CurrencyRateController = void 0;
25
- const base_controller_1 = require("@metamask/base-controller");
26
13
  const controller_utils_1 = require("@metamask/controller-utils");
14
+ const polling_controller_1 = require("@metamask/polling-controller");
27
15
  const async_mutex_1 = require("async-mutex");
28
16
  const crypto_compare_1 = require("./crypto-compare");
29
17
  const name = 'CurrencyRateController';
30
18
  const metadata = {
31
- conversionDate: { persist: true, anonymous: true },
32
- conversionRate: { persist: true, anonymous: true },
33
19
  currentCurrency: { persist: true, anonymous: true },
34
- nativeCurrency: { persist: true, anonymous: true },
35
- pendingCurrentCurrency: { persist: false, anonymous: true },
36
- pendingNativeCurrency: { persist: false, anonymous: true },
37
- usdConversionRate: { persist: true, anonymous: true },
20
+ currencyRates: { persist: true, anonymous: true },
38
21
  };
39
22
  const defaultState = {
40
- conversionDate: 0,
41
- conversionRate: 0,
42
23
  currentCurrency: 'usd',
43
- nativeCurrency: 'ETH',
44
- pendingCurrentCurrency: null,
45
- pendingNativeCurrency: null,
46
- usdConversionRate: null,
24
+ currencyRates: {
25
+ ETH: {
26
+ conversionDate: 0,
27
+ conversionRate: 0,
28
+ usdConversionRate: null,
29
+ },
30
+ },
47
31
  };
48
32
  /**
49
33
  * Controller that passively polls on a set interval for an exchange rate from the current network
50
34
  * asset to the user's preferred currency.
51
35
  */
52
- class CurrencyRateController extends base_controller_1.BaseControllerV2 {
36
+ class CurrencyRateController extends polling_controller_1.PollingController {
53
37
  /**
54
38
  * Creates a CurrencyRateController instance.
55
39
  *
@@ -68,39 +52,9 @@ class CurrencyRateController extends base_controller_1.BaseControllerV2 {
68
52
  state: Object.assign(Object.assign({}, defaultState), state),
69
53
  });
70
54
  this.mutex = new async_mutex_1.Mutex();
71
- /**
72
- * A boolean that controls whether or not network requests can be made by the controller
73
- */
74
- _CurrencyRateController_enabled.set(this, void 0);
75
55
  this.includeUsdRate = includeUsdRate;
76
- this.intervalDelay = interval;
56
+ this.setIntervalLength(interval);
77
57
  this.fetchExchangeRate = fetchExchangeRate;
78
- __classPrivateFieldSet(this, _CurrencyRateController_enabled, false, "f");
79
- }
80
- /**
81
- * Start polling for the currency rate.
82
- */
83
- start() {
84
- return __awaiter(this, void 0, void 0, function* () {
85
- __classPrivateFieldSet(this, _CurrencyRateController_enabled, true, "f");
86
- yield this.startPolling();
87
- });
88
- }
89
- /**
90
- * Stop polling for the currency rate.
91
- */
92
- stop() {
93
- __classPrivateFieldSet(this, _CurrencyRateController_enabled, false, "f");
94
- this.stopPolling();
95
- }
96
- /**
97
- * Prepare to discard this controller.
98
- *
99
- * This stops any active polling.
100
- */
101
- destroy() {
102
- super.destroy();
103
- this.stopPolling();
104
58
  }
105
59
  /**
106
60
  * Sets a currency to track.
@@ -109,61 +63,31 @@ class CurrencyRateController extends base_controller_1.BaseControllerV2 {
109
63
  */
110
64
  setCurrentCurrency(currentCurrency) {
111
65
  return __awaiter(this, void 0, void 0, function* () {
112
- this.update((state) => {
113
- state.pendingCurrentCurrency = currentCurrency;
114
- });
115
- yield this.updateExchangeRate();
116
- });
117
- }
118
- /**
119
- * Sets a new native currency.
120
- *
121
- * @param symbol - Symbol for the base asset.
122
- */
123
- setNativeCurrency(symbol) {
124
- return __awaiter(this, void 0, void 0, function* () {
125
- this.update((state) => {
126
- state.pendingNativeCurrency = symbol;
127
- });
128
- yield this.updateExchangeRate();
129
- });
130
- }
131
- stopPolling() {
132
- if (this.intervalId) {
133
- clearInterval(this.intervalId);
134
- }
135
- }
136
- /**
137
- * Starts a new polling interval.
138
- */
139
- startPolling() {
140
- return __awaiter(this, void 0, void 0, function* () {
141
- this.stopPolling();
142
- // TODO: Expose polling currency rate update errors
143
- yield (0, controller_utils_1.safelyExecute)(() => __awaiter(this, void 0, void 0, function* () { return yield this.updateExchangeRate(); }));
144
- this.intervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () {
145
- yield (0, controller_utils_1.safelyExecute)(() => __awaiter(this, void 0, void 0, function* () { return yield this.updateExchangeRate(); }));
146
- }), this.intervalDelay);
66
+ const releaseLock = yield this.mutex.acquire();
67
+ const nativeCurrencies = Object.keys(this.state.currencyRates);
68
+ try {
69
+ this.update(() => {
70
+ return Object.assign(Object.assign({}, defaultState), { currentCurrency });
71
+ });
72
+ }
73
+ finally {
74
+ releaseLock();
75
+ }
76
+ nativeCurrencies.forEach(this.updateExchangeRate.bind(this));
147
77
  });
148
78
  }
149
79
  /**
150
- * Updates exchange rate for the current currency.
80
+ * Updates the exchange rate for the current currency and native currency pair.
151
81
  *
152
- * @returns The controller state.
82
+ * @param nativeCurrency - The ticker symbol for the chain.
153
83
  */
154
- updateExchangeRate() {
84
+ updateExchangeRate(nativeCurrency) {
155
85
  return __awaiter(this, void 0, void 0, function* () {
156
- if (!__classPrivateFieldGet(this, _CurrencyRateController_enabled, "f")) {
157
- console.info('[CurrencyRateController] Not updating exchange rate since network requests have been disabled');
158
- return this.state;
159
- }
160
86
  const releaseLock = yield this.mutex.acquire();
161
- const { currentCurrency: stateCurrentCurrency, nativeCurrency: stateNativeCurrency, pendingCurrentCurrency, pendingNativeCurrency, } = this.state;
87
+ const { currentCurrency, currencyRates } = this.state;
162
88
  let conversionDate = null;
163
89
  let conversionRate = null;
164
90
  let usdConversionRate = null;
165
- const currentCurrency = pendingCurrentCurrency !== null && pendingCurrentCurrency !== void 0 ? pendingCurrentCurrency : stateCurrentCurrency;
166
- const nativeCurrency = pendingNativeCurrency !== null && pendingNativeCurrency !== void 0 ? pendingNativeCurrency : stateNativeCurrency;
167
91
  // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.
168
92
  const nativeCurrencyForExchangeRate = Object.values(controller_utils_1.TESTNET_TICKER_SYMBOLS).includes(nativeCurrency)
169
93
  ? controller_utils_1.FALL_BACK_VS_CURRENCY // ETH
@@ -192,16 +116,12 @@ class CurrencyRateController extends base_controller_1.BaseControllerV2 {
192
116
  try {
193
117
  this.update(() => {
194
118
  return {
195
- conversionDate,
196
- conversionRate,
197
- // we currently allow and handle an empty string as a valid nativeCurrency
198
- // in cases where a user has not entered a native ticker symbol for a custom network
199
- // currentCurrency is not from user input but this protects us from unexpected changes.
200
- nativeCurrency,
119
+ currencyRates: Object.assign(Object.assign({}, currencyRates), { [nativeCurrency]: {
120
+ conversionDate,
121
+ conversionRate,
122
+ usdConversionRate,
123
+ } }),
201
124
  currentCurrency,
202
- pendingCurrentCurrency: null,
203
- pendingNativeCurrency: null,
204
- usdConversionRate,
205
125
  };
206
126
  });
207
127
  }
@@ -209,11 +129,30 @@ class CurrencyRateController extends base_controller_1.BaseControllerV2 {
209
129
  releaseLock();
210
130
  }
211
131
  }
212
- return this.state;
132
+ });
133
+ }
134
+ /**
135
+ * Prepare to discard this controller.
136
+ *
137
+ * This stops any active polling.
138
+ */
139
+ destroy() {
140
+ super.destroy();
141
+ this.stopAllPolling();
142
+ }
143
+ /**
144
+ * Updates exchange rate for the current currency.
145
+ *
146
+ * @param networkClientId - The network client ID used to get a ticker value.
147
+ * @returns The controller state.
148
+ */
149
+ _executePoll(networkClientId) {
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ const networkClient = this.messagingSystem.call('NetworkController:getNetworkClientById', networkClientId);
152
+ yield this.updateExchangeRate(networkClient.configuration.ticker);
213
153
  });
214
154
  }
215
155
  }
216
156
  exports.CurrencyRateController = CurrencyRateController;
217
- _CurrencyRateController_enabled = new WeakMap();
218
157
  exports.default = CurrencyRateController;
219
158
  //# sourceMappingURL=CurrencyRateController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CurrencyRateController.js","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 } from '@metamask/base-controller';\nimport {\n TESTNET_TICKER_SYMBOLS,\n FALL_BACK_VS_CURRENCY,\n safelyExecute,\n} from '@metamask/controller-utils';\nimport { Mutex } from 'async-mutex';\nimport type { Patch } from 'immer';\n\nimport { fetchExchangeRate as defaultFetchExchangeRate } from './crypto-compare';\n\n/**\n * @type CurrencyRateState\n * @property conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch\n * @property conversionRate - Conversion rate from current base asset to the current currency\n * @property currentCurrency - Currently-active ISO 4217 currency code\n * @property nativeCurrency - Symbol for the base asset used for conversion\n * @property pendingCurrentCurrency - The currency being switched to\n * @property pendingNativeCurrency - The base asset currency being switched to\n * @property usdConversionRate - Conversion rate from usd to the current currency\n */\nexport type CurrencyRateState = {\n conversionDate: number | null;\n conversionRate: number | null;\n currentCurrency: string;\n nativeCurrency: string;\n pendingCurrentCurrency: string | null;\n pendingNativeCurrency: string | null;\n usdConversionRate: number | null;\n};\n\nconst name = 'CurrencyRateController';\n\nexport type CurrencyRateStateChange = {\n type: `${typeof name}:stateChange`;\n payload: [CurrencyRateState, Patch[]];\n};\n\nexport type GetCurrencyRateState = {\n type: `${typeof name}:getState`;\n handler: () => CurrencyRateState;\n};\n\ntype CurrencyRateMessenger = RestrictedControllerMessenger<\n typeof name,\n GetCurrencyRateState,\n CurrencyRateStateChange,\n never,\n never\n>;\n\nconst metadata = {\n conversionDate: { persist: true, anonymous: true },\n conversionRate: { persist: true, anonymous: true },\n currentCurrency: { persist: true, anonymous: true },\n nativeCurrency: { persist: true, anonymous: true },\n pendingCurrentCurrency: { persist: false, anonymous: true },\n pendingNativeCurrency: { persist: false, anonymous: true },\n usdConversionRate: { persist: true, anonymous: true },\n};\n\nconst defaultState = {\n conversionDate: 0,\n conversionRate: 0,\n currentCurrency: 'usd',\n nativeCurrency: 'ETH',\n pendingCurrentCurrency: null,\n pendingNativeCurrency: null,\n usdConversionRate: null,\n};\n\n/**\n * Controller that passively polls on a set interval for an exchange rate from the current network\n * asset to the user's preferred currency.\n */\nexport class CurrencyRateController extends BaseControllerV2<\n typeof name,\n CurrencyRateState,\n CurrencyRateMessenger\n> {\n private readonly mutex = new Mutex();\n\n private intervalId?: ReturnType<typeof setTimeout>;\n\n private readonly intervalDelay;\n\n private readonly fetchExchangeRate;\n\n private readonly includeUsdRate;\n\n /**\n * A boolean that controls whether or not network requests can be made by the controller\n */\n #enabled;\n\n /**\n * Creates a CurrencyRateController instance.\n *\n * @param options - Constructor options.\n * @param options.includeUsdRate - Keep track of the USD rate in addition to the current currency rate.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.messenger - A reference to the messaging system.\n * @param options.state - Initial state to set on this controller.\n * @param options.fetchExchangeRate - Fetches the exchange rate from an external API. This option is primarily meant for use in unit tests.\n */\n constructor({\n includeUsdRate = false,\n interval = 180000,\n messenger,\n state,\n fetchExchangeRate = defaultFetchExchangeRate,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n fetchExchangeRate?: typeof defaultFetchExchangeRate;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.includeUsdRate = includeUsdRate;\n this.intervalDelay = interval;\n this.fetchExchangeRate = fetchExchangeRate;\n this.#enabled = false;\n }\n\n /**\n * Start polling for the currency rate.\n */\n async start() {\n this.#enabled = true;\n\n await this.startPolling();\n }\n\n /**\n * Stop polling for the currency rate.\n */\n stop() {\n this.#enabled = false;\n\n this.stopPolling();\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopPolling();\n }\n\n /**\n * Sets a currency to track.\n *\n * @param currentCurrency - ISO 4217 currency code.\n */\n async setCurrentCurrency(currentCurrency: string) {\n this.update((state) => {\n state.pendingCurrentCurrency = currentCurrency;\n });\n await this.updateExchangeRate();\n }\n\n /**\n * Sets a new native currency.\n *\n * @param symbol - Symbol for the base asset.\n */\n async setNativeCurrency(symbol: string) {\n this.update((state) => {\n state.pendingNativeCurrency = symbol;\n });\n await this.updateExchangeRate();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n */\n private async startPolling(): Promise<void> {\n this.stopPolling();\n // TODO: Expose polling currency rate update errors\n\n await safelyExecute(async () => await this.updateExchangeRate());\n\n this.intervalId = setInterval(async () => {\n await safelyExecute(async () => await this.updateExchangeRate());\n }, this.intervalDelay);\n }\n\n /**\n * Updates exchange rate for the current currency.\n *\n * @returns The controller state.\n */\n async updateExchangeRate(): Promise<CurrencyRateState | void> {\n if (!this.#enabled) {\n console.info(\n '[CurrencyRateController] Not updating exchange rate since network requests have been disabled',\n );\n return this.state;\n }\n const releaseLock = await this.mutex.acquire();\n const {\n currentCurrency: stateCurrentCurrency,\n nativeCurrency: stateNativeCurrency,\n pendingCurrentCurrency,\n pendingNativeCurrency,\n } = this.state;\n\n let conversionDate: number | null = null;\n let conversionRate: number | null = null;\n let usdConversionRate: number | null = null;\n const currentCurrency = pendingCurrentCurrency ?? stateCurrentCurrency;\n const nativeCurrency = pendingNativeCurrency ?? stateNativeCurrency;\n\n // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.\n const nativeCurrencyForExchangeRate = Object.values(\n TESTNET_TICKER_SYMBOLS,\n ).includes(nativeCurrency)\n ? FALL_BACK_VS_CURRENCY // ETH\n : nativeCurrency;\n\n try {\n if (\n currentCurrency &&\n nativeCurrency &&\n // if either currency is an empty string we can skip the comparison\n // because it will result in an error from the api and ultimately\n // a null conversionRate either way.\n currentCurrency !== '' &&\n nativeCurrency !== ''\n ) {\n const fetchExchangeRateResponse = await this.fetchExchangeRate(\n currentCurrency,\n nativeCurrencyForExchangeRate,\n this.includeUsdRate,\n );\n\n conversionRate = fetchExchangeRateResponse.conversionRate;\n usdConversionRate = fetchExchangeRateResponse.usdConversionRate;\n conversionDate = Date.now() / 1000;\n }\n } catch (error) {\n if (\n !(\n error instanceof Error &&\n error.message.includes('market does not exist for this coin pair')\n )\n ) {\n throw error;\n }\n } finally {\n try {\n this.update(() => {\n return {\n conversionDate,\n conversionRate,\n // we currently allow and handle an empty string as a valid nativeCurrency\n // in cases where a user has not entered a native ticker symbol for a custom network\n // currentCurrency is not from user input but this protects us from unexpected changes.\n nativeCurrency,\n currentCurrency,\n pendingCurrentCurrency: null,\n pendingNativeCurrency: null,\n usdConversionRate,\n };\n });\n } finally {\n releaseLock();\n }\n }\n return this.state;\n }\n}\n\nexport default CurrencyRateController;\n"]}
1
+ {"version":3,"file":"CurrencyRateController.js","sourceRoot":"","sources":["../src/CurrencyRateController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport {\n TESTNET_TICKER_SYMBOLS,\n FALL_BACK_VS_CURRENCY,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { PollingController } from '@metamask/polling-controller';\nimport { Mutex } from 'async-mutex';\nimport type { Patch } from 'immer';\n\nimport { fetchExchangeRate as defaultFetchExchangeRate } from './crypto-compare';\n\n/**\n * @type CurrencyRateState\n * @property currencyRates - Object keyed by native currency\n * @property currencyRates.conversionDate - Timestamp of conversion rate expressed in ms since UNIX epoch\n * @property currencyRates.conversionRate - Conversion rate from current base asset to the current currency\n * @property currentCurrency - Currently-active ISO 4217 currency code\n * @property usdConversionRate - Conversion rate from usd to the current currency\n */\nexport type CurrencyRateState = {\n currentCurrency: string;\n currencyRates: Record<\n string,\n {\n conversionDate: number | null;\n conversionRate: number | null;\n usdConversionRate: number | null;\n }\n >;\n};\n\nconst name = 'CurrencyRateController';\n\nexport type CurrencyRateStateChange = {\n type: `${typeof name}:stateChange`;\n payload: [CurrencyRateState, Patch[]];\n};\n\nexport type GetCurrencyRateState = {\n type: `${typeof name}:getState`;\n handler: () => CurrencyRateState;\n};\n\ntype AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | GetCurrencyRateState;\n\ntype CurrencyRateMessenger = RestrictedControllerMessenger<\n typeof name,\n AllowedActions,\n CurrencyRateStateChange,\n AllowedActions['type'],\n never\n>;\n\nconst metadata = {\n currentCurrency: { persist: true, anonymous: true },\n currencyRates: { persist: true, anonymous: true },\n};\n\nconst defaultState = {\n currentCurrency: 'usd',\n currencyRates: {\n ETH: {\n conversionDate: 0,\n conversionRate: 0,\n usdConversionRate: null,\n },\n },\n};\n\n/**\n * Controller that passively polls on a set interval for an exchange rate from the current network\n * asset to the user's preferred currency.\n */\nexport class CurrencyRateController extends PollingController<\n typeof name,\n CurrencyRateState,\n CurrencyRateMessenger\n> {\n private readonly mutex = new Mutex();\n\n private readonly fetchExchangeRate;\n\n private readonly includeUsdRate;\n\n /**\n * Creates a CurrencyRateController instance.\n *\n * @param options - Constructor options.\n * @param options.includeUsdRate - Keep track of the USD rate in addition to the current currency rate.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.messenger - A reference to the messaging system.\n * @param options.state - Initial state to set on this controller.\n * @param options.fetchExchangeRate - Fetches the exchange rate from an external API. This option is primarily meant for use in unit tests.\n */\n constructor({\n includeUsdRate = false,\n interval = 180000,\n messenger,\n state,\n fetchExchangeRate = defaultFetchExchangeRate,\n }: {\n includeUsdRate?: boolean;\n interval?: number;\n messenger: CurrencyRateMessenger;\n state?: Partial<CurrencyRateState>;\n fetchExchangeRate?: typeof defaultFetchExchangeRate;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.includeUsdRate = includeUsdRate;\n this.setIntervalLength(interval);\n this.fetchExchangeRate = fetchExchangeRate;\n }\n\n /**\n * Sets a currency to track.\n *\n * @param currentCurrency - ISO 4217 currency code.\n */\n async setCurrentCurrency(currentCurrency: string) {\n const releaseLock = await this.mutex.acquire();\n const nativeCurrencies = Object.keys(this.state.currencyRates);\n try {\n this.update(() => {\n return {\n ...defaultState,\n currentCurrency,\n };\n });\n } finally {\n releaseLock();\n }\n nativeCurrencies.forEach(this.updateExchangeRate.bind(this));\n }\n\n /**\n * Updates the exchange rate for the current currency and native currency pair.\n *\n * @param nativeCurrency - The ticker symbol for the chain.\n */\n async updateExchangeRate(nativeCurrency: string): Promise<void> {\n const releaseLock = await this.mutex.acquire();\n const { currentCurrency, currencyRates } = this.state;\n\n let conversionDate: number | null = null;\n let conversionRate: number | null = null;\n let usdConversionRate: number | null = null;\n\n // For preloaded testnets (Goerli, Sepolia) we want to fetch exchange rate for real ETH.\n const nativeCurrencyForExchangeRate = Object.values(\n TESTNET_TICKER_SYMBOLS,\n ).includes(nativeCurrency)\n ? FALL_BACK_VS_CURRENCY // ETH\n : nativeCurrency;\n\n try {\n if (\n currentCurrency &&\n nativeCurrency &&\n // if either currency is an empty string we can skip the comparison\n // because it will result in an error from the api and ultimately\n // a null conversionRate either way.\n currentCurrency !== '' &&\n nativeCurrency !== ''\n ) {\n const fetchExchangeRateResponse = await this.fetchExchangeRate(\n currentCurrency,\n nativeCurrencyForExchangeRate,\n this.includeUsdRate,\n );\n conversionRate = fetchExchangeRateResponse.conversionRate;\n usdConversionRate = fetchExchangeRateResponse.usdConversionRate;\n conversionDate = Date.now() / 1000;\n }\n } catch (error) {\n if (\n !(\n error instanceof Error &&\n error.message.includes('market does not exist for this coin pair')\n )\n ) {\n throw error;\n }\n } finally {\n try {\n this.update(() => {\n return {\n currencyRates: {\n ...currencyRates,\n [nativeCurrency]: {\n conversionDate,\n conversionRate,\n usdConversionRate,\n },\n },\n currentCurrency,\n };\n });\n } finally {\n releaseLock();\n }\n }\n }\n\n /**\n * Prepare to discard this controller.\n *\n * This stops any active polling.\n */\n override destroy() {\n super.destroy();\n this.stopAllPolling();\n }\n\n /**\n * Updates exchange rate for the current currency.\n *\n * @param networkClientId - The network client ID used to get a ticker value.\n * @returns The controller state.\n */\n async _executePoll(networkClientId: NetworkClientId): Promise<void> {\n const networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n await this.updateExchangeRate(networkClient.configuration.ticker);\n }\n}\n\nexport default CurrencyRateController;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetectionController.d.ts","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TokenDetectionController.d.ts","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,7 @@ class TokenDetectionController extends polling_controller_1.PollingControllerV1
45
45
  this.name = 'TokenDetectionController';
46
46
  this.defaultConfig = Object.assign({ interval: DEFAULT_INTERVAL, selectedAddress: '', disabled: true, chainId: defaultChainId, isDetectionEnabledFromPreferences: defaultUseTokenDetection, isDetectionEnabledForNetwork: (0, assetsUtil_1.isTokenDetectionSupportedForNetwork)(defaultChainId) }, config);
47
47
  this.initialize();
48
+ this.setIntervalLength(this.config.interval);
48
49
  this.getTokensState = getTokensState;
49
50
  this.getTokenListState = getTokenListState;
50
51
  this.addDetectedTokens = addDetectedTokens;
@@ -1 +1 @@
1
- {"version":3,"file":"TokenDetectionController.js","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport {\n safelyExecute,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport { PollingControllerV1 } from '@metamask/polling-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { isTokenDetectionSupportedForNetwork } from './assetsUtil';\nimport type { TokenListState } from './TokenListController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensController, TokensState } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type TokenDetectionConfig\n *\n * TokenDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property selectedAddress - Vault selected address\n * @property chainId - The chain ID of the current network\n * @property isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController\n * @property isDetectionEnabledForNetwork - Boolean to track if detected is enabled for current network\n */\nexport interface TokenDetectionConfig extends BaseConfig {\n interval: number;\n selectedAddress: string;\n chainId: Hex;\n isDetectionEnabledFromPreferences: boolean;\n isDetectionEnabledForNetwork: boolean;\n}\n\n/**\n * Controller that passively polls on a set interval for Tokens auto detection\n */\nexport class TokenDetectionController extends PollingControllerV1<\n TokenDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenDetectionController';\n\n private readonly getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n\n private readonly addDetectedTokens: TokensController['addDetectedTokens'];\n\n private readonly getTokensState: () => TokensState;\n\n private readonly getTokenListState: () => TokenListState;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /**\n * Creates a TokenDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.\n * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.\n * @param options.addDetectedTokens - Add a list of detected tokens.\n * @param options.getTokenListState - Gets the current state of the TokenList controller.\n * @param options.getTokensState - Gets the current state of the Tokens controller.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPreferencesState - Gets the state of the preferences controller.\n * @param options.getNetworkClientById - Gets the network client by ID.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n onPreferencesStateChange,\n onNetworkStateChange,\n onTokenListStateChange,\n getBalancesInSingleCall,\n addDetectedTokens,\n getTokenListState,\n getTokensState,\n getNetworkState,\n getPreferencesState,\n getNetworkClientById,\n }: {\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n onTokenListStateChange: (\n listener: (tokenListState: TokenListState) => void,\n ) => void;\n getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n addDetectedTokens: TokensController['addDetectedTokens'];\n getTokenListState: () => TokenListState;\n getTokensState: () => TokensState;\n getNetworkState: () => NetworkState;\n getPreferencesState: () => PreferencesState;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n },\n config?: Partial<TokenDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n const {\n providerConfig: { chainId: defaultChainId },\n } = getNetworkState();\n const { useTokenDetection: defaultUseTokenDetection } =\n getPreferencesState();\n\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n selectedAddress: '',\n disabled: true,\n chainId: defaultChainId,\n isDetectionEnabledFromPreferences: defaultUseTokenDetection,\n isDetectionEnabledForNetwork:\n isTokenDetectionSupportedForNetwork(defaultChainId),\n ...config,\n };\n\n this.initialize();\n this.getTokensState = getTokensState;\n this.getTokenListState = getTokenListState;\n this.addDetectedTokens = addDetectedTokens;\n this.getBalancesInSingleCall = getBalancesInSingleCall;\n this.getNetworkClientById = getNetworkClientById;\n\n onTokenListStateChange(({ tokenList }) => {\n const hasTokens = Object.keys(tokenList).length;\n\n if (hasTokens) {\n this.detectTokens();\n }\n });\n\n onPreferencesStateChange(({ selectedAddress, useTokenDetection }) => {\n const {\n selectedAddress: currentSelectedAddress,\n isDetectionEnabledFromPreferences,\n } = this.config;\n const isSelectedAddressChanged =\n selectedAddress !== currentSelectedAddress;\n const isDetectionChangedFromPreferences =\n isDetectionEnabledFromPreferences !== useTokenDetection;\n\n this.configure({\n isDetectionEnabledFromPreferences: useTokenDetection,\n selectedAddress,\n });\n\n if (\n useTokenDetection &&\n (isSelectedAddressChanged || isDetectionChangedFromPreferences)\n ) {\n this.detectTokens();\n }\n });\n\n onNetworkStateChange(({ providerConfig: { chainId } }) => {\n const { chainId: currentChainId } = this.config;\n const isDetectionEnabledForNetwork =\n isTokenDetectionSupportedForNetwork(chainId);\n const isChainIdChanged = currentChainId !== chainId;\n\n this.configure({\n chainId,\n isDetectionEnabledForNetwork,\n });\n\n if (isDetectionEnabledForNetwork && isChainIdChanged) {\n this.detectTokens();\n }\n });\n }\n\n /**\n * Start polling for detected tokens.\n */\n async start() {\n this.configure({ disabled: false });\n await this.startPolling();\n }\n\n /**\n * Stop polling for detected tokens.\n */\n stop() {\n this.configure({ disabled: true });\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectTokens();\n this.intervalId = setInterval(async () => {\n await this.detectTokens();\n }, this.config.interval);\n }\n\n private getCorrectChainId(networkClientId?: NetworkClientId) {\n if (networkClientId) {\n return this.getNetworkClientById(networkClientId).configuration.chainId;\n }\n return this.config.chainId;\n }\n\n _executePoll(\n networkClientId: string,\n options: { address: string },\n ): Promise<void> {\n return this.detectTokens({\n networkClientId,\n accountAddress: options.address,\n });\n }\n\n /**\n * Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.\n *\n * @param options - Options to detect tokens.\n * @param options.networkClientId - The ID of the network client to use.\n * @param options.accountAddress - The account address to use.\n */\n async detectTokens(options?: {\n networkClientId?: NetworkClientId;\n accountAddress?: string;\n }) {\n const { networkClientId, accountAddress } = options || {};\n const {\n disabled,\n isDetectionEnabledForNetwork,\n isDetectionEnabledFromPreferences,\n } = this.config;\n if (\n disabled ||\n !isDetectionEnabledForNetwork ||\n !isDetectionEnabledFromPreferences\n ) {\n return;\n }\n const { tokens } = this.getTokensState();\n const selectedAddress = accountAddress || this.config.selectedAddress;\n const chainId = this.getCorrectChainId(networkClientId);\n\n const tokensAddresses = tokens.map(\n /* istanbul ignore next*/ (token) => token.address.toLowerCase(),\n );\n const { tokenList } = this.getTokenListState();\n const tokensToDetect: string[] = [];\n for (const address of Object.keys(tokenList)) {\n if (!tokensAddresses.includes(address)) {\n tokensToDetect.push(address);\n }\n }\n const sliceOfTokensToDetect = [];\n sliceOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);\n sliceOfTokensToDetect[1] = tokensToDetect.slice(\n 1000,\n tokensToDetect.length - 1,\n );\n\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n for (const tokensSlice of sliceOfTokensToDetect) {\n if (tokensSlice.length === 0) {\n break;\n }\n\n await safelyExecute(async () => {\n const balances = await this.getBalancesInSingleCall(\n selectedAddress,\n tokensSlice,\n );\n const tokensToAdd: Token[] = [];\n for (const tokenAddress of Object.keys(balances)) {\n let ignored;\n /* istanbul ignore else */\n const { ignoredTokens } = this.getTokensState();\n if (ignoredTokens.length) {\n ignored = ignoredTokens.find(\n (ignoredTokenAddress) =>\n ignoredTokenAddress === toChecksumHexAddress(tokenAddress),\n );\n }\n const caseInsensitiveTokenKey =\n Object.keys(tokenList).find(\n (i) => i.toLowerCase() === tokenAddress.toLowerCase(),\n ) || '';\n\n if (ignored === undefined) {\n const { decimals, symbol, aggregators, iconUrl, name } =\n tokenList[caseInsensitiveTokenKey];\n tokensToAdd.push({\n address: tokenAddress,\n decimals,\n symbol,\n aggregators,\n image: iconUrl,\n isERC721: false,\n name,\n });\n }\n }\n\n if (tokensToAdd.length) {\n await this.addDetectedTokens(tokensToAdd, {\n selectedAddress,\n chainId,\n });\n }\n });\n }\n }\n}\n\nexport default TokenDetectionController;\n"]}
1
+ {"version":3,"file":"TokenDetectionController.js","sourceRoot":"","sources":["../src/TokenDetectionController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport {\n safelyExecute,\n toChecksumHexAddress,\n} from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkController,\n NetworkState,\n} from '@metamask/network-controller';\nimport { PollingControllerV1 } from '@metamask/polling-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport type { AssetsContractController } from './AssetsContractController';\nimport { isTokenDetectionSupportedForNetwork } from './assetsUtil';\nimport type { TokenListState } from './TokenListController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensController, TokensState } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\n/**\n * @type TokenDetectionConfig\n *\n * TokenDetection configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property selectedAddress - Vault selected address\n * @property chainId - The chain ID of the current network\n * @property isDetectionEnabledFromPreferences - Boolean to track if detection is enabled from PreferencesController\n * @property isDetectionEnabledForNetwork - Boolean to track if detected is enabled for current network\n */\nexport interface TokenDetectionConfig extends BaseConfig {\n interval: number;\n selectedAddress: string;\n chainId: Hex;\n isDetectionEnabledFromPreferences: boolean;\n isDetectionEnabledForNetwork: boolean;\n}\n\n/**\n * Controller that passively polls on a set interval for Tokens auto detection\n */\nexport class TokenDetectionController extends PollingControllerV1<\n TokenDetectionConfig,\n BaseState\n> {\n private intervalId?: ReturnType<typeof setTimeout>;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenDetectionController';\n\n private readonly getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n\n private readonly addDetectedTokens: TokensController['addDetectedTokens'];\n\n private readonly getTokensState: () => TokensState;\n\n private readonly getTokenListState: () => TokenListState;\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /**\n * Creates a TokenDetectionController instance.\n *\n * @param options - The controller options.\n * @param options.onPreferencesStateChange - Allows subscribing to preferences controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network controller state changes.\n * @param options.onTokenListStateChange - Allows subscribing to token list controller state changes.\n * @param options.getBalancesInSingleCall - Gets the balances of a list of tokens for the given address.\n * @param options.addDetectedTokens - Add a list of detected tokens.\n * @param options.getTokenListState - Gets the current state of the TokenList controller.\n * @param options.getTokensState - Gets the current state of the Tokens controller.\n * @param options.getNetworkState - Gets the state of the network controller.\n * @param options.getPreferencesState - Gets the state of the preferences controller.\n * @param options.getNetworkClientById - Gets the network client by ID.\n * @param config - Initial options used to configure this controller.\n * @param state - Initial state to set on this controller.\n */\n constructor(\n {\n onPreferencesStateChange,\n onNetworkStateChange,\n onTokenListStateChange,\n getBalancesInSingleCall,\n addDetectedTokens,\n getTokenListState,\n getTokensState,\n getNetworkState,\n getPreferencesState,\n getNetworkClientById,\n }: {\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n onTokenListStateChange: (\n listener: (tokenListState: TokenListState) => void,\n ) => void;\n getBalancesInSingleCall: AssetsContractController['getBalancesInSingleCall'];\n addDetectedTokens: TokensController['addDetectedTokens'];\n getTokenListState: () => TokenListState;\n getTokensState: () => TokensState;\n getNetworkState: () => NetworkState;\n getPreferencesState: () => PreferencesState;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n },\n config?: Partial<TokenDetectionConfig>,\n state?: Partial<BaseState>,\n ) {\n const {\n providerConfig: { chainId: defaultChainId },\n } = getNetworkState();\n const { useTokenDetection: defaultUseTokenDetection } =\n getPreferencesState();\n\n super(config, state);\n this.defaultConfig = {\n interval: DEFAULT_INTERVAL,\n selectedAddress: '',\n disabled: true,\n chainId: defaultChainId,\n isDetectionEnabledFromPreferences: defaultUseTokenDetection,\n isDetectionEnabledForNetwork:\n isTokenDetectionSupportedForNetwork(defaultChainId),\n ...config,\n };\n\n this.initialize();\n this.setIntervalLength(this.config.interval);\n this.getTokensState = getTokensState;\n this.getTokenListState = getTokenListState;\n this.addDetectedTokens = addDetectedTokens;\n this.getBalancesInSingleCall = getBalancesInSingleCall;\n this.getNetworkClientById = getNetworkClientById;\n\n onTokenListStateChange(({ tokenList }) => {\n const hasTokens = Object.keys(tokenList).length;\n\n if (hasTokens) {\n this.detectTokens();\n }\n });\n\n onPreferencesStateChange(({ selectedAddress, useTokenDetection }) => {\n const {\n selectedAddress: currentSelectedAddress,\n isDetectionEnabledFromPreferences,\n } = this.config;\n const isSelectedAddressChanged =\n selectedAddress !== currentSelectedAddress;\n const isDetectionChangedFromPreferences =\n isDetectionEnabledFromPreferences !== useTokenDetection;\n\n this.configure({\n isDetectionEnabledFromPreferences: useTokenDetection,\n selectedAddress,\n });\n\n if (\n useTokenDetection &&\n (isSelectedAddressChanged || isDetectionChangedFromPreferences)\n ) {\n this.detectTokens();\n }\n });\n\n onNetworkStateChange(({ providerConfig: { chainId } }) => {\n const { chainId: currentChainId } = this.config;\n const isDetectionEnabledForNetwork =\n isTokenDetectionSupportedForNetwork(chainId);\n const isChainIdChanged = currentChainId !== chainId;\n\n this.configure({\n chainId,\n isDetectionEnabledForNetwork,\n });\n\n if (isDetectionEnabledForNetwork && isChainIdChanged) {\n this.detectTokens();\n }\n });\n }\n\n /**\n * Start polling for detected tokens.\n */\n async start() {\n this.configure({ disabled: false });\n await this.startPolling();\n }\n\n /**\n * Stop polling for detected tokens.\n */\n stop() {\n this.configure({ disabled: true });\n this.stopPolling();\n }\n\n private stopPolling() {\n if (this.intervalId) {\n clearInterval(this.intervalId);\n }\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - An interval on which to poll.\n */\n private async startPolling(interval?: number): Promise<void> {\n interval && this.configure({ interval }, false, false);\n this.stopPolling();\n await this.detectTokens();\n this.intervalId = setInterval(async () => {\n await this.detectTokens();\n }, this.config.interval);\n }\n\n private getCorrectChainId(networkClientId?: NetworkClientId) {\n if (networkClientId) {\n return this.getNetworkClientById(networkClientId).configuration.chainId;\n }\n return this.config.chainId;\n }\n\n _executePoll(\n networkClientId: string,\n options: { address: string },\n ): Promise<void> {\n return this.detectTokens({\n networkClientId,\n accountAddress: options.address,\n });\n }\n\n /**\n * Triggers asset ERC20 token auto detection for each contract address in contract metadata on mainnet.\n *\n * @param options - Options to detect tokens.\n * @param options.networkClientId - The ID of the network client to use.\n * @param options.accountAddress - The account address to use.\n */\n async detectTokens(options?: {\n networkClientId?: NetworkClientId;\n accountAddress?: string;\n }) {\n const { networkClientId, accountAddress } = options || {};\n const {\n disabled,\n isDetectionEnabledForNetwork,\n isDetectionEnabledFromPreferences,\n } = this.config;\n if (\n disabled ||\n !isDetectionEnabledForNetwork ||\n !isDetectionEnabledFromPreferences\n ) {\n return;\n }\n const { tokens } = this.getTokensState();\n const selectedAddress = accountAddress || this.config.selectedAddress;\n const chainId = this.getCorrectChainId(networkClientId);\n\n const tokensAddresses = tokens.map(\n /* istanbul ignore next*/ (token) => token.address.toLowerCase(),\n );\n const { tokenList } = this.getTokenListState();\n const tokensToDetect: string[] = [];\n for (const address of Object.keys(tokenList)) {\n if (!tokensAddresses.includes(address)) {\n tokensToDetect.push(address);\n }\n }\n const sliceOfTokensToDetect = [];\n sliceOfTokensToDetect[0] = tokensToDetect.slice(0, 1000);\n sliceOfTokensToDetect[1] = tokensToDetect.slice(\n 1000,\n tokensToDetect.length - 1,\n );\n\n /* istanbul ignore else */\n if (!selectedAddress) {\n return;\n }\n\n for (const tokensSlice of sliceOfTokensToDetect) {\n if (tokensSlice.length === 0) {\n break;\n }\n\n await safelyExecute(async () => {\n const balances = await this.getBalancesInSingleCall(\n selectedAddress,\n tokensSlice,\n );\n const tokensToAdd: Token[] = [];\n for (const tokenAddress of Object.keys(balances)) {\n let ignored;\n /* istanbul ignore else */\n const { ignoredTokens } = this.getTokensState();\n if (ignoredTokens.length) {\n ignored = ignoredTokens.find(\n (ignoredTokenAddress) =>\n ignoredTokenAddress === toChecksumHexAddress(tokenAddress),\n );\n }\n const caseInsensitiveTokenKey =\n Object.keys(tokenList).find(\n (i) => i.toLowerCase() === tokenAddress.toLowerCase(),\n ) || '';\n\n if (ignored === undefined) {\n const { decimals, symbol, aggregators, iconUrl, name } =\n tokenList[caseInsensitiveTokenKey];\n tokensToAdd.push({\n address: tokenAddress,\n decimals,\n symbol,\n aggregators,\n image: iconUrl,\n isERC721: false,\n name,\n });\n }\n }\n\n if (tokensToAdd.length) {\n await this.addDetectedTokens(tokensToAdd, {\n selectedAddress,\n chainId,\n });\n }\n });\n }\n }\n}\n\nexport default TokenDetectionController;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/assets-controllers",
3
- "version": "17.0.0-preview.0a6dab6",
3
+ "version": "18.0.0-preview.4ae7d84",
4
4
  "description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -24,8 +24,9 @@
24
24
  "build:docs": "typedoc",
25
25
  "changelog:validate": "../../scripts/validate-changelog.sh @metamask/assets-controllers",
26
26
  "publish:preview": "yarn npm publish --tag preview",
27
- "test": "jest",
27
+ "test": "jest --reporters=jest-silent-reporter",
28
28
  "test:clean": "jest --clearCache",
29
+ "test:verbose": "jest --verbose",
29
30
  "test:watch": "jest --watch"
30
31
  },
31
32
  "dependencies": {
@@ -40,11 +41,11 @@
40
41
  "@metamask/controller-utils": "^5.0.2",
41
42
  "@metamask/eth-query": "^3.0.1",
42
43
  "@metamask/metamask-eth-abis": "3.0.0",
43
- "@metamask/network-controller": "^15.1.0",
44
+ "@metamask/network-controller": "^15.2.0",
44
45
  "@metamask/polling-controller": "^1.0.1",
45
46
  "@metamask/preferences-controller": "^4.4.3",
46
47
  "@metamask/rpc-errors": "^6.1.0",
47
- "@metamask/utils": "^8.1.0",
48
+ "@metamask/utils": "^8.2.0",
48
49
  "@types/uuid": "^8.3.0",
49
50
  "async-mutex": "^0.2.6",
50
51
  "ethereumjs-util": "^7.0.10",
@@ -54,7 +55,7 @@
54
55
  "uuid": "^8.3.2"
55
56
  },
56
57
  "devDependencies": {
57
- "@metamask/auto-changelog": "^3.4.2",
58
+ "@metamask/auto-changelog": "^3.4.3",
58
59
  "@types/jest": "^27.4.1",
59
60
  "@types/node": "^16.18.54",
60
61
  "deepmerge": "^4.2.2",
@@ -70,7 +71,7 @@
70
71
  },
71
72
  "peerDependencies": {
72
73
  "@metamask/approval-controller": "^4.1.0",
73
- "@metamask/network-controller": "^15.1.0",
74
+ "@metamask/network-controller": "^15.2.0",
74
75
  "@metamask/preferences-controller": "^4.4.3"
75
76
  },
76
77
  "engines": {