@metamask/assets-controllers 43.1.1 → 44.0.0

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 (40) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/TokenBalancesController.cjs +157 -72
  3. package/dist/TokenBalancesController.cjs.map +1 -1
  4. package/dist/TokenBalancesController.d.cts +50 -30
  5. package/dist/TokenBalancesController.d.cts.map +1 -1
  6. package/dist/TokenBalancesController.d.mts +50 -30
  7. package/dist/TokenBalancesController.d.mts.map +1 -1
  8. package/dist/TokenBalancesController.mjs +159 -73
  9. package/dist/TokenBalancesController.mjs.map +1 -1
  10. package/dist/TokenRatesController.cjs +11 -1
  11. package/dist/TokenRatesController.cjs.map +1 -1
  12. package/dist/TokenRatesController.d.cts.map +1 -1
  13. package/dist/TokenRatesController.d.mts.map +1 -1
  14. package/dist/TokenRatesController.mjs +11 -1
  15. package/dist/TokenRatesController.mjs.map +1 -1
  16. package/dist/TokensController.cjs +15 -1
  17. package/dist/TokensController.cjs.map +1 -1
  18. package/dist/TokensController.d.cts +2 -2
  19. package/dist/TokensController.d.cts.map +1 -1
  20. package/dist/TokensController.d.mts +2 -2
  21. package/dist/TokensController.d.mts.map +1 -1
  22. package/dist/TokensController.mjs +15 -1
  23. package/dist/TokensController.mjs.map +1 -1
  24. package/dist/assetsUtil.cjs +2 -2
  25. package/dist/assetsUtil.cjs.map +1 -1
  26. package/dist/assetsUtil.d.cts +3 -3
  27. package/dist/assetsUtil.d.cts.map +1 -1
  28. package/dist/assetsUtil.d.mts +3 -3
  29. package/dist/assetsUtil.d.mts.map +1 -1
  30. package/dist/assetsUtil.mjs +2 -2
  31. package/dist/assetsUtil.mjs.map +1 -1
  32. package/dist/multicall.cjs +350 -0
  33. package/dist/multicall.cjs.map +1 -0
  34. package/dist/multicall.d.cts +26 -0
  35. package/dist/multicall.d.cts.map +1 -0
  36. package/dist/multicall.d.mts +26 -0
  37. package/dist/multicall.d.mts.map +1 -0
  38. package/dist/multicall.mjs +346 -0
  39. package/dist/multicall.mjs.map +1 -0
  40. package/package.json +2 -1
@@ -1,40 +1,38 @@
1
1
  import type { AccountsControllerGetSelectedAccountAction } from "@metamask/accounts-controller";
