@metamask/assets-controllers 19.0.0 → 20.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.
- package/CHANGELOG.md +28 -1
- package/dist/AccountTrackerController.d.ts +2 -2
- package/dist/AccountTrackerController.d.ts.map +1 -1
- package/dist/AccountTrackerController.js +1 -1
- package/dist/AccountTrackerController.js.map +1 -1
- package/dist/AssetsContractController.d.ts +2 -2
- package/dist/AssetsContractController.d.ts.map +1 -1
- package/dist/AssetsContractController.js +1 -1
- package/dist/AssetsContractController.js.map +1 -1
- package/dist/CurrencyRateController.d.ts +7 -12
- package/dist/CurrencyRateController.d.ts.map +1 -1
- package/dist/CurrencyRateController.js.map +1 -1
- package/dist/NftController.d.ts +2 -2
- package/dist/NftController.d.ts.map +1 -1
- package/dist/NftController.js +1 -1
- package/dist/NftController.js.map +1 -1
- package/dist/TokenBalancesController.d.ts +2 -2
- package/dist/TokenBalancesController.d.ts.map +1 -1
- package/dist/TokenBalancesController.js +1 -1
- package/dist/TokenBalancesController.js.map +1 -1
- package/dist/TokenListController.d.ts +7 -11
- package/dist/TokenListController.d.ts.map +1 -1
- package/dist/TokenListController.js.map +1 -1
- package/dist/TokenRatesController.d.ts +51 -16
- package/dist/TokenRatesController.d.ts.map +1 -1
- package/dist/TokenRatesController.js +84 -29
- package/dist/TokenRatesController.js.map +1 -1
- package/dist/TokensController.d.ts +2 -2
- package/dist/TokensController.d.ts.map +1 -1
- package/dist/TokensController.js +1 -1
- package/dist/TokensController.js.map +1 -1
- package/package.json +11 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenListController.js","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkControllerStateChangeEvent,\n NetworkState,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { PollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\nimport type { Patch } from 'immer';\n\nimport {\n isTokenListSupportedForNetwork,\n formatAggregatorNames,\n formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { fetchTokenListByChainId } from './token-service';\n\nconst DEFAULT_INTERVAL = 24 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 24 * 60 * 60 * 1000;\n\nconst name = 'TokenListController';\n\nexport type TokenListToken = {\n name: string;\n symbol: string;\n decimals: number;\n address: string;\n occurrences: number;\n aggregators: string[];\n iconUrl: string;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\ntype DataCache = {\n timestamp: number;\n data: TokenListMap;\n};\ntype TokensChainsCache = {\n [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n tokenList: TokenListMap;\n tokensChainsCache: TokensChainsCache;\n preventPollingOnNetworkRestart: boolean;\n};\n\nexport type TokenListStateChange = {\n type: `${typeof name}:stateChange`;\n payload: [TokenListState, Patch[]];\n};\n\nexport type GetTokenListState = {\n type: `${typeof name}:getState`;\n handler: () => TokenListState;\n};\ntype TokenListMessenger = RestrictedControllerMessenger<\n typeof name,\n GetTokenListState | NetworkControllerGetNetworkClientByIdAction,\n TokenListStateChange | NetworkControllerStateChangeEvent,\n NetworkControllerGetNetworkClientByIdAction['type'],\n TokenListStateChange['type'] | NetworkControllerStateChangeEvent['type']\n>;\n\nconst metadata = {\n tokenList: { persist: true, anonymous: true },\n tokensChainsCache: { persist: true, anonymous: true },\n preventPollingOnNetworkRestart: { persist: true, anonymous: true },\n};\n\nconst defaultState: TokenListState = {\n tokenList: {},\n tokensChainsCache: {},\n preventPollingOnNetworkRestart: false,\n};\n\n/**\n * Controller that passively polls on a set interval for the list of tokens from metaswaps api\n */\nexport class TokenListController extends PollingController<\n typeof name,\n TokenListState,\n TokenListMessenger\n> {\n private readonly mutex = new Mutex();\n\n private intervalId?: ReturnType<typeof setTimeout>;\n\n private readonly intervalDelay: number;\n\n private readonly cacheRefreshThreshold: number;\n\n private chainId: Hex;\n\n private abortController: AbortController;\n\n /**\n * Creates a TokenListController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.onNetworkStateChange - A function for registering an event handler for network state changes.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.\n * @param options.messenger - A restricted controller messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.preventPollingOnNetworkRestart - Determines whether to prevent poilling on network restart in extension.\n */\n constructor({\n chainId,\n preventPollingOnNetworkRestart = false,\n onNetworkStateChange,\n interval = DEFAULT_INTERVAL,\n cacheRefreshThreshold = DEFAULT_THRESHOLD,\n messenger,\n state,\n }: {\n chainId: Hex;\n preventPollingOnNetworkRestart?: boolean;\n onNetworkStateChange?: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n interval?: number;\n cacheRefreshThreshold?: number;\n messenger: TokenListMessenger;\n state?: Partial<TokenListState>;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.cacheRefreshThreshold = cacheRefreshThreshold;\n this.chainId = chainId;\n this.updatePreventPollingOnNetworkRestart(preventPollingOnNetworkRestart);\n this.abortController = new AbortController();\n if (onNetworkStateChange) {\n onNetworkStateChange(async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n });\n } else {\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n },\n );\n }\n }\n\n /**\n * Updates state and restarts polling on changes to the network controller\n * state.\n *\n * @param networkControllerState - The updated network controller state.\n */\n async #onNetworkControllerStateChange(networkControllerState: NetworkState) {\n if (this.chainId !== networkControllerState.providerConfig.chainId) {\n this.abortController.abort();\n this.abortController = new AbortController();\n this.chainId = networkControllerState.providerConfig.chainId;\n if (this.state.preventPollingOnNetworkRestart) {\n this.clearingTokenListData();\n } else {\n // Ensure tokenList is referencing data from correct network\n this.update(() => {\n return {\n ...this.state,\n tokenList: this.state.tokensChainsCache[this.chainId]?.data || {},\n };\n });\n await this.restart();\n }\n }\n }\n\n /**\n * Start polling for the token list.\n */\n async start() {\n if (!isTokenListSupportedForNetwork(this.chainId)) {\n return;\n }\n await this.startPolling();\n }\n\n /**\n * Restart polling for the token list.\n */\n async restart() {\n this.stopPolling();\n await this.startPolling();\n }\n\n /**\n * Stop polling for the token list.\n */\n stop() {\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 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 await safelyExecute(() => this.fetchTokenList());\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this.fetchTokenList());\n }, this.intervalDelay);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @private\n * @param networkClientId - The ID of the network client triggering the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll(networkClientId: string): Promise<void> {\n return this.fetchTokenList(networkClientId);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @param networkClientId - The ID of the network client triggering the fetch.\n */\n async fetchTokenList(networkClientId?: NetworkClientId): Promise<void> {\n const releaseLock = await this.mutex.acquire();\n let networkClient;\n if (networkClientId) {\n networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n }\n const chainId = networkClient?.configuration.chainId ?? this.chainId;\n try {\n const { tokensChainsCache } = this.state;\n let tokenList: TokenListMap = {};\n const cachedTokens: TokenListMap = await safelyExecute(() =>\n this.#fetchFromCache(chainId),\n );\n if (cachedTokens) {\n // Use non-expired cached tokens\n tokenList = { ...cachedTokens };\n } else {\n // Fetch fresh token list\n const tokensFromAPI: TokenListToken[] = await safelyExecute(() => {\n return fetchTokenListByChainId(chainId, this.abortController.signal);\n });\n if (!tokensFromAPI) {\n // Fallback to expired cached tokens\n tokenList = { ...(tokensChainsCache[chainId]?.data || {}) };\n this.update(() => {\n return {\n ...this.state,\n tokenList,\n tokensChainsCache,\n };\n });\n return;\n }\n // Filtering out tokens with less than 3 occurrences and native tokens\n const filteredTokenList = tokensFromAPI.filter(\n (token) =>\n token.occurrences &&\n token.occurrences >= 3 &&\n token.address !== '0x0000000000000000000000000000000000000000',\n );\n // Removing the tokens with symbol conflicts\n const symbolsList = filteredTokenList.map((token) => token.symbol);\n const duplicateSymbols = [\n ...new Set(\n symbolsList.filter(\n (symbol, index) => symbolsList.indexOf(symbol) !== index,\n ),\n ),\n ];\n const uniqueTokenList = filteredTokenList.filter(\n (token) => !duplicateSymbols.includes(token.symbol),\n );\n for (const token of uniqueTokenList) {\n const formattedToken: TokenListToken = {\n ...token,\n aggregators: formatAggregatorNames(token.aggregators),\n iconUrl: formatIconUrlWithProxy({\n chainId,\n tokenAddress: token.address,\n }),\n };\n tokenList[token.address] = formattedToken;\n }\n }\n const updatedTokensChainsCache: TokensChainsCache = {\n ...tokensChainsCache,\n [chainId]: {\n timestamp: Date.now(),\n data: tokenList,\n },\n };\n this.update(() => {\n return {\n ...this.state,\n tokenList,\n tokensChainsCache: updatedTokensChainsCache,\n };\n });\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Checks if the Cache timestamp is valid,\n * if yes data in cache will be returned\n * otherwise null will be returned.\n * @param chainId - The chain ID of the network for which to fetch the cache.\n * @returns The cached data, or `null` if the cache was expired.\n */\n async #fetchFromCache(chainId: Hex): Promise<TokenListMap | null> {\n const { tokensChainsCache }: TokenListState = this.state;\n const dataCache = tokensChainsCache[chainId];\n const now = Date.now();\n if (\n dataCache?.data &&\n now - dataCache?.timestamp < this.cacheRefreshThreshold\n ) {\n return dataCache.data;\n }\n return null;\n }\n\n /**\n * Clearing tokenList and tokensChainsCache explicitly.\n */\n clearingTokenListData(): void {\n this.update(() => {\n return {\n ...this.state,\n tokenList: {},\n tokensChainsCache: {},\n };\n });\n }\n\n /**\n * Updates preventPollingOnNetworkRestart from extension.\n *\n * @param shouldPreventPolling - Determine whether to prevent polling on network change\n */\n updatePreventPollingOnNetworkRestart(shouldPreventPolling: boolean): void {\n this.update(() => {\n return {\n ...this.state,\n preventPollingOnNetworkRestart: shouldPreventPolling,\n };\n });\n }\n}\n\nexport default TokenListController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenListController.js","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type {\n NetworkClientId,\n NetworkControllerStateChangeEvent,\n NetworkState,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { PollingController } from '@metamask/polling-controller';\nimport type { Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\nimport {\n isTokenListSupportedForNetwork,\n formatAggregatorNames,\n formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { fetchTokenListByChainId } from './token-service';\n\nconst DEFAULT_INTERVAL = 24 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 24 * 60 * 60 * 1000;\n\nconst name = 'TokenListController';\n\nexport type TokenListToken = {\n name: string;\n symbol: string;\n decimals: number;\n address: string;\n occurrences: number;\n aggregators: string[];\n iconUrl: string;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\ntype DataCache = {\n timestamp: number;\n data: TokenListMap;\n};\ntype TokensChainsCache = {\n [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n tokenList: TokenListMap;\n tokensChainsCache: TokensChainsCache;\n preventPollingOnNetworkRestart: boolean;\n};\n\nexport type TokenListStateChange = ControllerStateChangeEvent<\n typeof name,\n TokenListState\n>;\n\nexport type TokenListControllerEvents = TokenListStateChange;\n\nexport type GetTokenListState = ControllerGetStateAction<\n typeof name,\n TokenListState\n>;\n\nexport type TokenListControllerActions = GetTokenListState;\n\ntype AllowedActions = NetworkControllerGetNetworkClientByIdAction;\n\ntype TokenListMessenger = RestrictedControllerMessenger<\n typeof name,\n TokenListControllerActions | AllowedActions,\n TokenListControllerEvents | NetworkControllerStateChangeEvent,\n AllowedActions['type'],\n (TokenListControllerEvents | NetworkControllerStateChangeEvent)['type']\n>;\n\nconst metadata = {\n tokenList: { persist: true, anonymous: true },\n tokensChainsCache: { persist: true, anonymous: true },\n preventPollingOnNetworkRestart: { persist: true, anonymous: true },\n};\n\nconst defaultState: TokenListState = {\n tokenList: {},\n tokensChainsCache: {},\n preventPollingOnNetworkRestart: false,\n};\n\n/**\n * Controller that passively polls on a set interval for the list of tokens from metaswaps api\n */\nexport class TokenListController extends PollingController<\n typeof name,\n TokenListState,\n TokenListMessenger\n> {\n private readonly mutex = new Mutex();\n\n private intervalId?: ReturnType<typeof setTimeout>;\n\n private readonly intervalDelay: number;\n\n private readonly cacheRefreshThreshold: number;\n\n private chainId: Hex;\n\n private abortController: AbortController;\n\n /**\n * Creates a TokenListController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.onNetworkStateChange - A function for registering an event handler for network state changes.\n * @param options.interval - The polling interval, in milliseconds.\n * @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.\n * @param options.messenger - A restricted controller messenger.\n * @param options.state - Initial state to set on this controller.\n * @param options.preventPollingOnNetworkRestart - Determines whether to prevent poilling on network restart in extension.\n */\n constructor({\n chainId,\n preventPollingOnNetworkRestart = false,\n onNetworkStateChange,\n interval = DEFAULT_INTERVAL,\n cacheRefreshThreshold = DEFAULT_THRESHOLD,\n messenger,\n state,\n }: {\n chainId: Hex;\n preventPollingOnNetworkRestart?: boolean;\n onNetworkStateChange?: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n interval?: number;\n cacheRefreshThreshold?: number;\n messenger: TokenListMessenger;\n state?: Partial<TokenListState>;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...defaultState, ...state },\n });\n this.intervalDelay = interval;\n this.cacheRefreshThreshold = cacheRefreshThreshold;\n this.chainId = chainId;\n this.updatePreventPollingOnNetworkRestart(preventPollingOnNetworkRestart);\n this.abortController = new AbortController();\n if (onNetworkStateChange) {\n onNetworkStateChange(async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n });\n } else {\n this.messagingSystem.subscribe(\n 'NetworkController:stateChange',\n async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n },\n );\n }\n }\n\n /**\n * Updates state and restarts polling on changes to the network controller\n * state.\n *\n * @param networkControllerState - The updated network controller state.\n */\n async #onNetworkControllerStateChange(networkControllerState: NetworkState) {\n if (this.chainId !== networkControllerState.providerConfig.chainId) {\n this.abortController.abort();\n this.abortController = new AbortController();\n this.chainId = networkControllerState.providerConfig.chainId;\n if (this.state.preventPollingOnNetworkRestart) {\n this.clearingTokenListData();\n } else {\n // Ensure tokenList is referencing data from correct network\n this.update(() => {\n return {\n ...this.state,\n tokenList: this.state.tokensChainsCache[this.chainId]?.data || {},\n };\n });\n await this.restart();\n }\n }\n }\n\n /**\n * Start polling for the token list.\n */\n async start() {\n if (!isTokenListSupportedForNetwork(this.chainId)) {\n return;\n }\n await this.startPolling();\n }\n\n /**\n * Restart polling for the token list.\n */\n async restart() {\n this.stopPolling();\n await this.startPolling();\n }\n\n /**\n * Stop polling for the token list.\n */\n stop() {\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 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 await safelyExecute(() => this.fetchTokenList());\n this.intervalId = setInterval(async () => {\n await safelyExecute(() => this.fetchTokenList());\n }, this.intervalDelay);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @private\n * @param networkClientId - The ID of the network client triggering the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll(networkClientId: string): Promise<void> {\n return this.fetchTokenList(networkClientId);\n }\n\n /**\n * Fetching token list from the Token Service API.\n *\n * @param networkClientId - The ID of the network client triggering the fetch.\n */\n async fetchTokenList(networkClientId?: NetworkClientId): Promise<void> {\n const releaseLock = await this.mutex.acquire();\n let networkClient;\n if (networkClientId) {\n networkClient = this.messagingSystem.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n }\n const chainId = networkClient?.configuration.chainId ?? this.chainId;\n try {\n const { tokensChainsCache } = this.state;\n let tokenList: TokenListMap = {};\n const cachedTokens: TokenListMap = await safelyExecute(() =>\n this.#fetchFromCache(chainId),\n );\n if (cachedTokens) {\n // Use non-expired cached tokens\n tokenList = { ...cachedTokens };\n } else {\n // Fetch fresh token list\n const tokensFromAPI: TokenListToken[] = await safelyExecute(() => {\n return fetchTokenListByChainId(chainId, this.abortController.signal);\n });\n if (!tokensFromAPI) {\n // Fallback to expired cached tokens\n tokenList = { ...(tokensChainsCache[chainId]?.data || {}) };\n this.update(() => {\n return {\n ...this.state,\n tokenList,\n tokensChainsCache,\n };\n });\n return;\n }\n // Filtering out tokens with less than 3 occurrences and native tokens\n const filteredTokenList = tokensFromAPI.filter(\n (token) =>\n token.occurrences &&\n token.occurrences >= 3 &&\n token.address !== '0x0000000000000000000000000000000000000000',\n );\n // Removing the tokens with symbol conflicts\n const symbolsList = filteredTokenList.map((token) => token.symbol);\n const duplicateSymbols = [\n ...new Set(\n symbolsList.filter(\n (symbol, index) => symbolsList.indexOf(symbol) !== index,\n ),\n ),\n ];\n const uniqueTokenList = filteredTokenList.filter(\n (token) => !duplicateSymbols.includes(token.symbol),\n );\n for (const token of uniqueTokenList) {\n const formattedToken: TokenListToken = {\n ...token,\n aggregators: formatAggregatorNames(token.aggregators),\n iconUrl: formatIconUrlWithProxy({\n chainId,\n tokenAddress: token.address,\n }),\n };\n tokenList[token.address] = formattedToken;\n }\n }\n const updatedTokensChainsCache: TokensChainsCache = {\n ...tokensChainsCache,\n [chainId]: {\n timestamp: Date.now(),\n data: tokenList,\n },\n };\n this.update(() => {\n return {\n ...this.state,\n tokenList,\n tokensChainsCache: updatedTokensChainsCache,\n };\n });\n } finally {\n releaseLock();\n }\n }\n\n /**\n * Checks if the Cache timestamp is valid,\n * if yes data in cache will be returned\n * otherwise null will be returned.\n * @param chainId - The chain ID of the network for which to fetch the cache.\n * @returns The cached data, or `null` if the cache was expired.\n */\n async #fetchFromCache(chainId: Hex): Promise<TokenListMap | null> {\n const { tokensChainsCache }: TokenListState = this.state;\n const dataCache = tokensChainsCache[chainId];\n const now = Date.now();\n if (\n dataCache?.data &&\n now - dataCache?.timestamp < this.cacheRefreshThreshold\n ) {\n return dataCache.data;\n }\n return null;\n }\n\n /**\n * Clearing tokenList and tokensChainsCache explicitly.\n */\n clearingTokenListData(): void {\n this.update(() => {\n return {\n ...this.state,\n tokenList: {},\n tokensChainsCache: {},\n };\n });\n }\n\n /**\n * Updates preventPollingOnNetworkRestart from extension.\n *\n * @param shouldPreventPolling - Determine whether to prevent polling on network change\n */\n updatePreventPollingOnNetworkRestart(shouldPreventPolling: boolean): void {\n this.update(() => {\n return {\n ...this.state,\n preventPollingOnNetworkRestart: shouldPreventPolling,\n };\n });\n }\n}\n\nexport default TokenListController;\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BaseConfig, BaseState } from '@metamask/base-controller';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import type { NetworkClientId, NetworkController, NetworkState } from '@metamask/network-controller';
|
|
3
|
+
import { PollingControllerV1 } from '@metamask/polling-controller';
|
|
4
4
|
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
5
5
|
import type { Hex } from '@metamask/utils';
|
|
6
6
|
import type { TokensState } from './TokensController';
|
|
@@ -78,17 +78,18 @@ interface ContractExchangeRates {
|
|
|
78
78
|
* @type TokenRatesState
|
|
79
79
|
*
|
|
80
80
|
* Token rates controller state
|
|
81
|
-
* @property contractExchangeRates - Hash of token contract addresses to exchange rates
|
|
82
|
-
* @property
|
|
81
|
+
* @property contractExchangeRates - Hash of token contract addresses to exchange rates (single globally selected chain, will be deprecated soon)
|
|
82
|
+
* @property contractExchangeRatesByChainId - Hash of token contract addresses to exchange rates keyed by chain ID and native currency (ticker)
|
|
83
83
|
*/
|
|
84
84
|
export interface TokenRatesState extends BaseState {
|
|
85
85
|
contractExchangeRates: ContractExchangeRates;
|
|
86
|
+
contractExchangeRatesByChainId: Record<Hex, Record<string, ContractExchangeRates>>;
|
|
86
87
|
}
|
|
87
88
|
/**
|
|
88
89
|
* Controller that passively polls on a set interval for token-to-fiat exchange rates
|
|
89
90
|
* for tokens stored in the TokensController
|
|
90
91
|
*/
|
|
91
|
-
export declare class TokenRatesController extends
|
|
92
|
+
export declare class TokenRatesController extends PollingControllerV1<TokenRatesConfig, TokenRatesState> {
|
|
92
93
|
#private;
|
|
93
94
|
private handle?;
|
|
94
95
|
private tokenList;
|
|
@@ -98,10 +99,14 @@ export declare class TokenRatesController extends BaseController<TokenRatesConfi
|
|
|
98
99
|
* Name of this controller used during composition
|
|
99
100
|
*/
|
|
100
101
|
name: string;
|
|
102
|
+
private readonly getNetworkClientById;
|
|
101
103
|
/**
|
|
102
104
|
* Creates a TokenRatesController instance.
|
|
103
105
|
*
|
|
104
106
|
* @param options - The controller options.
|
|
107
|
+
* @param options.interval - The polling interval in ms
|
|
108
|
+
* @param options.threshold - The duration in ms before metadata fetched from CoinGecko is considered stale
|
|
109
|
+
* @param options.getNetworkClientById - Gets the network client with the given id from the NetworkController.
|
|
105
110
|
* @param options.chainId - The chain ID of the current network.
|
|
106
111
|
* @param options.ticker - The ticker for the current network.
|
|
107
112
|
* @param options.selectedAddress - The current selected address.
|
|
@@ -111,7 +116,10 @@ export declare class TokenRatesController extends BaseController<TokenRatesConfi
|
|
|
111
116
|
* @param config - Initial options used to configure this controller.
|
|
112
117
|
* @param state - Initial state to set on this controller.
|
|
113
118
|
*/
|
|
114
|
-
constructor({ chainId: initialChainId, ticker: initialTicker, selectedAddress: initialSelectedAddress, onPreferencesStateChange, onTokensStateChange, onNetworkStateChange, }: {
|
|
119
|
+
constructor({ interval, threshold, getNetworkClientById, chainId: initialChainId, ticker: initialTicker, selectedAddress: initialSelectedAddress, onPreferencesStateChange, onTokensStateChange, onNetworkStateChange, }: {
|
|
120
|
+
interval?: number;
|
|
121
|
+
threshold?: number;
|
|
122
|
+
getNetworkClientById: NetworkController['getNetworkClientById'];
|
|
115
123
|
chainId: Hex;
|
|
116
124
|
ticker: string;
|
|
117
125
|
selectedAddress: string;
|
|
@@ -130,43 +138,70 @@ export declare class TokenRatesController extends BaseController<TokenRatesConfi
|
|
|
130
138
|
/**
|
|
131
139
|
* Fetches a pairs of token address and native currency.
|
|
132
140
|
*
|
|
133
|
-
* @param chainSlug -
|
|
134
|
-
* @param vsCurrency -
|
|
141
|
+
* @param chainSlug - The chain string identifier.
|
|
142
|
+
* @param vsCurrency - The currency used to generate pairs against the tokens.
|
|
143
|
+
* @param tokenAddresses - The addresses for the token contracts.
|
|
135
144
|
* @returns The exchange rates for the given pairs.
|
|
136
145
|
*/
|
|
137
|
-
fetchExchangeRate(chainSlug: string, vsCurrency: string): Promise<CoinGeckoResponse>;
|
|
146
|
+
fetchExchangeRate(chainSlug: string, vsCurrency: string, tokenAddresses?: string[]): Promise<CoinGeckoResponse>;
|
|
138
147
|
/**
|
|
139
148
|
* Checks if the current native currency is a supported vs currency to use
|
|
140
149
|
* to query for token exchange rates.
|
|
141
150
|
*
|
|
142
|
-
* @param nativeCurrency - The native currency
|
|
151
|
+
* @param nativeCurrency - The native currency to check.
|
|
143
152
|
* @returns A boolean indicating whether it's a supported vsCurrency.
|
|
144
153
|
*/
|
|
145
154
|
private checkIsSupportedVsCurrency;
|
|
146
155
|
/**
|
|
147
|
-
* Gets
|
|
156
|
+
* Gets the slug from cached supported platforms CoinGecko API response for the chain ID.
|
|
148
157
|
* If cached supported platforms response is stale, fetches and updates it.
|
|
149
158
|
*
|
|
150
|
-
* @
|
|
159
|
+
* @param chainId - The chain ID.
|
|
160
|
+
* @returns The CoinGecko slug for the chain ID.
|
|
151
161
|
*/
|
|
152
|
-
getChainSlug(): Promise<string | null>;
|
|
162
|
+
getChainSlug(chainId?: Hex): Promise<string | null>;
|
|
153
163
|
/**
|
|
154
164
|
* Updates exchange rates for all tokens.
|
|
155
165
|
*/
|
|
156
166
|
updateExchangeRates(): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Updates exchange rates for all tokens.
|
|
169
|
+
*
|
|
170
|
+
* @param options - The options to fetch exchange rates.
|
|
171
|
+
* @param options.chainId - The chain ID.
|
|
172
|
+
* @param options.nativeCurrency - The ticker for the chain.
|
|
173
|
+
* @param options.tokenAddresses - The addresses for the token contracts.
|
|
174
|
+
*/
|
|
175
|
+
updateExchangeRatesByChainId({ chainId, nativeCurrency, tokenAddresses, }: {
|
|
176
|
+
chainId: Hex;
|
|
177
|
+
nativeCurrency: string;
|
|
178
|
+
tokenAddresses: string[];
|
|
179
|
+
}): Promise<void>;
|
|
157
180
|
/**
|
|
158
181
|
* Checks if the active network's native currency is supported by the coingecko API.
|
|
159
182
|
* If supported, it fetches and maps contractExchange rates to a format to be consumed by the UI.
|
|
160
183
|
* If not supported, it fetches contractExchange rates and maps them from token/fallback-currency
|
|
161
184
|
* to token/nativeCurrency.
|
|
162
185
|
*
|
|
163
|
-
* @param nativeCurrency - The native currency
|
|
164
|
-
* @param
|
|
186
|
+
* @param nativeCurrency - The native currency ticker against which to fetch exchange rates
|
|
187
|
+
* @param chainSlug - The unique slug used to id the chain by the coingecko api
|
|
165
188
|
* should be used to query token exchange rates.
|
|
189
|
+
* @param tokenAddresses - The addresses for the token contracts against which to fetch exchange rates.
|
|
166
190
|
* @returns An object with conversion rates for each token
|
|
167
191
|
* related to the network's native currency.
|
|
168
192
|
*/
|
|
169
|
-
fetchAndMapExchangeRates(nativeCurrency: string,
|
|
193
|
+
fetchAndMapExchangeRates(nativeCurrency: string, chainSlug: string, tokenAddresses?: string[]): Promise<ContractExchangeRates>;
|
|
194
|
+
/**
|
|
195
|
+
* Updates token rates for the given networkClientId and contract addresses
|
|
196
|
+
*
|
|
197
|
+
* @param networkClientId - The network client ID used to get a ticker value.
|
|
198
|
+
* @param options - The polling options.
|
|
199
|
+
* @param options.tokenAddresses - The addresses for the token contracts.
|
|
200
|
+
* @returns The controller state.
|
|
201
|
+
*/
|
|
202
|
+
_executePoll(networkClientId: NetworkClientId, options: {
|
|
203
|
+
tokenAddresses: string[];
|
|
204
|
+
}): Promise<void>;
|
|
170
205
|
}
|
|
171
206
|
export default TokenRatesController;
|
|
172
207
|
//# sourceMappingURL=TokenRatesController.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenRatesController.d.ts","sourceRoot":"","sources":["../src/TokenRatesController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"TokenRatesController.d.ts","sourceRoot":"","sources":["../src/TokenRatesController.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,8 +22,8 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
22
22
|
var _TokenRatesController_instances, _TokenRatesController_pollState, _TokenRatesController_updateTokenList, _TokenRatesController_stopPoll, _TokenRatesController_poll;
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.TokenRatesController = void 0;
|
|
25
|
-
const base_controller_1 = require("@metamask/base-controller");
|
|
26
25
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
26
|
+
const polling_controller_1 = require("@metamask/polling-controller");
|
|
27
27
|
const crypto_compare_1 = require("./crypto-compare");
|
|
28
28
|
var PollState;
|
|
29
29
|
(function (PollState) {
|
|
@@ -61,11 +61,14 @@ function findChainSlug(chainId, data) {
|
|
|
61
61
|
* Controller that passively polls on a set interval for token-to-fiat exchange rates
|
|
62
62
|
* for tokens stored in the TokensController
|
|
63
63
|
*/
|
|
64
|
-
class TokenRatesController extends
|
|
64
|
+
class TokenRatesController extends polling_controller_1.PollingControllerV1 {
|
|
65
65
|
/**
|
|
66
66
|
* Creates a TokenRatesController instance.
|
|
67
67
|
*
|
|
68
68
|
* @param options - The controller options.
|
|
69
|
+
* @param options.interval - The polling interval in ms
|
|
70
|
+
* @param options.threshold - The duration in ms before metadata fetched from CoinGecko is considered stale
|
|
71
|
+
* @param options.getNetworkClientById - Gets the network client with the given id from the NetworkController.
|
|
69
72
|
* @param options.chainId - The chain ID of the current network.
|
|
70
73
|
* @param options.ticker - The ticker for the current network.
|
|
71
74
|
* @param options.selectedAddress - The current selected address.
|
|
@@ -75,7 +78,7 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
75
78
|
* @param config - Initial options used to configure this controller.
|
|
76
79
|
* @param state - Initial state to set on this controller.
|
|
77
80
|
*/
|
|
78
|
-
constructor({ chainId: initialChainId, ticker: initialTicker, selectedAddress: initialSelectedAddress, onPreferencesStateChange, onTokensStateChange, onNetworkStateChange, }, config, state) {
|
|
81
|
+
constructor({ interval = 3 * 60 * 1000, threshold = 6 * 60 * 60 * 1000, getNetworkClientById, chainId: initialChainId, ticker: initialTicker, selectedAddress: initialSelectedAddress, onPreferencesStateChange, onTokensStateChange, onNetworkStateChange, }, config, state) {
|
|
79
82
|
super(config, state);
|
|
80
83
|
_TokenRatesController_instances.add(this);
|
|
81
84
|
this.tokenList = [];
|
|
@@ -93,19 +96,22 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
93
96
|
*/
|
|
94
97
|
this.name = 'TokenRatesController';
|
|
95
98
|
this.defaultConfig = {
|
|
99
|
+
interval,
|
|
100
|
+
threshold,
|
|
96
101
|
disabled: false,
|
|
97
|
-
interval: 3 * 60 * 1000,
|
|
98
102
|
nativeCurrency: initialTicker,
|
|
99
103
|
chainId: initialChainId,
|
|
100
104
|
selectedAddress: initialSelectedAddress,
|
|
101
105
|
allTokens: {},
|
|
102
106
|
allDetectedTokens: {},
|
|
103
|
-
threshold: 6 * 60 * 60 * 1000,
|
|
104
107
|
};
|
|
105
108
|
this.defaultState = {
|
|
106
109
|
contractExchangeRates: {},
|
|
110
|
+
contractExchangeRatesByChainId: {},
|
|
107
111
|
};
|
|
108
112
|
this.initialize();
|
|
113
|
+
this.setIntervalLength(interval);
|
|
114
|
+
this.getNetworkClientById = getNetworkClientById;
|
|
109
115
|
if (config === null || config === void 0 ? void 0 : config.disabled) {
|
|
110
116
|
this.configure({ disabled: true }, false, false);
|
|
111
117
|
}
|
|
@@ -163,13 +169,14 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
163
169
|
/**
|
|
164
170
|
* Fetches a pairs of token address and native currency.
|
|
165
171
|
*
|
|
166
|
-
* @param chainSlug -
|
|
167
|
-
* @param vsCurrency -
|
|
172
|
+
* @param chainSlug - The chain string identifier.
|
|
173
|
+
* @param vsCurrency - The currency used to generate pairs against the tokens.
|
|
174
|
+
* @param tokenAddresses - The addresses for the token contracts.
|
|
168
175
|
* @returns The exchange rates for the given pairs.
|
|
169
176
|
*/
|
|
170
|
-
fetchExchangeRate(chainSlug, vsCurrency) {
|
|
177
|
+
fetchExchangeRate(chainSlug, vsCurrency, tokenAddresses = this.tokenList.map((token) => token.address)) {
|
|
171
178
|
return __awaiter(this, void 0, void 0, function* () {
|
|
172
|
-
const tokenPairs =
|
|
179
|
+
const tokenPairs = tokenAddresses.join(',');
|
|
173
180
|
const query = `contract_addresses=${tokenPairs}&vs_currencies=${vsCurrency.toLowerCase()}`;
|
|
174
181
|
return (0, controller_utils_1.handleFetch)(CoinGeckoApi.getTokenPriceURL(chainSlug, query));
|
|
175
182
|
});
|
|
@@ -178,7 +185,7 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
178
185
|
* Checks if the current native currency is a supported vs currency to use
|
|
179
186
|
* to query for token exchange rates.
|
|
180
187
|
*
|
|
181
|
-
* @param nativeCurrency - The native currency
|
|
188
|
+
* @param nativeCurrency - The native currency to check.
|
|
182
189
|
* @returns A boolean indicating whether it's a supported vsCurrency.
|
|
183
190
|
*/
|
|
184
191
|
checkIsSupportedVsCurrency(nativeCurrency) {
|
|
@@ -198,14 +205,15 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
198
205
|
});
|
|
199
206
|
}
|
|
200
207
|
/**
|
|
201
|
-
* Gets
|
|
208
|
+
* Gets the slug from cached supported platforms CoinGecko API response for the chain ID.
|
|
202
209
|
* If cached supported platforms response is stale, fetches and updates it.
|
|
203
210
|
*
|
|
204
|
-
* @
|
|
211
|
+
* @param chainId - The chain ID.
|
|
212
|
+
* @returns The CoinGecko slug for the chain ID.
|
|
205
213
|
*/
|
|
206
|
-
getChainSlug() {
|
|
214
|
+
getChainSlug(chainId = this.config.chainId) {
|
|
207
215
|
return __awaiter(this, void 0, void 0, function* () {
|
|
208
|
-
const { threshold
|
|
216
|
+
const { threshold } = this.config;
|
|
209
217
|
const { data, timestamp } = this.supportedChains;
|
|
210
218
|
const now = Date.now();
|
|
211
219
|
if (now - timestamp > threshold) {
|
|
@@ -227,19 +235,47 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
227
235
|
if (this.tokenList.length === 0 || this.disabled) {
|
|
228
236
|
return;
|
|
229
237
|
}
|
|
230
|
-
const
|
|
238
|
+
const { chainId, nativeCurrency } = this.config;
|
|
239
|
+
const tokenAddresses = this.tokenList.map((token) => token.address);
|
|
240
|
+
yield this.updateExchangeRatesByChainId({
|
|
241
|
+
chainId,
|
|
242
|
+
nativeCurrency,
|
|
243
|
+
tokenAddresses,
|
|
244
|
+
});
|
|
245
|
+
this.update({
|
|
246
|
+
contractExchangeRates: this.state.contractExchangeRatesByChainId[chainId][nativeCurrency],
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Updates exchange rates for all tokens.
|
|
252
|
+
*
|
|
253
|
+
* @param options - The options to fetch exchange rates.
|
|
254
|
+
* @param options.chainId - The chain ID.
|
|
255
|
+
* @param options.nativeCurrency - The ticker for the chain.
|
|
256
|
+
* @param options.tokenAddresses - The addresses for the token contracts.
|
|
257
|
+
*/
|
|
258
|
+
updateExchangeRatesByChainId({ chainId, nativeCurrency, tokenAddresses, }) {
|
|
259
|
+
var _a, _b;
|
|
260
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
261
|
+
if (!tokenAddresses.length) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const chainSlug = yield this.getChainSlug(chainId);
|
|
231
265
|
let newContractExchangeRates = {};
|
|
232
|
-
if (!
|
|
233
|
-
|
|
234
|
-
const address = (0, controller_utils_1.toChecksumHexAddress)(
|
|
266
|
+
if (!chainSlug) {
|
|
267
|
+
tokenAddresses.forEach((tokenAddress) => {
|
|
268
|
+
const address = (0, controller_utils_1.toChecksumHexAddress)(tokenAddress);
|
|
235
269
|
newContractExchangeRates[address] = undefined;
|
|
236
270
|
});
|
|
237
271
|
}
|
|
238
272
|
else {
|
|
239
|
-
|
|
240
|
-
newContractExchangeRates = yield this.fetchAndMapExchangeRates(nativeCurrency, slug);
|
|
273
|
+
newContractExchangeRates = yield this.fetchAndMapExchangeRates(nativeCurrency, chainSlug, tokenAddresses);
|
|
241
274
|
}
|
|
242
|
-
this.
|
|
275
|
+
const existingContractExchangeRatesForChainId = (_a = this.state.contractExchangeRatesByChainId[chainId]) !== null && _a !== void 0 ? _a : {};
|
|
276
|
+
this.update({
|
|
277
|
+
contractExchangeRatesByChainId: Object.assign(Object.assign({}, this.state.contractExchangeRatesByChainId), { [chainId]: Object.assign(Object.assign({}, existingContractExchangeRatesForChainId), { [nativeCurrency]: Object.assign(Object.assign({}, ((_b = existingContractExchangeRatesForChainId[nativeCurrency]) !== null && _b !== void 0 ? _b : {})), newContractExchangeRates) }) }),
|
|
278
|
+
});
|
|
243
279
|
});
|
|
244
280
|
}
|
|
245
281
|
/**
|
|
@@ -248,23 +284,24 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
248
284
|
* If not supported, it fetches contractExchange rates and maps them from token/fallback-currency
|
|
249
285
|
* to token/nativeCurrency.
|
|
250
286
|
*
|
|
251
|
-
* @param nativeCurrency - The native currency
|
|
252
|
-
* @param
|
|
287
|
+
* @param nativeCurrency - The native currency ticker against which to fetch exchange rates
|
|
288
|
+
* @param chainSlug - The unique slug used to id the chain by the coingecko api
|
|
253
289
|
* should be used to query token exchange rates.
|
|
290
|
+
* @param tokenAddresses - The addresses for the token contracts against which to fetch exchange rates.
|
|
254
291
|
* @returns An object with conversion rates for each token
|
|
255
292
|
* related to the network's native currency.
|
|
256
293
|
*/
|
|
257
|
-
fetchAndMapExchangeRates(nativeCurrency,
|
|
294
|
+
fetchAndMapExchangeRates(nativeCurrency, chainSlug, tokenAddresses = this.tokenList.map((token) => token.address)) {
|
|
258
295
|
return __awaiter(this, void 0, void 0, function* () {
|
|
259
296
|
const contractExchangeRates = {};
|
|
260
297
|
// check if native currency is supported as a vs_currency by the API
|
|
261
298
|
const nativeCurrencySupported = yield this.checkIsSupportedVsCurrency(nativeCurrency);
|
|
262
299
|
if (nativeCurrencySupported) {
|
|
263
300
|
// If it is we can do a simple fetch against the CoinGecko API
|
|
264
|
-
const prices = yield this.fetchExchangeRate(
|
|
265
|
-
|
|
266
|
-
const price = prices[
|
|
267
|
-
contractExchangeRates[(0, controller_utils_1.toChecksumHexAddress)(
|
|
301
|
+
const prices = yield this.fetchExchangeRate(chainSlug, nativeCurrency, tokenAddresses);
|
|
302
|
+
tokenAddresses.forEach((tokenAddress) => {
|
|
303
|
+
const price = prices[tokenAddress.toLowerCase()];
|
|
304
|
+
contractExchangeRates[(0, controller_utils_1.toChecksumHexAddress)(tokenAddress)] = price
|
|
268
305
|
? price[nativeCurrency.toLowerCase()]
|
|
269
306
|
: 0;
|
|
270
307
|
});
|
|
@@ -279,7 +316,7 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
279
316
|
tokenExchangeRates,
|
|
280
317
|
{ conversionRate: vsCurrencyToNativeCurrencyConversionRate },
|
|
281
318
|
] = yield Promise.all([
|
|
282
|
-
this.fetchExchangeRate(
|
|
319
|
+
this.fetchExchangeRate(chainSlug, controller_utils_1.FALL_BACK_VS_CURRENCY, tokenAddresses),
|
|
283
320
|
(0, crypto_compare_1.fetchExchangeRate)(nativeCurrency, controller_utils_1.FALL_BACK_VS_CURRENCY, false),
|
|
284
321
|
]);
|
|
285
322
|
}
|
|
@@ -300,6 +337,24 @@ class TokenRatesController extends base_controller_1.BaseController {
|
|
|
300
337
|
return contractExchangeRates;
|
|
301
338
|
});
|
|
302
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Updates token rates for the given networkClientId and contract addresses
|
|
342
|
+
*
|
|
343
|
+
* @param networkClientId - The network client ID used to get a ticker value.
|
|
344
|
+
* @param options - The polling options.
|
|
345
|
+
* @param options.tokenAddresses - The addresses for the token contracts.
|
|
346
|
+
* @returns The controller state.
|
|
347
|
+
*/
|
|
348
|
+
_executePoll(networkClientId, options) {
|
|
349
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
350
|
+
const networkClient = this.getNetworkClientById(networkClientId);
|
|
351
|
+
yield this.updateExchangeRatesByChainId({
|
|
352
|
+
chainId: networkClient.configuration.chainId,
|
|
353
|
+
nativeCurrency: networkClient.configuration.ticker,
|
|
354
|
+
tokenAddresses: options.tokenAddresses,
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
303
358
|
}
|
|
304
359
|
exports.TokenRatesController = TokenRatesController;
|
|
305
360
|
_TokenRatesController_pollState = new WeakMap(), _TokenRatesController_instances = new WeakSet(), _TokenRatesController_updateTokenList = function _TokenRatesController_updateTokenList() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenRatesController.js","sourceRoot":"","sources":["../src/TokenRatesController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport {\n safelyExecute,\n handleFetch,\n toChecksumHexAddress,\n FALL_BACK_VS_CURRENCY,\n toHex,\n} from '@metamask/controller-utils';\nimport type { NetworkState } from '@metamask/network-controller';\nimport type { PreferencesState } from '@metamask/preferences-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { fetchExchangeRate as fetchNativeExchangeRate } from './crypto-compare';\nimport type { TokensState } from './TokensController';\n\n/**\n * @type CoinGeckoResponse\n *\n * CoinGecko API response representation\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface CoinGeckoResponse {\n [address: string]: {\n [currency: string]: number;\n };\n}\n/**\n * @type CoinGeckoPlatform\n *\n * CoinGecko supported platform API representation\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface CoinGeckoPlatform {\n id: string;\n chain_identifier: null | number;\n name: string;\n shortname: string;\n}\n\n/**\n * @type Token\n *\n * Token representation\n * @property address - Hex address of the token contract\n * @property decimals - Number of decimals the token uses\n * @property symbol - Symbol of the token\n * @property image - Image of the token, url or bit32 image\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface Token {\n address: string;\n decimals: number;\n symbol: string;\n aggregators?: string[];\n image?: string;\n balanceError?: unknown;\n isERC721?: boolean;\n name?: string;\n}\n\n/**\n * @type TokenRatesConfig\n *\n * Token rates controller configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property nativeCurrency - Current native currency selected to use base of rates\n * @property chainId - Current network chainId\n * @property tokens - List of tokens to track exchange rates for\n * @property threshold - Threshold to invalidate the supportedChains\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface TokenRatesConfig extends BaseConfig {\n interval: number;\n nativeCurrency: string;\n chainId: Hex;\n selectedAddress: string;\n allTokens: { [chainId: Hex]: { [key: string]: Token[] } };\n allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } };\n threshold: number;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface ContractExchangeRates {\n [address: string]: number | undefined;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface SupportedChainsCache {\n timestamp: number;\n data: CoinGeckoPlatform[] | null;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface SupportedVsCurrenciesCache {\n timestamp: number;\n data: string[];\n}\n\nenum PollState {\n Active = 'Active',\n Inactive = 'Inactive',\n}\n\n/**\n * @type TokenRatesState\n *\n * Token rates controller state\n * @property contractExchangeRates - Hash of token contract addresses to exchange rates\n * @property supportedChains - Cached chain data\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface TokenRatesState extends BaseState {\n contractExchangeRates: ContractExchangeRates;\n}\n\nconst CoinGeckoApi = {\n BASE_URL: 'https://api.coingecko.com/api/v3',\n getTokenPriceURL(chainSlug: string, query: string) {\n return `${this.BASE_URL}/simple/token_price/${chainSlug}?${query}`;\n },\n getPlatformsURL() {\n return `${this.BASE_URL}/asset_platforms`;\n },\n getSupportedVsCurrencies() {\n return `${this.BASE_URL}/simple/supported_vs_currencies`;\n },\n};\n\n/**\n * Finds the chain slug in the data array given a chainId.\n *\n * @param chainId - The current chain ID.\n * @param data - A list platforms supported by the CoinGecko API.\n * @returns The CoinGecko slug for the given chain ID, or `null` if the slug was not found.\n */\nfunction findChainSlug(\n chainId: Hex,\n data: CoinGeckoPlatform[] | null,\n): string | null {\n if (!data) {\n return null;\n }\n const chain =\n data.find(\n ({ chain_identifier }) =>\n chain_identifier !== null && toHex(chain_identifier) === chainId,\n ) ?? null;\n return chain?.id || null;\n}\n\n/**\n * Controller that passively polls on a set interval for token-to-fiat exchange rates\n * for tokens stored in the TokensController\n */\nexport class TokenRatesController extends BaseController<\n TokenRatesConfig,\n TokenRatesState\n> {\n private handle?: ReturnType<typeof setTimeout>;\n\n private tokenList: Token[] = [];\n\n private supportedChains: SupportedChainsCache = {\n timestamp: 0,\n data: null,\n };\n\n private supportedVsCurrencies: SupportedVsCurrenciesCache = {\n timestamp: 0,\n data: [],\n };\n\n #pollState = PollState.Inactive;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenRatesController';\n\n /**\n * Creates a TokenRatesController instance.\n *\n * @param options - The controller options.\n * @param options.chainId - The chain ID of the current network.\n * @param options.ticker - The ticker for the current network.\n * @param options.selectedAddress - The current selected address.\n * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.\n * @param options.onTokensStateChange - Allows subscribing to token controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network state changes.\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 chainId: initialChainId,\n ticker: initialTicker,\n selectedAddress: initialSelectedAddress,\n onPreferencesStateChange,\n onTokensStateChange,\n onNetworkStateChange,\n }: {\n chainId: Hex;\n ticker: string;\n selectedAddress: string;\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onTokensStateChange: (\n listener: (tokensState: TokensState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n },\n config?: Partial<TokenRatesConfig>,\n state?: Partial<TokenRatesState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n disabled: false,\n interval: 3 * 60 * 1000,\n nativeCurrency: initialTicker,\n chainId: initialChainId,\n selectedAddress: initialSelectedAddress,\n allTokens: {}, // TODO: initialize these correctly, maybe as part of BaseControllerV2 migration\n allDetectedTokens: {},\n threshold: 6 * 60 * 60 * 1000,\n };\n\n this.defaultState = {\n contractExchangeRates: {},\n };\n this.initialize();\n if (config?.disabled) {\n this.configure({ disabled: true }, false, false);\n }\n this.#updateTokenList();\n\n onPreferencesStateChange(async ({ selectedAddress }) => {\n if (this.config.selectedAddress !== selectedAddress) {\n this.configure({ selectedAddress });\n this.#updateTokenList();\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n });\n\n onTokensStateChange(async ({ allTokens, allDetectedTokens }) => {\n // These two state properties are assumed to be immutable\n if (\n this.config.allTokens !== allTokens ||\n this.config.allDetectedTokens !== allDetectedTokens\n ) {\n this.configure({ allTokens, allDetectedTokens });\n this.#updateTokenList();\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n });\n\n onNetworkStateChange(async ({ providerConfig }) => {\n const { chainId, ticker } = providerConfig;\n if (\n this.config.chainId !== chainId ||\n this.config.nativeCurrency !== ticker\n ) {\n this.update({ contractExchangeRates: {} });\n this.configure({ chainId, nativeCurrency: ticker });\n this.#updateTokenList();\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n });\n }\n\n #updateTokenList() {\n const { allTokens, allDetectedTokens } = this.config;\n const tokens =\n allTokens[this.config.chainId]?.[this.config.selectedAddress] || [];\n const detectedTokens =\n allDetectedTokens[this.config.chainId]?.[this.config.selectedAddress] ||\n [];\n this.tokenList = [...tokens, ...detectedTokens];\n }\n\n /**\n * Start (or restart) polling.\n */\n async start() {\n this.#stopPoll();\n this.#pollState = PollState.Active;\n await this.#poll();\n }\n\n /**\n * Stop polling.\n */\n stop() {\n this.#stopPoll();\n this.#pollState = PollState.Inactive;\n }\n\n /**\n * Clear the active polling timer, if present.\n */\n #stopPoll() {\n if (this.handle) {\n clearTimeout(this.handle);\n }\n }\n\n /**\n * Poll for exchange rate updates.\n */\n async #poll() {\n await safelyExecute(() => this.updateExchangeRates());\n\n // Poll using recursive `setTimeout` instead of `setInterval` so that\n // requests don't stack if they take longer than the polling interval\n this.handle = setTimeout(() => {\n this.#poll();\n }, this.config.interval);\n }\n\n /**\n * Fetches a pairs of token address and native currency.\n *\n * @param chainSlug - Chain string identifier.\n * @param vsCurrency - Query according to tokens in tokenList and native currency.\n * @returns The exchange rates for the given pairs.\n */\n async fetchExchangeRate(\n chainSlug: string,\n vsCurrency: string,\n ): Promise<CoinGeckoResponse> {\n const tokenPairs = this.tokenList.map((token) => token.address).join(',');\n const query = `contract_addresses=${tokenPairs}&vs_currencies=${vsCurrency.toLowerCase()}`;\n return handleFetch(CoinGeckoApi.getTokenPriceURL(chainSlug, query));\n }\n\n /**\n * Checks if the current native currency is a supported vs currency to use\n * to query for token exchange rates.\n *\n * @param nativeCurrency - The native currency of the currently active network.\n * @returns A boolean indicating whether it's a supported vsCurrency.\n */\n private async checkIsSupportedVsCurrency(nativeCurrency: string) {\n const { threshold } = this.config;\n const { timestamp, data } = this.supportedVsCurrencies;\n\n const now = Date.now();\n\n if (now - timestamp > threshold) {\n const currencies = await handleFetch(\n CoinGeckoApi.getSupportedVsCurrencies(),\n );\n this.supportedVsCurrencies = {\n data: currencies,\n timestamp: Date.now(),\n };\n return currencies.includes(nativeCurrency.toLowerCase());\n }\n\n return data.includes(nativeCurrency.toLowerCase());\n }\n\n /**\n * Gets current chain ID slug from cached supported platforms CoinGecko API response.\n * If cached supported platforms response is stale, fetches and updates it.\n *\n * @returns The CoinGecko slug for the current chain ID.\n */\n async getChainSlug(): Promise<string | null> {\n const { threshold, chainId } = this.config;\n const { data, timestamp } = this.supportedChains;\n\n const now = Date.now();\n\n if (now - timestamp > threshold) {\n const platforms = await handleFetch(CoinGeckoApi.getPlatformsURL());\n this.supportedChains = {\n data: platforms,\n timestamp: Date.now(),\n };\n return findChainSlug(chainId, platforms);\n }\n\n return findChainSlug(chainId, data);\n }\n\n /**\n * Updates exchange rates for all tokens.\n */\n async updateExchangeRates() {\n if (this.tokenList.length === 0 || this.disabled) {\n return;\n }\n const slug = await this.getChainSlug();\n\n let newContractExchangeRates: ContractExchangeRates = {};\n if (!slug) {\n this.tokenList.forEach((token) => {\n const address = toChecksumHexAddress(token.address);\n newContractExchangeRates[address] = undefined;\n });\n } else {\n const { nativeCurrency } = this.config;\n newContractExchangeRates = await this.fetchAndMapExchangeRates(\n nativeCurrency,\n slug,\n );\n }\n this.update({ contractExchangeRates: newContractExchangeRates });\n }\n\n /**\n * Checks if the active network's native currency is supported by the coingecko API.\n * If supported, it fetches and maps contractExchange rates to a format to be consumed by the UI.\n * If not supported, it fetches contractExchange rates and maps them from token/fallback-currency\n * to token/nativeCurrency.\n *\n * @param nativeCurrency - The native currency of the currently active network.\n * @param slug - The unique slug used to id the chain by the coingecko api\n * should be used to query token exchange rates.\n * @returns An object with conversion rates for each token\n * related to the network's native currency.\n */\n async fetchAndMapExchangeRates(\n nativeCurrency: string,\n slug: string,\n ): Promise<ContractExchangeRates> {\n const contractExchangeRates: ContractExchangeRates = {};\n\n // check if native currency is supported as a vs_currency by the API\n const nativeCurrencySupported = await this.checkIsSupportedVsCurrency(\n nativeCurrency,\n );\n\n if (nativeCurrencySupported) {\n // If it is we can do a simple fetch against the CoinGecko API\n const prices = await this.fetchExchangeRate(slug, nativeCurrency);\n this.tokenList.forEach((token) => {\n const price = prices[token.address.toLowerCase()];\n contractExchangeRates[toChecksumHexAddress(token.address)] = price\n ? price[nativeCurrency.toLowerCase()]\n : 0;\n });\n } else {\n // if native currency is not supported we need to use a fallback vsCurrency, get the exchange rates\n // in token/fallback-currency format and convert them to expected token/nativeCurrency format.\n let tokenExchangeRates;\n let vsCurrencyToNativeCurrencyConversionRate = 0;\n try {\n [\n tokenExchangeRates,\n { conversionRate: vsCurrencyToNativeCurrencyConversionRate },\n ] = await Promise.all([\n this.fetchExchangeRate(slug, FALL_BACK_VS_CURRENCY),\n fetchNativeExchangeRate(nativeCurrency, FALL_BACK_VS_CURRENCY, false),\n ]);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('market does not exist for this coin pair')\n ) {\n return {};\n }\n throw error;\n }\n\n for (const [tokenAddress, conversion] of Object.entries(\n tokenExchangeRates,\n )) {\n const tokenToVsCurrencyConversionRate =\n conversion[FALL_BACK_VS_CURRENCY.toLowerCase()];\n contractExchangeRates[toChecksumHexAddress(tokenAddress)] =\n tokenToVsCurrencyConversionRate *\n vsCurrencyToNativeCurrencyConversionRate;\n }\n }\n\n return contractExchangeRates;\n }\n}\n\nexport default TokenRatesController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenRatesController.js","sourceRoot":"","sources":["../src/TokenRatesController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AACA,iEAMoC;AAMpC,qEAAmE;AAInE,qDAAgF;AAoGhF,IAAK,SAGJ;AAHD,WAAK,SAAS;IACZ,8BAAiB,CAAA;IACjB,kCAAqB,CAAA;AACvB,CAAC,EAHI,SAAS,KAAT,SAAS,QAGb;AAoBD,MAAM,YAAY,GAAG;IACnB,QAAQ,EAAE,kCAAkC;IAC5C,gBAAgB,CAAC,SAAiB,EAAE,KAAa;QAC/C,OAAO,GAAG,IAAI,CAAC,QAAQ,uBAAuB,SAAS,IAAI,KAAK,EAAE,CAAC;IACrE,CAAC;IACD,eAAe;QACb,OAAO,GAAG,IAAI,CAAC,QAAQ,kBAAkB,CAAC;IAC5C,CAAC;IACD,wBAAwB;QACtB,OAAO,GAAG,IAAI,CAAC,QAAQ,iCAAiC,CAAC;IAC3D,CAAC;CACF,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,aAAa,CACpB,OAAY,EACZ,IAAgC;;IAEhC,IAAI,CAAC,IAAI,EAAE;QACT,OAAO,IAAI,CAAC;KACb;IACD,MAAM,KAAK,GACT,MAAA,IAAI,CAAC,IAAI,CACP,CAAC,EAAE,gBAAgB,EAAE,EAAE,EAAE,CACvB,gBAAgB,KAAK,IAAI,IAAI,IAAA,wBAAK,EAAC,gBAAgB,CAAC,KAAK,OAAO,CACnE,mCAAI,IAAI,CAAC;IACZ,OAAO,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,EAAE,KAAI,IAAI,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAa,oBAAqB,SAAQ,wCAGzC;IAwBC;;;;;;;;;;;;;;;OAeG;IACH,YACE,EACE,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,EACxB,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAC9B,oBAAoB,EACpB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,EACrB,eAAe,EAAE,sBAAsB,EACvC,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,GAiBrB,EACD,MAAkC,EAClC,KAAgC;QAEhC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;QApEf,cAAS,GAAY,EAAE,CAAC;QAExB,oBAAe,GAAyB;YAC9C,SAAS,EAAE,CAAC;YACZ,IAAI,EAAE,IAAI;SACX,CAAC;QAEM,0BAAqB,GAA+B;YAC1D,SAAS,EAAE,CAAC;YACZ,IAAI,EAAE,EAAE;SACT,CAAC;QAEF,0CAAa,SAAS,CAAC,QAAQ,EAAC;QAEhC;;WAEG;QACM,SAAI,GAAG,sBAAsB,CAAC;QAoDrC,IAAI,CAAC,aAAa,GAAG;YACnB,QAAQ;YACR,SAAS;YACT,QAAQ,EAAE,KAAK;YACf,cAAc,EAAE,aAAa;YAC7B,OAAO,EAAE,cAAc;YACvB,eAAe,EAAE,sBAAsB;YACvC,SAAS,EAAE,EAAE;YACb,iBAAiB,EAAE,EAAE;SACtB,CAAC;QAEF,IAAI,CAAC,YAAY,GAAG;YAClB,qBAAqB,EAAE,EAAE;YACzB,8BAA8B,EAAE,EAAE;SACnC,CAAC;QACF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;QAEjD,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,QAAQ,EAAE;YACpB,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;SAClD;QACD,uBAAA,IAAI,8EAAiB,MAArB,IAAI,CAAmB,CAAC;QAExB,wBAAwB,CAAC,CAAO,EAAE,eAAe,EAAE,EAAE,EAAE;YACrD,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,KAAK,eAAe,EAAE;gBACnD,IAAI,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;gBACpC,uBAAA,IAAI,8EAAiB,MAArB,IAAI,CAAmB,CAAC;gBACxB,IAAI,uBAAA,IAAI,uCAAW,KAAK,SAAS,CAAC,MAAM,EAAE;oBACxC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;iBAClC;aACF;QACH,CAAC,CAAA,CAAC,CAAC;QAEH,mBAAmB,CAAC,CAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,EAAE,EAAE;YAC7D,yDAAyD;YACzD,IACE,IAAI,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS;gBACnC,IAAI,CAAC,MAAM,CAAC,iBAAiB,KAAK,iBAAiB,EACnD;gBACA,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC;gBACjD,uBAAA,IAAI,8EAAiB,MAArB,IAAI,CAAmB,CAAC;gBACxB,IAAI,uBAAA,IAAI,uCAAW,KAAK,SAAS,CAAC,MAAM,EAAE;oBACxC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;iBAClC;aACF;QACH,CAAC,CAAA,CAAC,CAAC;QAEH,oBAAoB,CAAC,CAAO,EAAE,cAAc,EAAE,EAAE,EAAE;YAChD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC;YAC3C,IACE,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,OAAO;gBAC/B,IAAI,CAAC,MAAM,CAAC,cAAc,KAAK,MAAM,EACrC;gBACA,IAAI,CAAC,MAAM,CAAC,EAAE,qBAAqB,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC3C,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC;gBACpD,uBAAA,IAAI,8EAAiB,MAArB,IAAI,CAAmB,CAAC;gBACxB,IAAI,uBAAA,IAAI,uCAAW,KAAK,SAAS,CAAC,MAAM,EAAE;oBACxC,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;iBAClC;aACF;QACH,CAAC,CAAA,CAAC,CAAC;IACL,CAAC;IAYD;;OAEG;IACG,KAAK;;YACT,uBAAA,IAAI,uEAAU,MAAd,IAAI,CAAY,CAAC;YACjB,uBAAA,IAAI,mCAAc,SAAS,CAAC,MAAM,MAAA,CAAC;YACnC,MAAM,uBAAA,IAAI,mEAAM,MAAV,IAAI,CAAQ,CAAC;QACrB,CAAC;KAAA;IAED;;OAEG;IACH,IAAI;QACF,uBAAA,IAAI,uEAAU,MAAd,IAAI,CAAY,CAAC;QACjB,uBAAA,IAAI,mCAAc,SAAS,CAAC,QAAQ,MAAA,CAAC;IACvC,CAAC;IAwBD;;;;;;;OAOG;IACG,iBAAiB,CACrB,SAAiB,EACjB,UAAkB,EAClB,iBAA2B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;;YAEvE,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,sBAAsB,UAAU,kBAAkB,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC;YAC3F,OAAO,IAAA,8BAAW,EAAC,YAAY,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;QACtE,CAAC;KAAA;IAED;;;;;;OAMG;IACW,0BAA0B,CAAC,cAAsB;;YAC7D,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAClC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,qBAAqB,CAAC;YAEvD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAEvB,IAAI,GAAG,GAAG,SAAS,GAAG,SAAS,EAAE;gBAC/B,MAAM,UAAU,GAAG,MAAM,IAAA,8BAAW,EAClC,YAAY,CAAC,wBAAwB,EAAE,CACxC,CAAC;gBACF,IAAI,CAAC,qBAAqB,GAAG;oBAC3B,IAAI,EAAE,UAAU;oBAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAC;gBACF,OAAO,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC;aAC1D;YAED,OAAO,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC;QACrD,CAAC;KAAA;IAED;;;;;;OAMG;IACG,YAAY,CAChB,UAAe,IAAI,CAAC,MAAM,CAAC,OAAO;;YAElC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAClC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC;YAEjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAEvB,IAAI,GAAG,GAAG,SAAS,GAAG,SAAS,EAAE;gBAC/B,MAAM,SAAS,GAAG,MAAM,IAAA,8BAAW,EAAC,YAAY,CAAC,eAAe,EAAE,CAAC,CAAC;gBACpE,IAAI,CAAC,eAAe,GAAG;oBACrB,IAAI,EAAE,SAAS;oBACf,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAC;gBACF,OAAO,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;aAC1C;YAED,OAAO,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;KAAA;IAED;;OAEG;IACG,mBAAmB;;YACvB,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE;gBAChD,OAAO;aACR;YAED,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAChD,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACpE,MAAM,IAAI,CAAC,4BAA4B,CAAC;gBACtC,OAAO;gBACP,cAAc;gBACd,cAAc;aACf,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC;gBACV,qBAAqB,EACnB,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC;aACrE,CAAC,CAAC;QACL,CAAC;KAAA;IAED;;;;;;;OAOG;IACG,4BAA4B,CAAC,EACjC,OAAO,EACP,cAAc,EACd,cAAc,GAKf;;;YACC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE;gBAC1B,OAAO;aACR;YACD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YAEnD,IAAI,wBAAwB,GAA0B,EAAE,CAAC;YACzD,IAAI,CAAC,SAAS,EAAE;gBACd,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;oBACtC,MAAM,OAAO,GAAG,IAAA,uCAAoB,EAAC,YAAY,CAAC,CAAC;oBACnD,wBAAwB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;gBAChD,CAAC,CAAC,CAAC;aACJ;iBAAM;gBACL,wBAAwB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAC5D,cAAc,EACd,SAAS,EACT,cAAc,CACf,CAAC;aACH;YAED,MAAM,uCAAuC,GAC3C,MAAA,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,OAAO,CAAC,mCAAI,EAAE,CAAC;YAC3D,IAAI,CAAC,MAAM,CAAC;gBACV,8BAA8B,kCACzB,IAAI,CAAC,KAAK,CAAC,8BAA8B,KAC5C,CAAC,OAAO,CAAC,kCACJ,uCAAuC,KAC1C,CAAC,cAAc,CAAC,kCACX,CAAC,MAAA,uCAAuC,CAAC,cAAc,CAAC,mCAAI,EAAE,CAAC,GAC/D,wBAAwB,OAGhC;aACF,CAAC,CAAC;;KACJ;IAED;;;;;;;;;;;;OAYG;IACG,wBAAwB,CAC5B,cAAsB,EACtB,SAAiB,EACjB,iBAA2B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;;YAEvE,MAAM,qBAAqB,GAA0B,EAAE,CAAC;YAExD,oEAAoE;YACpE,MAAM,uBAAuB,GAAG,MAAM,IAAI,CAAC,0BAA0B,CACnE,cAAc,CACf,CAAC;YAEF,IAAI,uBAAuB,EAAE;gBAC3B,8DAA8D;gBAC9D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,iBAAiB,CACzC,SAAS,EACT,cAAc,EACd,cAAc,CACf,CAAC;gBACF,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,EAAE;oBACtC,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;oBACjD,qBAAqB,CAAC,IAAA,uCAAoB,EAAC,YAAY,CAAC,CAAC,GAAG,KAAK;wBAC/D,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC;wBACrC,CAAC,CAAC,CAAC,CAAC;gBACR,CAAC,CAAC,CAAC;aACJ;iBAAM;gBACL,mGAAmG;gBACnG,8FAA8F;gBAC9F,IAAI,kBAAkB,CAAC;gBACvB,IAAI,wCAAwC,GAAG,CAAC,CAAC;gBACjD,IAAI;oBACF;wBACE,kBAAkB;wBAClB,EAAE,cAAc,EAAE,wCAAwC,EAAE;qBAC7D,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;wBACpB,IAAI,CAAC,iBAAiB,CACpB,SAAS,EACT,wCAAqB,EACrB,cAAc,CACf;wBACD,IAAA,kCAAuB,EAAC,cAAc,EAAE,wCAAqB,EAAE,KAAK,CAAC;qBACtE,CAAC,CAAC;iBACJ;gBAAC,OAAO,KAAK,EAAE;oBACd,IACE,KAAK,YAAY,KAAK;wBACtB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,0CAA0C,CAAC,EAClE;wBACA,OAAO,EAAE,CAAC;qBACX;oBACD,MAAM,KAAK,CAAC;iBACb;gBAED,KAAK,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CACrD,kBAAkB,CACnB,EAAE;oBACD,MAAM,+BAA+B,GACnC,UAAU,CAAC,wCAAqB,CAAC,WAAW,EAAE,CAAC,CAAC;oBAClD,qBAAqB,CAAC,IAAA,uCAAoB,EAAC,YAAY,CAAC,CAAC;wBACvD,+BAA+B;4BAC/B,wCAAwC,CAAC;iBAC5C;aACF;YAED,OAAO,qBAAqB,CAAC;QAC/B,CAAC;KAAA;IAED;;;;;;;OAOG;IACG,YAAY,CAChB,eAAgC,EAChC,OAAqC;;YAErC,MAAM,aAAa,GAAG,IAAI,CAAC,oBAAoB,CAAC,eAAe,CAAC,CAAC;YACjE,MAAM,IAAI,CAAC,4BAA4B,CAAC;gBACtC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,OAAO;gBAC5C,cAAc,EAAE,aAAa,CAAC,aAAa,CAAC,MAAM;gBAClD,cAAc,EAAE,OAAO,CAAC,cAAc;aACvC,CAAC,CAAC;QACL,CAAC;KAAA;CACF;AAhbD,oDAgbC;;;IApSG,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;IACrD,MAAM,MAAM,GACV,CAAA,MAAA,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,0CAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,KAAI,EAAE,CAAC;IACtE,MAAM,cAAc,GAClB,CAAA,MAAA,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,0CAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QACrE,EAAE,CAAC;IACL,IAAI,CAAC,SAAS,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,CAAC,CAAC;AAClD,CAAC;IAuBC,IAAI,IAAI,CAAC,MAAM,EAAE;QACf,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;KAC3B;AACH,CAAC;;QAMC,MAAM,IAAA,gCAAa,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAEtD,qEAAqE;QACrE,qEAAqE;QACrE,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,uBAAA,IAAI,mEAAM,MAAV,IAAI,CAAQ,CAAC;QACf,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;;AAwPH,kBAAe,oBAAoB,CAAC","sourcesContent":["import type { BaseConfig, BaseState } from '@metamask/base-controller';\nimport {\n safelyExecute,\n handleFetch,\n toChecksumHexAddress,\n FALL_BACK_VS_CURRENCY,\n toHex,\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 { fetchExchangeRate as fetchNativeExchangeRate } from './crypto-compare';\nimport type { TokensState } from './TokensController';\n\n/**\n * @type CoinGeckoResponse\n *\n * CoinGecko API response representation\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface CoinGeckoResponse {\n [address: string]: {\n [currency: string]: number;\n };\n}\n/**\n * @type CoinGeckoPlatform\n *\n * CoinGecko supported platform API representation\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface CoinGeckoPlatform {\n id: string;\n chain_identifier: null | number;\n name: string;\n shortname: string;\n}\n\n/**\n * @type Token\n *\n * Token representation\n * @property address - Hex address of the token contract\n * @property decimals - Number of decimals the token uses\n * @property symbol - Symbol of the token\n * @property image - Image of the token, url or bit32 image\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface Token {\n address: string;\n decimals: number;\n symbol: string;\n aggregators?: string[];\n image?: string;\n balanceError?: unknown;\n isERC721?: boolean;\n name?: string;\n}\n\n/**\n * @type TokenRatesConfig\n *\n * Token rates controller configuration\n * @property interval - Polling interval used to fetch new token rates\n * @property nativeCurrency - Current native currency selected to use base of rates\n * @property chainId - Current network chainId\n * @property tokens - List of tokens to track exchange rates for\n * @property threshold - Threshold to invalidate the supportedChains\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface TokenRatesConfig extends BaseConfig {\n interval: number;\n nativeCurrency: string;\n chainId: Hex;\n selectedAddress: string;\n allTokens: { [chainId: Hex]: { [key: string]: Token[] } };\n allDetectedTokens: { [chainId: Hex]: { [key: string]: Token[] } };\n threshold: number;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface ContractExchangeRates {\n [address: string]: number | undefined;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface SupportedChainsCache {\n timestamp: number;\n data: CoinGeckoPlatform[] | null;\n}\n\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface SupportedVsCurrenciesCache {\n timestamp: number;\n data: string[];\n}\n\nenum PollState {\n Active = 'Active',\n Inactive = 'Inactive',\n}\n\n/**\n * @type TokenRatesState\n *\n * Token rates controller state\n * @property contractExchangeRates - Hash of token contract addresses to exchange rates (single globally selected chain, will be deprecated soon)\n * @property contractExchangeRatesByChainId - Hash of token contract addresses to exchange rates keyed by chain ID and native currency (ticker)\n */\n// This interface was created before this ESLint rule was added.\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\nexport interface TokenRatesState extends BaseState {\n contractExchangeRates: ContractExchangeRates;\n contractExchangeRatesByChainId: Record<\n Hex,\n Record<string, ContractExchangeRates>\n >;\n}\n\nconst CoinGeckoApi = {\n BASE_URL: 'https://api.coingecko.com/api/v3',\n getTokenPriceURL(chainSlug: string, query: string) {\n return `${this.BASE_URL}/simple/token_price/${chainSlug}?${query}`;\n },\n getPlatformsURL() {\n return `${this.BASE_URL}/asset_platforms`;\n },\n getSupportedVsCurrencies() {\n return `${this.BASE_URL}/simple/supported_vs_currencies`;\n },\n};\n\n/**\n * Finds the chain slug in the data array given a chainId.\n *\n * @param chainId - The current chain ID.\n * @param data - A list platforms supported by the CoinGecko API.\n * @returns The CoinGecko slug for the given chain ID, or `null` if the slug was not found.\n */\nfunction findChainSlug(\n chainId: Hex,\n data: CoinGeckoPlatform[] | null,\n): string | null {\n if (!data) {\n return null;\n }\n const chain =\n data.find(\n ({ chain_identifier }) =>\n chain_identifier !== null && toHex(chain_identifier) === chainId,\n ) ?? null;\n return chain?.id || null;\n}\n\n/**\n * Controller that passively polls on a set interval for token-to-fiat exchange rates\n * for tokens stored in the TokensController\n */\nexport class TokenRatesController extends PollingControllerV1<\n TokenRatesConfig,\n TokenRatesState\n> {\n private handle?: ReturnType<typeof setTimeout>;\n\n private tokenList: Token[] = [];\n\n private supportedChains: SupportedChainsCache = {\n timestamp: 0,\n data: null,\n };\n\n private supportedVsCurrencies: SupportedVsCurrenciesCache = {\n timestamp: 0,\n data: [],\n };\n\n #pollState = PollState.Inactive;\n\n /**\n * Name of this controller used during composition\n */\n override name = 'TokenRatesController';\n\n private readonly getNetworkClientById: NetworkController['getNetworkClientById'];\n\n /**\n * Creates a TokenRatesController instance.\n *\n * @param options - The controller options.\n * @param options.interval - The polling interval in ms\n * @param options.threshold - The duration in ms before metadata fetched from CoinGecko is considered stale\n * @param options.getNetworkClientById - Gets the network client with the given id from the NetworkController.\n * @param options.chainId - The chain ID of the current network.\n * @param options.ticker - The ticker for the current network.\n * @param options.selectedAddress - The current selected address.\n * @param options.onPreferencesStateChange - Allows subscribing to preference controller state changes.\n * @param options.onTokensStateChange - Allows subscribing to token controller state changes.\n * @param options.onNetworkStateChange - Allows subscribing to network state changes.\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 interval = 3 * 60 * 1000,\n threshold = 6 * 60 * 60 * 1000,\n getNetworkClientById,\n chainId: initialChainId,\n ticker: initialTicker,\n selectedAddress: initialSelectedAddress,\n onPreferencesStateChange,\n onTokensStateChange,\n onNetworkStateChange,\n }: {\n interval?: number;\n threshold?: number;\n getNetworkClientById: NetworkController['getNetworkClientById'];\n chainId: Hex;\n ticker: string;\n selectedAddress: string;\n onPreferencesStateChange: (\n listener: (preferencesState: PreferencesState) => void,\n ) => void;\n onTokensStateChange: (\n listener: (tokensState: TokensState) => void,\n ) => void;\n onNetworkStateChange: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n },\n config?: Partial<TokenRatesConfig>,\n state?: Partial<TokenRatesState>,\n ) {\n super(config, state);\n this.defaultConfig = {\n interval,\n threshold,\n disabled: false,\n nativeCurrency: initialTicker,\n chainId: initialChainId,\n selectedAddress: initialSelectedAddress,\n allTokens: {}, // TODO: initialize these correctly, maybe as part of BaseControllerV2 migration\n allDetectedTokens: {},\n };\n\n this.defaultState = {\n contractExchangeRates: {},\n contractExchangeRatesByChainId: {},\n };\n this.initialize();\n this.setIntervalLength(interval);\n this.getNetworkClientById = getNetworkClientById;\n\n if (config?.disabled) {\n this.configure({ disabled: true }, false, false);\n }\n this.#updateTokenList();\n\n onPreferencesStateChange(async ({ selectedAddress }) => {\n if (this.config.selectedAddress !== selectedAddress) {\n this.configure({ selectedAddress });\n this.#updateTokenList();\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n });\n\n onTokensStateChange(async ({ allTokens, allDetectedTokens }) => {\n // These two state properties are assumed to be immutable\n if (\n this.config.allTokens !== allTokens ||\n this.config.allDetectedTokens !== allDetectedTokens\n ) {\n this.configure({ allTokens, allDetectedTokens });\n this.#updateTokenList();\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n });\n\n onNetworkStateChange(async ({ providerConfig }) => {\n const { chainId, ticker } = providerConfig;\n if (\n this.config.chainId !== chainId ||\n this.config.nativeCurrency !== ticker\n ) {\n this.update({ contractExchangeRates: {} });\n this.configure({ chainId, nativeCurrency: ticker });\n this.#updateTokenList();\n if (this.#pollState === PollState.Active) {\n await this.updateExchangeRates();\n }\n }\n });\n }\n\n #updateTokenList() {\n const { allTokens, allDetectedTokens } = this.config;\n const tokens =\n allTokens[this.config.chainId]?.[this.config.selectedAddress] || [];\n const detectedTokens =\n allDetectedTokens[this.config.chainId]?.[this.config.selectedAddress] ||\n [];\n this.tokenList = [...tokens, ...detectedTokens];\n }\n\n /**\n * Start (or restart) polling.\n */\n async start() {\n this.#stopPoll();\n this.#pollState = PollState.Active;\n await this.#poll();\n }\n\n /**\n * Stop polling.\n */\n stop() {\n this.#stopPoll();\n this.#pollState = PollState.Inactive;\n }\n\n /**\n * Clear the active polling timer, if present.\n */\n #stopPoll() {\n if (this.handle) {\n clearTimeout(this.handle);\n }\n }\n\n /**\n * Poll for exchange rate updates.\n */\n async #poll() {\n await safelyExecute(() => this.updateExchangeRates());\n\n // Poll using recursive `setTimeout` instead of `setInterval` so that\n // requests don't stack if they take longer than the polling interval\n this.handle = setTimeout(() => {\n this.#poll();\n }, this.config.interval);\n }\n\n /**\n * Fetches a pairs of token address and native currency.\n *\n * @param chainSlug - The chain string identifier.\n * @param vsCurrency - The currency used to generate pairs against the tokens.\n * @param tokenAddresses - The addresses for the token contracts.\n * @returns The exchange rates for the given pairs.\n */\n async fetchExchangeRate(\n chainSlug: string,\n vsCurrency: string,\n tokenAddresses: string[] = this.tokenList.map((token) => token.address),\n ): Promise<CoinGeckoResponse> {\n const tokenPairs = tokenAddresses.join(',');\n const query = `contract_addresses=${tokenPairs}&vs_currencies=${vsCurrency.toLowerCase()}`;\n return handleFetch(CoinGeckoApi.getTokenPriceURL(chainSlug, query));\n }\n\n /**\n * Checks if the current native currency is a supported vs currency to use\n * to query for token exchange rates.\n *\n * @param nativeCurrency - The native currency to check.\n * @returns A boolean indicating whether it's a supported vsCurrency.\n */\n private async checkIsSupportedVsCurrency(nativeCurrency: string) {\n const { threshold } = this.config;\n const { timestamp, data } = this.supportedVsCurrencies;\n\n const now = Date.now();\n\n if (now - timestamp > threshold) {\n const currencies = await handleFetch(\n CoinGeckoApi.getSupportedVsCurrencies(),\n );\n this.supportedVsCurrencies = {\n data: currencies,\n timestamp: Date.now(),\n };\n return currencies.includes(nativeCurrency.toLowerCase());\n }\n\n return data.includes(nativeCurrency.toLowerCase());\n }\n\n /**\n * Gets the slug from cached supported platforms CoinGecko API response for the chain ID.\n * If cached supported platforms response is stale, fetches and updates it.\n *\n * @param chainId - The chain ID.\n * @returns The CoinGecko slug for the chain ID.\n */\n async getChainSlug(\n chainId: Hex = this.config.chainId,\n ): Promise<string | null> {\n const { threshold } = this.config;\n const { data, timestamp } = this.supportedChains;\n\n const now = Date.now();\n\n if (now - timestamp > threshold) {\n const platforms = await handleFetch(CoinGeckoApi.getPlatformsURL());\n this.supportedChains = {\n data: platforms,\n timestamp: Date.now(),\n };\n return findChainSlug(chainId, platforms);\n }\n\n return findChainSlug(chainId, data);\n }\n\n /**\n * Updates exchange rates for all tokens.\n */\n async updateExchangeRates() {\n if (this.tokenList.length === 0 || this.disabled) {\n return;\n }\n\n const { chainId, nativeCurrency } = this.config;\n const tokenAddresses = this.tokenList.map((token) => token.address);\n await this.updateExchangeRatesByChainId({\n chainId,\n nativeCurrency,\n tokenAddresses,\n });\n\n this.update({\n contractExchangeRates:\n this.state.contractExchangeRatesByChainId[chainId][nativeCurrency],\n });\n }\n\n /**\n * Updates exchange rates for all tokens.\n *\n * @param options - The options to fetch exchange rates.\n * @param options.chainId - The chain ID.\n * @param options.nativeCurrency - The ticker for the chain.\n * @param options.tokenAddresses - The addresses for the token contracts.\n */\n async updateExchangeRatesByChainId({\n chainId,\n nativeCurrency,\n tokenAddresses,\n }: {\n chainId: Hex;\n nativeCurrency: string;\n tokenAddresses: string[];\n }) {\n if (!tokenAddresses.length) {\n return;\n }\n const chainSlug = await this.getChainSlug(chainId);\n\n let newContractExchangeRates: ContractExchangeRates = {};\n if (!chainSlug) {\n tokenAddresses.forEach((tokenAddress) => {\n const address = toChecksumHexAddress(tokenAddress);\n newContractExchangeRates[address] = undefined;\n });\n } else {\n newContractExchangeRates = await this.fetchAndMapExchangeRates(\n nativeCurrency,\n chainSlug,\n tokenAddresses,\n );\n }\n\n const existingContractExchangeRatesForChainId =\n this.state.contractExchangeRatesByChainId[chainId] ?? {};\n this.update({\n contractExchangeRatesByChainId: {\n ...this.state.contractExchangeRatesByChainId,\n [chainId]: {\n ...existingContractExchangeRatesForChainId,\n [nativeCurrency]: {\n ...(existingContractExchangeRatesForChainId[nativeCurrency] ?? {}),\n ...newContractExchangeRates,\n },\n },\n },\n });\n }\n\n /**\n * Checks if the active network's native currency is supported by the coingecko API.\n * If supported, it fetches and maps contractExchange rates to a format to be consumed by the UI.\n * If not supported, it fetches contractExchange rates and maps them from token/fallback-currency\n * to token/nativeCurrency.\n *\n * @param nativeCurrency - The native currency ticker against which to fetch exchange rates\n * @param chainSlug - The unique slug used to id the chain by the coingecko api\n * should be used to query token exchange rates.\n * @param tokenAddresses - The addresses for the token contracts against which to fetch exchange rates.\n * @returns An object with conversion rates for each token\n * related to the network's native currency.\n */\n async fetchAndMapExchangeRates(\n nativeCurrency: string,\n chainSlug: string,\n tokenAddresses: string[] = this.tokenList.map((token) => token.address),\n ): Promise<ContractExchangeRates> {\n const contractExchangeRates: ContractExchangeRates = {};\n\n // check if native currency is supported as a vs_currency by the API\n const nativeCurrencySupported = await this.checkIsSupportedVsCurrency(\n nativeCurrency,\n );\n\n if (nativeCurrencySupported) {\n // If it is we can do a simple fetch against the CoinGecko API\n const prices = await this.fetchExchangeRate(\n chainSlug,\n nativeCurrency,\n tokenAddresses,\n );\n tokenAddresses.forEach((tokenAddress) => {\n const price = prices[tokenAddress.toLowerCase()];\n contractExchangeRates[toChecksumHexAddress(tokenAddress)] = price\n ? price[nativeCurrency.toLowerCase()]\n : 0;\n });\n } else {\n // if native currency is not supported we need to use a fallback vsCurrency, get the exchange rates\n // in token/fallback-currency format and convert them to expected token/nativeCurrency format.\n let tokenExchangeRates;\n let vsCurrencyToNativeCurrencyConversionRate = 0;\n try {\n [\n tokenExchangeRates,\n { conversionRate: vsCurrencyToNativeCurrencyConversionRate },\n ] = await Promise.all([\n this.fetchExchangeRate(\n chainSlug,\n FALL_BACK_VS_CURRENCY,\n tokenAddresses,\n ),\n fetchNativeExchangeRate(nativeCurrency, FALL_BACK_VS_CURRENCY, false),\n ]);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('market does not exist for this coin pair')\n ) {\n return {};\n }\n throw error;\n }\n\n for (const [tokenAddress, conversion] of Object.entries(\n tokenExchangeRates,\n )) {\n const tokenToVsCurrencyConversionRate =\n conversion[FALL_BACK_VS_CURRENCY.toLowerCase()];\n contractExchangeRates[toChecksumHexAddress(tokenAddress)] =\n tokenToVsCurrencyConversionRate *\n vsCurrencyToNativeCurrencyConversionRate;\n }\n }\n\n return contractExchangeRates;\n }\n\n /**\n * Updates token rates for the given networkClientId and contract addresses\n *\n * @param networkClientId - The network client ID used to get a ticker value.\n * @param options - The polling options.\n * @param options.tokenAddresses - The addresses for the token contracts.\n * @returns The controller state.\n */\n async _executePoll(\n networkClientId: NetworkClientId,\n options: { tokenAddresses: string[] },\n ): Promise<void> {\n const networkClient = this.getNetworkClientById(networkClientId);\n await this.updateExchangeRatesByChainId({\n chainId: networkClient.configuration.chainId,\n nativeCurrency: networkClient.configuration.ticker,\n tokenAddresses: options.tokenAddresses,\n });\n }\n}\n\nexport default TokenRatesController;\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Contract } from '@ethersproject/contracts';
|
|
3
3
|
import type { AddApprovalRequest } from '@metamask/approval-controller';
|
|
4
4
|
import type { BaseConfig, BaseState, RestrictedControllerMessenger } from '@metamask/base-controller';
|
|
5
|
-
import {
|
|
5
|
+
import { BaseControllerV1 } from '@metamask/base-controller';
|
|
6
6
|
import type { NetworkClientId, NetworkController, NetworkState } from '@metamask/network-controller';
|
|
7
7
|
import type { PreferencesState } from '@metamask/preferences-controller';
|
|
8
8
|
import type { Hex } from '@metamask/utils';
|
|
@@ -84,7 +84,7 @@ export declare type TokensControllerMessenger = RestrictedControllerMessenger<ty
|
|
|
84
84
|
/**
|
|
85
85
|
* Controller that stores assets and exposes convenience methods
|
|
86
86
|
*/
|
|
87
|
-
export declare class TokensController extends
|
|
87
|
+
export declare class TokensController extends BaseControllerV1<TokensConfig, TokensState> {
|
|
88
88
|
private readonly mutex;
|
|
89
89
|
private abortController;
|
|
90
90
|
private readonly messagingSystem;
|