2
2
  import type { RestrictedControllerMessenger, ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
3
- import { BaseController } from "@metamask/base-controller";
4
- import type { AssetsContractControllerGetERC20BalanceOfAction } from "./AssetsContractController.mjs";
5
- import type { Token } from "./TokenRatesController.mjs";
6
- import type { TokensControllerStateChangeEvent } from "./TokensController.mjs";
3
+ import type { NetworkControllerGetNetworkClientByIdAction, NetworkControllerGetStateAction, NetworkControllerStateChangeEvent } from "@metamask/network-controller";
4
+ import type { PreferencesControllerGetStateAction, PreferencesControllerStateChangeEvent } from "@metamask/preferences-controller";
5
+ import type { Hex } from "@metamask/utils";
6
+ import type { TokensControllerGetStateAction, TokensControllerStateChangeEvent } from "./TokensController.mjs";
7
7
  declare const controllerName = "TokenBalancesController";
8
8
  /**
9
9
  * Token balances controller options
10
10
  * @property interval - Polling interval used to fetch new token balances.
11
- * @property tokens - List of tokens to track balances for.
12
- * @property disabled - If set to true, all tracked tokens contract balances updates are blocked.
11
+ * @property messenger - A controller messenger.
12
+ * @property state - Initial state for the controller.
13
13
  */
14
14
  type TokenBalancesControllerOptions = {
15
15
  interval?: number;
16
- tokens?: Token[];
17
- disabled?: boolean;
18
16
  messenger: TokenBalancesControllerMessenger;
19
17
  state?: Partial<TokenBalancesControllerState>;
20
18
  };
21
19
  /**
22
- * Represents a mapping of hash token contract addresses to their balances.
20
+ * A mapping from account address to chain id to token address to balance.
23
21
  */
24
- type ContractBalances = Record<string, string>;
22
+ type TokenBalances = Record<Hex, Record<Hex, Record<Hex, Hex>>>;
25
23
  /**
26
24
  * Token balances controller state
27
- * @property contractBalances - Hash of token contract addresses to balances
25
+ * @property tokenBalances - A mapping from account address to chain id to token address to balance.
28
26
  */
29
27
  export type TokenBalancesControllerState = {
30
- contractBalances: ContractBalances;
28
+ tokenBalances: TokenBalances;
31
29
  };
32
30
  export type TokenBalancesControllerGetStateAction = ControllerGetStateAction<typeof controllerName, TokenBalancesControllerState>;
33
31
  export type TokenBalancesControllerActions = TokenBalancesControllerGetStateAction;
34
- export type AllowedActions = AccountsControllerGetSelectedAccountAction | AssetsContractControllerGetERC20BalanceOfAction;
32
+ export type AllowedActions = NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction | TokensControllerGetStateAction | PreferencesControllerGetStateAction | AccountsControllerGetSelectedAccountAction;
35
33
  export type TokenBalancesControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, TokenBalancesControllerState>;
36
34
  export type TokenBalancesControllerEvents = TokenBalancesControllerStateChangeEvent;
37
- export type AllowedEvents = TokensControllerStateChangeEvent;
35
+ export type AllowedEvents = TokensControllerStateChangeEvent | PreferencesControllerStateChangeEvent | NetworkControllerStateChangeEvent;
38
36
  export type TokenBalancesControllerMessenger = RestrictedControllerMessenger<typeof controllerName, TokenBalancesControllerActions | AllowedActions, TokenBalancesControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
39
37
  /**
40
38
  * Get the default TokenBalancesController state.
@@ -42,41 +40,63 @@ export type TokenBalancesControllerMessenger = RestrictedControllerMessenger<typ
42
40
  * @returns The default TokenBalancesController state.
43
41
  */
44
42
  export declare function getDefaultTokenBalancesState(): TokenBalancesControllerState;
43
+ /** The input to start polling for the {@link TokenBalancesController} */
44
+ export type TokenBalancesPollingInput = {
45
+ chainId: Hex;
46
+ };
47
+ declare const TokenBalancesController_base: (abstract new (...args: any[]) => {
48
+ readonly "__#787887@#intervalIds": Record<string, NodeJS.Timeout>;
49
+ "__#787887@#intervalLength": number | undefined;
50
+ setIntervalLength(intervalLength: number): void;
51
+ getIntervalLength(): number | undefined;
52
+ _startPolling(input: TokenBalancesPollingInput): void;
53
+ _stopPollingByPollingTokenSetId(key: string): void;
54
+ readonly "__#787879@#pollingTokenSets": Map<string, Set<string>>;
55
+ "__#787879@#callbacks": Map<string, Set<(input: TokenBalancesPollingInput) => void>>;
56
+ _executePoll(input: TokenBalancesPollingInput): Promise<void>;
57
+ startPolling(input: TokenBalancesPollingInput): string;
58
+ stopAllPolling(): void;
59
+ stopPollingByPollingToken(pollingToken: string): void;
60
+ onPollingComplete(input: TokenBalancesPollingInput, callback: (input: TokenBalancesPollingInput) => void): void;
61
+ }) & typeof import("@metamask/base-controller").BaseController;
45
62
  /**
46
63
  * Controller that passively polls on a set interval token balances
47
64
  * for tokens stored in the TokensController
48
65
  */
49
- export declare class TokenBalancesController extends BaseController<typeof controllerName, TokenBalancesControllerState, TokenBalancesControllerMessenger> {
66
+ export declare class TokenBalancesController extends TokenBalancesController_base<typeof controllerName, TokenBalancesControllerState, TokenBalancesControllerMessenger> {
50
67
  #private;
51
68
  /**
52
69
  * Construct a Token Balances Controller.
53
70
  *
54
71
  * @param options - The controller options.
55
72
  * @param options.interval - Polling interval used to fetch new token balances.
56
- * @param options.tokens - List of tokens to track balances for.
57
- * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked.
58
73
  * @param options.state - Initial state to set on this controller.
59
74
  * @param options.messenger - The controller restricted messenger.
60
75
  */
61
- constructor({ interval, tokens, disabled, messenger, state, }: TokenBalancesControllerOptions);
62
- /**
63
- * Allows controller to update tracked tokens contract balances.
64
- */
65
- enable(): void;
76
+ constructor({ interval, messenger, state, }: TokenBalancesControllerOptions);
66
77
  /**
67
- * Blocks controller from updating tracked tokens contract balances.
78
+ * Polls for erc20 token balances.
79
+ * @param input - The input for the poll.
80
+ * @param input.chainId - The chain id to poll token balances on.
68
81
  */
69
- disable(): void;
82
+ _executePoll({ chainId }: TokenBalancesPollingInput): Promise<void>;
70
83
  /**
71
- * Starts a new polling interval.
72
- *
73
- * @param interval - Polling interval used to fetch new token balances.
84
+ * Updates the token balances for the given chain ids.
85
+ * @param input - The input for the update.
86
+ * @param input.chainIds - The chain ids to update token balances for.
87
+ * Or omitted to update all chains that contain tokens.
74
88
  */
75
- poll(interval?: number): Promise<void>;
89
+ updateBalances({ chainIds }?: {
90
+ chainIds?: Hex[];
91
+ }): Promise<void>;
76
92
  /**
77
- * Updates balances for all tokens.
93
+ * Updates token balances for the given chain id.
94
+ * @param input - The input for the update.
95
+ * @param input.chainId - The chain id to update token balances on.
78
96
  */
79
- updateBalances(): Promise<void>;
97
+ updateBalancesByChainId({ chainId }: {
98
+ chainId: Hex;
99
+ }): Promise<void>;
80
100
  /**
81
101
  * Reset the controller state to the default state.
82
102
  */
@@ -1 +1 @@
1
- {"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0CAA0C,EAAE,sCAAsC;AAChG,OAAO,KAAK,EACV,6BAA6B,EAC7B,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAG3D,OAAO,KAAK,EAAE,+CAA+C,EAAE,uCAAmC;AAClG,OAAO,KAAK,EAAE,KAAK,EAAE,mCAA+B;AACpD,OAAO,KAAK,EAAE,gCAAgC,EAAE,+BAA2B;AAI3E,QAAA,MAAM,cAAc,4BAA4B,CAAC;AAMjD;;;;;GAKG;AACH,KAAK,8BAA8B,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,gCAAgC,CAAC;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,gBAAgB,EAAE,gBAAgB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,cAAc,GACtB,0CAA0C,GAC1C,+CAA+C,CAAC;AAEpD,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CACxB,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEJ,MAAM,MAAM,6BAA6B,GACvC,uCAAuC,CAAC;AAE1C,MAAM,MAAM,aAAa,GAAG,gCAAgC,CAAC;AAE7D,MAAM,MAAM,gCAAgC,GAAG,6BAA6B,CAC1E,OAAO,cAAc,EACrB,8BAA8B,GAAG,cAAc,EAC/C,6BAA6B,GAAG,aAAa,EAC7C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,4BAA4B,CAI3E;AAED;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,cAAc,CACzD,OAAO,cAAc,EACrB,4BAA4B,EAC5B,gCAAgC,CACjC;;IASC;;;;;;;;;OASG;gBACS,EACV,QAA2B,EAC3B,MAAW,EACX,QAAgB,EAChB,SAAS,EACT,KAAU,GACX,EAAE,8BAA8B;IA8BjC;;OAEG;IACH,MAAM;IAIN;;OAEG;IACH,OAAO;IAIP;;;;OAIG;IACG,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5C;;OAEG;IACG,cAAc;IA8BpB;;OAEG;IACH,UAAU;CAKX;AAED,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"TokenBalancesController.d.mts","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0CAA0C,EAAE,sCAAsC;AAChG,OAAO,KAAK,EACV,6BAA6B,EAC7B,wBAAwB,EACxB,0BAA0B,EAC3B,kCAAkC;AAGnC,OAAO,KAAK,EACV,2CAA2C,EAC3C,+BAA+B,EAC/B,iCAAiC,EAElC,qCAAqC;AAEtC,OAAO,KAAK,EACV,mCAAmC,EACnC,qCAAqC,EAEtC,yCAAyC;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAQ3C,OAAO,KAAK,EACV,8BAA8B,EAE9B,gCAAgC,EACjC,+BAA2B;AAI5B,QAAA,MAAM,cAAc,4BAA4B,CAAC;AAMjD;;;;;GAKG;AACH,KAAK,8BAA8B,GAAG;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,gCAAgC,CAAC;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC/C,CAAC;AAEF;;GAEG;AACH,KAAK,aAAa,GAAG,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,aAAa,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG,wBAAwB,CAC1E,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACxC,qCAAqC,CAAC;AAExC,MAAM,MAAM,cAAc,GACtB,2CAA2C,GAC3C,+BAA+B,GAC/B,8BAA8B,GAC9B,mCAAmC,GACnC,0CAA0C,CAAC;AAE/C,MAAM,MAAM,uCAAuC,GACjD,0BAA0B,CACxB,OAAO,cAAc,EACrB,4BAA4B,CAC7B,CAAC;AAEJ,MAAM,MAAM,6BAA6B,GACvC,uCAAuC,CAAC;AAE1C,MAAM,MAAM,aAAa,GACrB,gCAAgC,GAChC,qCAAqC,GACrC,iCAAiC,CAAC;AAEtC,MAAM,MAAM,gCAAgC,GAAG,6BAA6B,CAC1E,OAAO,cAAc,EACrB,8BAA8B,GAAG,cAAc,EAC/C,6BAA6B,GAAG,aAAa,EAC7C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,4BAA4B,CAI3E;AAED,yEAAyE;AACzE,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,GAAG,CAAC;CACd,CAAC;;;;;;;;;;;;;;;;AAEF;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,6BAC3C,OAAO,cAAc,EACrB,4BAA4B,EAC5B,gCAAgC,CACjC;;IAOC;;;;;;;OAOG;gBACS,EACV,QAA2B,EAC3B,SAAS,EACT,KAAU,GACX,EAAE,8BAA8B;IA2IjC;;;;OAIG;IACG,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,yBAAyB;IAIzD;;;;;OAKG;IACG,cAAc,CAAC,EAAE,QAAQ,EAAE,GAAE;QAAE,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAA;KAAO;IAQ5D;;;;OAIG;IACG,uBAAuB,CAAC,EAAE,OAAO,EAAE,EAAE;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE;IA+D3D;;OAEG;IACH,UAAU;CAiCX;AAED,eAAe,uBAAuB,CAAC"}
@@ -1,21 +1,27 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ 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");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
1
6
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
7
  if (kind === "m") throw new TypeError("Private method is not writable");
3
8
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
9
  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");
5
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
11
  };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- var _TokenBalancesController_handle, _TokenBalancesController_interval, _TokenBalancesController_tokens, _TokenBalancesController_disabled;
13
- import { BaseController } from "@metamask/base-controller";
14
- import { safelyExecute, toHex } from "@metamask/controller-utils";
12
+ var _TokenBalancesController_instances, _TokenBalancesController_queryMultipleAccounts, _TokenBalancesController_allTokens, _TokenBalancesController_allDetectedTokens, _TokenBalancesController_calculateQueryMultipleAccounts, _TokenBalancesController_onPreferencesStateChange, _TokenBalancesController_onTokensStateChange, _TokenBalancesController_onNetworkStateChange, _TokenBalancesController_getChainIds, _TokenBalancesController_getNetworkClient;
13
+ import { Contract } from "@ethersproject/contracts";
14
+ import { Web3Provider } from "@ethersproject/providers";
15
+ import { toChecksumHexAddress, toHex } from "@metamask/controller-utils";
16
+ import { abiERC20 } from "@metamask/metamask-eth-abis";
17
+ import { StaticIntervalPollingController } from "@metamask/polling-controller";
18
+ import $lodash from "lodash";
19
+ const { isEqual } = $lodash;
20
+ import { multicallOrFallback } from "./multicall.mjs";
15
21
  const DEFAULT_INTERVAL = 180000;
16
22
  const controllerName = 'TokenBalancesController';
17
23
  const metadata = {
18
- contractBalances: { persist: true, anonymous: false },
24
+ tokenBalances: { persist: true, anonymous: false },
19
25
  };
20
26
  /**
21
27
  * Get the default TokenBalancesController state.
@@ -24,25 +30,24 @@ const metadata = {
24
30
  */
25
31
  export function getDefaultTokenBalancesState() {
26
32
  return {
27
- contractBalances: {},
33
+ tokenBalances: {},
28
34
  };
29
35
  }
30
36
  /**
31
37
  * Controller that passively polls on a set interval token balances
32
38
  * for tokens stored in the TokensController
33
39
  */
34
- export class TokenBalancesController extends BaseController {
40
+ export class TokenBalancesController extends StaticIntervalPollingController() {
35
41
  /**
36
42
  * Construct a Token Balances Controller.
37
43
  *
38
44
  * @param options - The controller options.
39
45
  * @param options.interval - Polling interval used to fetch new token balances.
40
- * @param options.tokens - List of tokens to track balances for.
41
- * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked.
42
46
  * @param options.state - Initial state to set on this controller.
43
47
  * @param options.messenger - The controller restricted messenger.
44
48
  */
45
- constructor({ interval = DEFAULT_INTERVAL, tokens = [], disabled = false, messenger, state = {}, }) {
49
+ constructor({ interval = DEFAULT_INTERVAL, messenger, state = {}, }) {
50
+ var _a, _b;
46
51
  super({
47
52
  name: controllerName,
48
53
  metadata,
@@ -52,77 +57,137 @@ export class TokenBalancesController extends BaseController {
52
57
  ...state,
53
58
  },
54
59
  });
55
- _TokenBalancesController_handle.set(this, void 0);
56
- _TokenBalancesController_interval.set(this, void 0);
57
- _TokenBalancesController_tokens.set(this, void 0);
58
- _TokenBalancesController_disabled.set(this, void 0);
59
- __classPrivateFieldSet(this, _TokenBalancesController_disabled, disabled, "f");
60
- __classPrivateFieldSet(this, _TokenBalancesController_interval, interval, "f");
61
- __classPrivateFieldSet(this, _TokenBalancesController_tokens, tokens, "f");
62
- this.messagingSystem.subscribe('TokensController:stateChange', ({ tokens: newTokens, detectedTokens }) => {
63
- __classPrivateFieldSet(this, _TokenBalancesController_tokens, [...newTokens, ...detectedTokens], "f");
64
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
65
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
66
- this.updateBalances();
60
+ _TokenBalancesController_instances.add(this);
61
+ _TokenBalancesController_queryMultipleAccounts.set(this, void 0);
62
+ _TokenBalancesController_allTokens.set(this, void 0);
63
+ _TokenBalancesController_allDetectedTokens.set(this, void 0);
64
+ /**
65
+ * Determines whether to query all accounts, or just the selected account.
66
+ * @param preferences - The preferences state.
67
+ * @param preferences.isMultiAccountBalancesEnabled - whether to query all accounts (mobile).
68
+ * @param preferences.useMultiAccountBalanceChecker - whether to query all accounts (extension).
69
+ * @returns true if all accounts should be queried.
70
+ */
71
+ _TokenBalancesController_calculateQueryMultipleAccounts.set(this, ({ isMultiAccountBalancesEnabled, useMultiAccountBalanceChecker, }) => {
72
+ return Boolean(
73
+ // Note: These settings have different names on extension vs mobile
74
+ isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker);
67
75
  });
68
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
69
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
70
- this.poll();
71
- }
72
- /**
73
- * Allows controller to update tracked tokens contract balances.
74
- */
75
- enable() {
76
- __classPrivateFieldSet(this, _TokenBalancesController_disabled, false, "f");
76
+ /**
77
+ * Handles the event for preferences state changes.
78
+ * @param preferences - The preferences state.
79
+ */
80
+ _TokenBalancesController_onPreferencesStateChange.set(this, (preferences) => {
81
+ // Update the user preference for whether to query multiple accounts.
82
+ const queryMultipleAccounts = __classPrivateFieldGet(this, _TokenBalancesController_calculateQueryMultipleAccounts, "f").call(this, preferences);
83
+ // Refresh when flipped off -> on
84
+ const refresh = queryMultipleAccounts && !__classPrivateFieldGet(this, _TokenBalancesController_queryMultipleAccounts, "f");
85
+ __classPrivateFieldSet(this, _TokenBalancesController_queryMultipleAccounts, queryMultipleAccounts, "f");
86
+ if (refresh) {
87
+ this.updateBalances().catch(console.error);
88
+ }
89
+ });
90
+ /**
91
+ * Handles the event for tokens state changes.
92
+ * @param state - The token state.
93
+ * @param state.allTokens - The state for imported tokens across all chains.
94
+ * @param state.allDetectedTokens - The state for detected tokens across all chains.
95
+ */
96
+ _TokenBalancesController_onTokensStateChange.set(this, ({ allTokens, allDetectedTokens, }) => {
97
+ // Refresh token balances on chains whose tokens have changed.
98
+ const chainIds = __classPrivateFieldGet(this, _TokenBalancesController_getChainIds, "f").call(this, allTokens, allDetectedTokens);
99
+ const chainIdsToUpdate = chainIds.filter((chainId) => !isEqual(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId], allTokens[chainId]) ||
100
+ !isEqual(__classPrivateFieldGet(this, _TokenBalancesController_allDetectedTokens, "f")[chainId], allDetectedTokens[chainId]));
101
+ __classPrivateFieldSet(this, _TokenBalancesController_allTokens, allTokens, "f");
102
+ __classPrivateFieldSet(this, _TokenBalancesController_allDetectedTokens, allDetectedTokens, "f");
103
+ this.updateBalances({ chainIds: chainIdsToUpdate }).catch(console.error);
104
+ });
105
+ /**
106
+ * Returns an array of chain ids that have tokens.
107
+ * @param allTokens - The state for imported tokens across all chains.
108
+ * @param allDetectedTokens - The state for detected tokens across all chains.
109
+ * @returns An array of chain ids that have tokens.
110
+ */
111
+ _TokenBalancesController_getChainIds.set(this, (allTokens, allDetectedTokens) => [
112
+ ...new Set([
113
+ ...Object.keys(allTokens),
114
+ ...Object.keys(allDetectedTokens),
115
+ ]),
116
+ ]);
117
+ this.setIntervalLength(interval);
118
+ // Set initial preference for querying multiple accounts, and subscribe to changes
119
+ __classPrivateFieldSet(this, _TokenBalancesController_queryMultipleAccounts, __classPrivateFieldGet(this, _TokenBalancesController_calculateQueryMultipleAccounts, "f").call(this, this.messagingSystem.call('PreferencesController:getState')), "f");
120
+ this.messagingSystem.subscribe('PreferencesController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onPreferencesStateChange, "f").bind(this));
121
+ // Set initial tokens, and subscribe to changes
122
+ (_a = this, _b = this, {
123
+ allTokens: ({ set value(_c) { __classPrivateFieldSet(_a, _TokenBalancesController_allTokens, _c, "f"); } }).value,
124
+ allDetectedTokens: ({ set value(_c) { __classPrivateFieldSet(_b, _TokenBalancesController_allDetectedTokens, _c, "f"); } }).value,
125
+ } = this.messagingSystem.call('TokensController:getState'));
126
+ this.messagingSystem.subscribe('TokensController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_onTokensStateChange, "f").bind(this));
127
+ // Subscribe to network state changes
128
+ this.messagingSystem.subscribe('NetworkController:stateChange', __classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_onNetworkStateChange).bind(this));
77
129
  }
78
130
  /**
79
- * Blocks controller from updating tracked tokens contract balances.
131
+ * Polls for erc20 token balances.
132
+ * @param input - The input for the poll.
133
+ * @param input.chainId - The chain id to poll token balances on.
80
134
  */
81
- disable() {
82
- __classPrivateFieldSet(this, _TokenBalancesController_disabled, true, "f");
135
+ async _executePoll({ chainId }) {
136
+ await this.updateBalancesByChainId({ chainId });
83
137
  }
84
138
  /**
85
- * Starts a new polling interval.
86
- *
87
- * @param interval - Polling interval used to fetch new token balances.
139
+ * Updates the token balances for the given chain ids.
140
+ * @param input - The input for the update.
141
+ * @param input.chainIds - The chain ids to update token balances for.
142
+ * Or omitted to update all chains that contain tokens.
88
143
  */
89
- async poll(interval) {
90
- if (interval) {
91
- __classPrivateFieldSet(this, _TokenBalancesController_interval, interval, "f");
92
- }
93
- if (__classPrivateFieldGet(this, _TokenBalancesController_handle, "f")) {
94
- clearTimeout(__classPrivateFieldGet(this, _TokenBalancesController_handle, "f"));
95
- }
96
- await safelyExecute(() => this.updateBalances());
97
- __classPrivateFieldSet(this, _TokenBalancesController_handle, setTimeout(() => {
98
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
99
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
100
- this.poll(__classPrivateFieldGet(this, _TokenBalancesController_interval, "f"));
101
- }, __classPrivateFieldGet(this, _TokenBalancesController_interval, "f")), "f");
144
+ async updateBalances({ chainIds } = {}) {
145
+ chainIds ?? (chainIds = __classPrivateFieldGet(this, _TokenBalancesController_getChainIds, "f").call(this, __classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f"), __classPrivateFieldGet(this, _TokenBalancesController_allDetectedTokens, "f")));
146
+ await Promise.allSettled(chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })));
102
147
  }
103
148
  /**
104
- * Updates balances for all tokens.
149
+ * Updates token balances for the given chain id.
150
+ * @param input - The input for the update.
151
+ * @param input.chainId - The chain id to update token balances on.
105
152
  */
106
- async updateBalances() {
107
- if (__classPrivateFieldGet(this, _TokenBalancesController_disabled, "f")) {
108
- return;
153
+ async updateBalancesByChainId({ chainId }) {
154
+ const { address: selectedAccountAddress } = this.messagingSystem.call('AccountsController:getSelectedAccount');
155
+ const isSelectedAccount = (accountAddress) => toChecksumHexAddress(accountAddress) ===
156
+ toChecksumHexAddress(selectedAccountAddress);
157
+ const accountTokenPairs = [];
158
+ const addTokens = ([accountAddress, tokens]) => __classPrivateFieldGet(this, _TokenBalancesController_queryMultipleAccounts, "f") || isSelectedAccount(accountAddress)
159
+ ? tokens.forEach((t) => accountTokenPairs.push({
160
+ accountAddress: accountAddress,
161
+ tokenAddress: t.address,
162
+ }))
163
+ : undefined;
164
+ // Balances will be updated for both imported and detected tokens
165
+ Object.entries(__classPrivateFieldGet(this, _TokenBalancesController_allTokens, "f")[chainId] ?? {}).forEach(addTokens);
166
+ Object.entries(__classPrivateFieldGet(this, _TokenBalancesController_allDetectedTokens, "f")[chainId] ?? {}).forEach(addTokens);
167
+ let results = [];
168
+ if (accountTokenPairs.length > 0) {
169
+ const provider = new Web3Provider(__classPrivateFieldGet(this, _TokenBalancesController_instances, "m", _TokenBalancesController_getNetworkClient).call(this, chainId).provider);
170
+ const calls = accountTokenPairs.map(({ accountAddress, tokenAddress }) => ({
171
+ contract: new Contract(tokenAddress, abiERC20, provider),
172
+ functionSignature: 'balanceOf(address)',
173
+ arguments: [accountAddress],
174
+ }));
175
+ results = await multicallOrFallback(calls, chainId, provider);
109
176
  }
110
- const selectedInternalAccount = this.messagingSystem.call('AccountsController:getSelectedAccount');
111
- const newContractBalances = {};
112
- for (const token of __classPrivateFieldGet(this, _TokenBalancesController_tokens, "f")) {
113
- const { address } = token;
114
- try {
115
- const balance = await this.messagingSystem.call('AssetsContractController:getERC20BalanceOf', address, selectedInternalAccount.address);
116
- newContractBalances[address] = toHex(balance);
117
- token.hasBalanceError = false;
177
+ this.update((state) => {
178
+ var _a, _b;
179
+ // Reset so that when accounts or tokens are removed,
180
+ // their balances are removed rather than left stale.
181
+ for (const accountAddress of Object.keys(state.tokenBalances)) {
182
+ state.tokenBalances[accountAddress][chainId] = {};
118
183
  }
119
- catch (error) {
120
- newContractBalances[address] = toHex(0);
121
- token.hasBalanceError = true;
184
+ for (let i = 0; i < results.length; i++) {
185
+ const { success, value } = results[i];
186
+ const { accountAddress, tokenAddress } = accountTokenPairs[i];
187
+ if (success) {
188
+ ((_b = ((_a = state.tokenBalances)[accountAddress] ?? (_a[accountAddress] = {})))[chainId] ?? (_b[chainId] = {}))[tokenAddress] = toHex(value);
189
+ }
122
190
  }
123
- }
124
- this.update((state) => {
125
- state.contractBalances = newContractBalances;
126
191
  });
127
192
  }
128
193
  /**
@@ -134,6 +199,27 @@ export class TokenBalancesController extends BaseController {
134
199
  });
135
200
  }
136
201
  }
137
- _TokenBalancesController_handle = new WeakMap(), _TokenBalancesController_interval = new WeakMap(), _TokenBalancesController_tokens = new WeakMap(), _TokenBalancesController_disabled = new WeakMap();
202
+ _TokenBalancesController_queryMultipleAccounts = new WeakMap(), _TokenBalancesController_allTokens = new WeakMap(), _TokenBalancesController_allDetectedTokens = new WeakMap(), _TokenBalancesController_calculateQueryMultipleAccounts = new WeakMap(), _TokenBalancesController_onPreferencesStateChange = new WeakMap(), _TokenBalancesController_onTokensStateChange = new WeakMap(), _TokenBalancesController_getChainIds = new WeakMap(), _TokenBalancesController_instances = new WeakSet(), _TokenBalancesController_onNetworkStateChange = function _TokenBalancesController_onNetworkStateChange(_, patches) {
203
+ // Remove state for deleted networks
204
+ for (const patch of patches) {
205
+ if (patch.op === 'remove' &&
206
+ patch.path[0] === 'networkConfigurationsByChainId') {
207
+ const removedChainId = patch.path[1];
208
+ this.update((state) => {
209
+ for (const accountAddress of Object.keys(state.tokenBalances)) {
210
+ delete state.tokenBalances[accountAddress][removedChainId];
211
+ }
212
+ });
213
+ }
214
+ }
215
+ }, _TokenBalancesController_getNetworkClient = function _TokenBalancesController_getNetworkClient(chainId) {
216
+ const { networkConfigurationsByChainId } = this.messagingSystem.call('NetworkController:getState');
217
+ const networkConfiguration = networkConfigurationsByChainId[chainId];
218
+ if (!networkConfiguration) {
219
+ throw new Error(`TokenBalancesController: No network configuration found for chainId ${chainId}`);
220
+ }
221
+ const { networkClientId } = networkConfiguration.rpcEndpoints[networkConfiguration.defaultRpcEndpointIndex];
222
+ return this.messagingSystem.call(`NetworkController:getNetworkClientById`, networkClientId);
223
+ };
138
224
  export default TokenBalancesController;
139
225
  //# sourceMappingURL=TokenBalancesController.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type {\n RestrictedControllerMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { safelyExecute, toHex } from '@metamask/controller-utils';\n\nimport type { AssetsContractControllerGetERC20BalanceOfAction } from './AssetsContractController';\nimport type { Token } from './TokenRatesController';\nimport type { TokensControllerStateChangeEvent } from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n contractBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property tokens - List of tokens to track balances for.\n * @property disabled - If set to true, all tracked tokens contract balances updates are blocked.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n tokens?: Token[];\n disabled?: boolean;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * Represents a mapping of hash token contract addresses to their balances.\n */\ntype ContractBalances = Record<string, string>;\n\n/**\n * Token balances controller state\n * @property contractBalances - Hash of token contract addresses to balances\n */\nexport type TokenBalancesControllerState = {\n contractBalances: ContractBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | AccountsControllerGetSelectedAccountAction\n | AssetsContractControllerGetERC20BalanceOfAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents = TokensControllerStateChangeEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n contractBalances: {},\n };\n}\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends BaseController<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #handle?: ReturnType<typeof setTimeout>;\n\n #interval: number;\n\n #tokens: Token[];\n\n #disabled: boolean;\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.tokens - List of tokens to track balances for.\n * @param options.disabled - If set to true, all tracked tokens contract balances updates are blocked.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n tokens = [],\n disabled = false,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.#disabled = disabled;\n this.#interval = interval;\n this.#tokens = tokens;\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n ({ tokens: newTokens, detectedTokens }) => {\n this.#tokens = [...newTokens, ...detectedTokens];\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.updateBalances();\n },\n );\n\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.poll();\n }\n\n /**\n * Allows controller to update tracked tokens contract balances.\n */\n enable() {\n this.#disabled = false;\n }\n\n /**\n * Blocks controller from updating tracked tokens contract balances.\n */\n disable() {\n this.#disabled = true;\n }\n\n /**\n * Starts a new polling interval.\n *\n * @param interval - Polling interval used to fetch new token balances.\n */\n async poll(interval?: number): Promise<void> {\n if (interval) {\n this.#interval = interval;\n }\n\n if (this.#handle) {\n clearTimeout(this.#handle);\n }\n\n await safelyExecute(() => this.updateBalances());\n\n this.#handle = setTimeout(() => {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.poll(this.#interval);\n }, this.#interval);\n }\n\n /**\n * Updates balances for all tokens.\n */\n async updateBalances() {\n if (this.#disabled) {\n return;\n }\n const selectedInternalAccount = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const newContractBalances: ContractBalances = {};\n for (const token of this.#tokens) {\n const { address } = token;\n try {\n const balance = await this.messagingSystem.call(\n 'AssetsContractController:getERC20BalanceOf',\n address,\n selectedInternalAccount.address,\n );\n newContractBalances[address] = toHex(balance);\n token.hasBalanceError = false;\n } catch (error) {\n newContractBalances[address] = toHex(0);\n token.hasBalanceError = true;\n }\n }\n\n this.update((state) => {\n state.contractBalances = newContractBalances;\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n}\n\nexport default TokenBalancesController;\n"]}
1
+ {"version":3,"file":"TokenBalancesController.mjs","sourceRoot":"","sources":["../src/TokenBalancesController.ts"],"names":[],"mappings":"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","sourcesContent":["import { Contract } from '@ethersproject/contracts';\nimport { Web3Provider } from '@ethersproject/providers';\nimport type { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller';\nimport type {\n RestrictedControllerMessenger,\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n} from '@metamask/base-controller';\nimport { toChecksumHexAddress, toHex } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type {\n NetworkControllerGetNetworkClientByIdAction,\n NetworkControllerGetStateAction,\n NetworkControllerStateChangeEvent,\n NetworkState,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n PreferencesControllerGetStateAction,\n PreferencesControllerStateChangeEvent,\n PreferencesState,\n} from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\nimport type BN from 'bn.js';\nimport type { Patch } from 'immer';\nimport { isEqual } from 'lodash';\n\nimport type { MulticallResult } from './multicall';\nimport { multicallOrFallback } from './multicall';\nimport type { Token } from './TokenRatesController';\nimport type {\n TokensControllerGetStateAction,\n TokensControllerState,\n TokensControllerStateChangeEvent,\n} from './TokensController';\n\nconst DEFAULT_INTERVAL = 180000;\n\nconst controllerName = 'TokenBalancesController';\n\nconst metadata = {\n tokenBalances: { persist: true, anonymous: false },\n};\n\n/**\n * Token balances controller options\n * @property interval - Polling interval used to fetch new token balances.\n * @property messenger - A controller messenger.\n * @property state - Initial state for the controller.\n */\ntype TokenBalancesControllerOptions = {\n interval?: number;\n messenger: TokenBalancesControllerMessenger;\n state?: Partial<TokenBalancesControllerState>;\n};\n\n/**\n * A mapping from account address to chain id to token address to balance.\n */\ntype TokenBalances = Record<Hex, Record<Hex, Record<Hex, Hex>>>;\n\n/**\n * Token balances controller state\n * @property tokenBalances - A mapping from account address to chain id to token address to balance.\n */\nexport type TokenBalancesControllerState = {\n tokenBalances: TokenBalances;\n};\n\nexport type TokenBalancesControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n TokenBalancesControllerState\n>;\n\nexport type TokenBalancesControllerActions =\n TokenBalancesControllerGetStateAction;\n\nexport type AllowedActions =\n | NetworkControllerGetNetworkClientByIdAction\n | NetworkControllerGetStateAction\n | TokensControllerGetStateAction\n | PreferencesControllerGetStateAction\n | AccountsControllerGetSelectedAccountAction;\n\nexport type TokenBalancesControllerStateChangeEvent =\n ControllerStateChangeEvent<\n typeof controllerName,\n TokenBalancesControllerState\n >;\n\nexport type TokenBalancesControllerEvents =\n TokenBalancesControllerStateChangeEvent;\n\nexport type AllowedEvents =\n | TokensControllerStateChangeEvent\n | PreferencesControllerStateChangeEvent\n | NetworkControllerStateChangeEvent;\n\nexport type TokenBalancesControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n TokenBalancesControllerActions | AllowedActions,\n TokenBalancesControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\n/**\n * Get the default TokenBalancesController state.\n *\n * @returns The default TokenBalancesController state.\n */\nexport function getDefaultTokenBalancesState(): TokenBalancesControllerState {\n return {\n tokenBalances: {},\n };\n}\n\n/** The input to start polling for the {@link TokenBalancesController} */\nexport type TokenBalancesPollingInput = {\n chainId: Hex;\n};\n\n/**\n * Controller that passively polls on a set interval token balances\n * for tokens stored in the TokensController\n */\nexport class TokenBalancesController extends StaticIntervalPollingController<TokenBalancesPollingInput>()<\n typeof controllerName,\n TokenBalancesControllerState,\n TokenBalancesControllerMessenger\n> {\n #queryMultipleAccounts: boolean;\n\n #allTokens: TokensControllerState['allTokens'];\n\n #allDetectedTokens: TokensControllerState['allDetectedTokens'];\n\n /**\n * Construct a Token Balances Controller.\n *\n * @param options - The controller options.\n * @param options.interval - Polling interval used to fetch new token balances.\n * @param options.state - Initial state to set on this controller.\n * @param options.messenger - The controller restricted messenger.\n */\n constructor({\n interval = DEFAULT_INTERVAL,\n messenger,\n state = {},\n }: TokenBalancesControllerOptions) {\n super({\n name: controllerName,\n metadata,\n messenger,\n state: {\n ...getDefaultTokenBalancesState(),\n ...state,\n },\n });\n\n this.setIntervalLength(interval);\n\n // Set initial preference for querying multiple accounts, and subscribe to changes\n this.#queryMultipleAccounts = this.#calculateQueryMultipleAccounts(\n this.messagingSystem.call('PreferencesController:getState'),\n );\n this.messagingSystem.subscribe(\n 'PreferencesController:stateChange',\n this.#onPreferencesStateChange.bind(this),\n );\n\n // Set initial tokens, and subscribe to changes\n ({\n allTokens: this.#allTokens,\n allDetectedTokens: this.#allDetectedTokens,\n } = this.messagingSystem.call('TokensController:getState'));\n\n this.messagingSystem.subscribe(\n 'TokensController:stateChange',\n this.#onTokensStateChange.bind(this),\n );\n\n // Subscribe to network state changes\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n this.#onNetworkStateChange.bind(this),\n );\n }\n\n /**\n * Determines whether to query all accounts, or just the selected account.\n * @param preferences - The preferences state.\n * @param preferences.isMultiAccountBalancesEnabled - whether to query all accounts (mobile).\n * @param preferences.useMultiAccountBalanceChecker - whether to query all accounts (extension).\n * @returns true if all accounts should be queried.\n */\n #calculateQueryMultipleAccounts = ({\n isMultiAccountBalancesEnabled,\n useMultiAccountBalanceChecker,\n }: PreferencesState & { useMultiAccountBalanceChecker?: boolean }) => {\n return Boolean(\n // Note: These settings have different names on extension vs mobile\n isMultiAccountBalancesEnabled || useMultiAccountBalanceChecker,\n );\n };\n\n /**\n * Handles the event for preferences state changes.\n * @param preferences - The preferences state.\n */\n #onPreferencesStateChange = (preferences: PreferencesState) => {\n // Update the user preference for whether to query multiple accounts.\n const queryMultipleAccounts =\n this.#calculateQueryMultipleAccounts(preferences);\n\n // Refresh when flipped off -> on\n const refresh = queryMultipleAccounts && !this.#queryMultipleAccounts;\n this.#queryMultipleAccounts = queryMultipleAccounts;\n\n if (refresh) {\n this.updateBalances().catch(console.error);\n }\n };\n\n /**\n * Handles the event for tokens state changes.\n * @param state - The token state.\n * @param state.allTokens - The state for imported tokens across all chains.\n * @param state.allDetectedTokens - The state for detected tokens across all chains.\n */\n #onTokensStateChange = ({\n allTokens,\n allDetectedTokens,\n }: TokensControllerState) => {\n // Refresh token balances on chains whose tokens have changed.\n const chainIds = this.#getChainIds(allTokens, allDetectedTokens);\n const chainIdsToUpdate = chainIds.filter(\n (chainId) =>\n !isEqual(this.#allTokens[chainId], allTokens[chainId]) ||\n !isEqual(this.#allDetectedTokens[chainId], allDetectedTokens[chainId]),\n );\n\n this.#allTokens = allTokens;\n this.#allDetectedTokens = allDetectedTokens;\n\n this.updateBalances({ chainIds: chainIdsToUpdate }).catch(console.error);\n };\n\n /**\n * Handles the event for network state changes.\n * @param _ - The network state.\n * @param patches - An array of patch operations performed on the network state.\n */\n #onNetworkStateChange(_: NetworkState, patches: Patch[]) {\n // Remove state for deleted networks\n for (const patch of patches) {\n if (\n patch.op === 'remove' &&\n patch.path[0] === 'networkConfigurationsByChainId'\n ) {\n const removedChainId = patch.path[1] as Hex;\n\n this.update((state) => {\n for (const accountAddress of Object.keys(state.tokenBalances)) {\n delete state.tokenBalances[accountAddress as Hex][removedChainId];\n }\n });\n }\n }\n }\n\n /**\n * Returns an array of chain ids that have tokens.\n * @param allTokens - The state for imported tokens across all chains.\n * @param allDetectedTokens - The state for detected tokens across all chains.\n * @returns An array of chain ids that have tokens.\n */\n #getChainIds = (\n allTokens: TokensControllerState['allTokens'],\n allDetectedTokens: TokensControllerState['allDetectedTokens'],\n ) =>\n [\n ...new Set([\n ...Object.keys(allTokens),\n ...Object.keys(allDetectedTokens),\n ]),\n ] as Hex[];\n\n /**\n * Polls for erc20 token balances.\n * @param input - The input for the poll.\n * @param input.chainId - The chain id to poll token balances on.\n */\n async _executePoll({ chainId }: TokenBalancesPollingInput) {\n await this.updateBalancesByChainId({ chainId });\n }\n\n /**\n * Updates the token balances for the given chain ids.\n * @param input - The input for the update.\n * @param input.chainIds - The chain ids to update token balances for.\n * Or omitted to update all chains that contain tokens.\n */\n async updateBalances({ chainIds }: { chainIds?: Hex[] } = {}) {\n chainIds ??= this.#getChainIds(this.#allTokens, this.#allDetectedTokens);\n\n await Promise.allSettled(\n chainIds.map((chainId) => this.updateBalancesByChainId({ chainId })),\n );\n }\n\n /**\n * Updates token balances for the given chain id.\n * @param input - The input for the update.\n * @param input.chainId - The chain id to update token balances on.\n */\n async updateBalancesByChainId({ chainId }: { chainId: Hex }) {\n const { address: selectedAccountAddress } = this.messagingSystem.call(\n 'AccountsController:getSelectedAccount',\n );\n\n const isSelectedAccount = (accountAddress: string) =>\n toChecksumHexAddress(accountAddress) ===\n toChecksumHexAddress(selectedAccountAddress);\n\n const accountTokenPairs: { accountAddress: Hex; tokenAddress: Hex }[] = [];\n\n const addTokens = ([accountAddress, tokens]: [string, Token[]]) =>\n this.#queryMultipleAccounts || isSelectedAccount(accountAddress)\n ? tokens.forEach((t) =>\n accountTokenPairs.push({\n accountAddress: accountAddress as Hex,\n tokenAddress: t.address as Hex,\n }),\n )\n : undefined;\n\n // Balances will be updated for both imported and detected tokens\n Object.entries(this.#allTokens[chainId] ?? {}).forEach(addTokens);\n Object.entries(this.#allDetectedTokens[chainId] ?? {}).forEach(addTokens);\n\n let results: MulticallResult[] = [];\n\n if (accountTokenPairs.length > 0) {\n const provider = new Web3Provider(\n this.#getNetworkClient(chainId).provider,\n );\n\n const calls = accountTokenPairs.map(\n ({ accountAddress, tokenAddress }) => ({\n contract: new Contract(tokenAddress, abiERC20, provider),\n functionSignature: 'balanceOf(address)',\n arguments: [accountAddress],\n }),\n );\n\n results = await multicallOrFallback(calls, chainId, provider);\n }\n\n this.update((state) => {\n // Reset so that when accounts or tokens are removed,\n // their balances are removed rather than left stale.\n for (const accountAddress of Object.keys(state.tokenBalances)) {\n state.tokenBalances[accountAddress as Hex][chainId] = {};\n }\n\n for (let i = 0; i < results.length; i++) {\n const { success, value } = results[i];\n const { accountAddress, tokenAddress } = accountTokenPairs[i];\n\n if (success) {\n ((state.tokenBalances[accountAddress] ??= {})[chainId] ??= {})[\n tokenAddress\n ] = toHex(value as BN);\n }\n }\n });\n }\n\n /**\n * Reset the controller state to the default state.\n */\n resetState() {\n this.update(() => {\n return getDefaultTokenBalancesState();\n });\n }\n\n /**\n * Returns the network client for a given chain id\n * @param chainId - The chain id to get the network client for.\n * @returns The network client for the given chain id.\n */\n #getNetworkClient(chainId: Hex) {\n const { networkConfigurationsByChainId } = this.messagingSystem.call(\n 'NetworkController:getState',\n );\n\n const networkConfiguration = networkConfigurationsByChainId[chainId];\n if (!networkConfiguration) {\n throw new Error(\n `TokenBalancesController: No network configuration found for chainId ${chainId}`,\n );\n }\n\n const { networkClientId } =\n networkConfiguration.rpcEndpoints[\n networkConfiguration.defaultRpcEndpointIndex\n ];\n\n return this.messagingSystem.call(\n `NetworkController:getNetworkClientById`,\n networkClientId,\n );\n }\n}\n\nexport default TokenBalancesController;\n"]}
@@ -265,7 +265,7 @@ _TokenRatesController_handle = new WeakMap(), _TokenRatesController_pollState =
265
265
  this.messagingSystem.subscribe('NetworkController:stateChange',
266
266
  // TODO: Either fix this lint violation or explain why it's necessary to ignore.
267
267
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
268
- async ({ selectedNetworkClientId }) => {
268
+ async ({ selectedNetworkClientId }, patches) => {
269
269
  const { configuration: { chainId, ticker }, } = this.messagingSystem.call('NetworkController:getNetworkClientById', selectedNetworkClientId);
270
270
  if (__classPrivateFieldGet(this, _TokenRatesController_chainId, "f") !== chainId || __classPrivateFieldGet(this, _TokenRatesController_ticker, "f") !== ticker) {
271
271
  __classPrivateFieldSet(this, _TokenRatesController_chainId, chainId, "f");
@@ -274,6 +274,16 @@ _TokenRatesController_handle = new WeakMap(), _TokenRatesController_pollState =
274
274
  await this.updateExchangeRates();
275
275
  }
276
276
  }
277
+ // Remove state for deleted networks
278
+ for (const patch of patches) {
279
+ if (patch.op === 'remove' &&
280
+ patch.path[0] === 'networkConfigurationsByChainId') {
281
+ const removedChainId = patch.path[1];
282
+ this.update((state) => {
283
+ delete state.marketData[removedChainId];
284
+ });
285
+ }
286
+ }
277
287
  });
278
288
  }, _TokenRatesController_getTokenAddresses = function _TokenRatesController_getTokenAddresses(chainId) {
279
289
  const getTokens = (allTokens) => Object.values(allTokens ?? {}).flatMap((tokens) => tokens.map(({ address }) => (0, controller_utils_1.toHex)((0, controller_utils_1.toChecksumHexAddress)(address))));