@metamask-previews/assets-controllers 108.3.0-preview-be0eea21e → 108.3.0-preview-1e1b476aa
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 +0 -6
- package/dist/TokenListController.cjs +4 -84
- package/dist/TokenListController.cjs.map +1 -1
- package/dist/TokenListController.d.cts +1 -6
- package/dist/TokenListController.d.cts.map +1 -1
- package/dist/TokenListController.d.mts +1 -6
- package/dist/TokenListController.d.mts.map +1 -1
- package/dist/TokenListController.mjs +4 -84
- package/dist/TokenListController.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,12 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Added
|
|
11
|
-
|
|
12
|
-
- Add `isDisabled` option to `TokenListController` constructor, a function that returns whether the controller should be disabled ([#8945](https://github.com/MetaMask/core/pull/8945))
|
|
13
|
-
- When `isDisabled()` returns `true`, every fetching entry point (`initialize()`, `start()`, `restart()`, `_executePoll()`, and `fetchTokenList()`) resets `tokensChainsCache` to `{}`, cancels any pending debounced persist (so a stale entry can't write old data after the in-memory reset), and overwrites every persisted `tokensChainsCache:*` entry in `StorageService` with `{ data: {}, timestamp: 0 }`. No HTTP calls are issued to the token API.
|
|
14
|
-
- The function is re-evaluated on each entry point so it can be toggled at runtime without reconstructing the controller — including from inside any deprecated `setInterval` already running from a prior `start()`.
|
|
15
|
-
|
|
16
10
|
## [108.3.0]
|
|
17
11
|
|
|
18
12
|
### Added
|
|
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
10
10
|
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");
|
|
11
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
12
|
};
|
|
13
|
-
var _TokenListController_instances, _a, _TokenListController_persistDebounceTimer, _TokenListController_initializePromise, _TokenListController_persistInFlightPromise, _TokenListController_changedChainsToPersist, _TokenListController_previousTokensChainsCache, _TokenListController_persistDebounceMs, _TokenListController_storageKeyPrefix, _TokenListController_getChainStorageKey, _TokenListController_intervalId, _TokenListController_intervalDelay, _TokenListController_cacheRefreshThreshold, _TokenListController_chainId, _TokenListController_abortController,
|
|
13
|
+
var _TokenListController_instances, _a, _TokenListController_persistDebounceTimer, _TokenListController_initializePromise, _TokenListController_persistInFlightPromise, _TokenListController_changedChainsToPersist, _TokenListController_previousTokensChainsCache, _TokenListController_persistDebounceMs, _TokenListController_storageKeyPrefix, _TokenListController_getChainStorageKey, _TokenListController_intervalId, _TokenListController_intervalDelay, _TokenListController_cacheRefreshThreshold, _TokenListController_chainId, _TokenListController_abortController, _TokenListController_waitForInitialization, _TokenListController_onCacheChanged, _TokenListController_debouncePersist, _TokenListController_persistChangedChains, _TokenListController_synchronizeCacheWithStorage, _TokenListController_saveChainCacheToStorage, _TokenListController_onNetworkControllerStateChange, _TokenListController_stopPolling, _TokenListController_startDeprecatedPolling;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.TokenListController = exports.getDefaultTokenListState = void 0;
|
|
16
16
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
@@ -47,14 +47,10 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
47
47
|
* @param options.onNetworkStateChange - A function for registering an event handler for network state changes.
|
|
48
48
|
* @param options.interval - The polling interval, in milliseconds.
|
|
49
49
|
* @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.
|
|
50
|
-
* @param options.isDisabled - Optional function returning whether the controller should be disabled.
|
|
51
|
-
* When it returns `true`, `tokensChainsCache` is reset to `{}` at construction and at every
|
|
52
|
-
* polling entry point, no fetches are issued, and persisted storage is cleared on initialize.
|
|
53
|
-
* The function is evaluated dynamically on each entry point so it can be toggled at runtime.
|
|
54
50
|
* @param options.messenger - A restricted messenger.
|
|
55
51
|
* @param options.state - Initial state to set on this controller.
|
|
56
52
|
*/
|
|
57
|
-
constructor({ chainId, onNetworkStateChange, interval = DEFAULT_INTERVAL, cacheRefreshThreshold = DEFAULT_THRESHOLD,
|
|
53
|
+
constructor({ chainId, onNetworkStateChange, interval = DEFAULT_INTERVAL, cacheRefreshThreshold = DEFAULT_THRESHOLD, messenger, state, }) {
|
|
58
54
|
super({
|
|
59
55
|
name,
|
|
60
56
|
metadata,
|
|
@@ -89,19 +85,11 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
89
85
|
_TokenListController_cacheRefreshThreshold.set(this, void 0);
|
|
90
86
|
_TokenListController_chainId.set(this, void 0);
|
|
91
87
|
_TokenListController_abortController.set(this, void 0);
|
|
92
|
-
_TokenListController_isDisabled.set(this, void 0);
|
|
93
88
|
__classPrivateFieldSet(this, _TokenListController_intervalDelay, interval, "f");
|
|
94
89
|
this.setIntervalLength(interval);
|
|
95
90
|
__classPrivateFieldSet(this, _TokenListController_cacheRefreshThreshold, cacheRefreshThreshold, "f");
|
|
96
91
|
__classPrivateFieldSet(this, _TokenListController_chainId, chainId, "f");
|
|
97
92
|
__classPrivateFieldSet(this, _TokenListController_abortController, new AbortController(), "f");
|
|
98
|
-
__classPrivateFieldSet(this, _TokenListController_isDisabled, isDisabled, "f");
|
|
99
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
100
|
-
// Fire-and-forget: storage clearing is async but the constructor is sync.
|
|
101
|
-
// Errors are caught inside `#enforceDisabledState`.
|
|
102
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
103
|
-
__classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
104
|
-
}
|
|
105
93
|
if (onNetworkStateChange) {
|
|
106
94
|
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
107
95
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
@@ -131,10 +119,6 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
131
119
|
}
|
|
132
120
|
const executeInit = async () => {
|
|
133
121
|
try {
|
|
134
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
135
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
122
|
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_synchronizeCacheWithStorage).call(this);
|
|
139
123
|
// Subscribe to state changes to automatically persist tokensChainsCache
|
|
140
124
|
this.messenger.subscribe('TokenListController:stateChange', (newCache) => __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_onCacheChanged).call(this, newCache), (controllerState) => controllerState.tokensChainsCache);
|
|
@@ -158,10 +142,6 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
158
142
|
* Consider using the new polling approach instead
|
|
159
143
|
*/
|
|
160
144
|
async start() {
|
|
161
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
162
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
145
|
if (!(0, assetsUtil_1.isTokenListSupportedForNetwork)(__classPrivateFieldGet(this, _TokenListController_chainId, "f"))) {
|
|
166
146
|
return;
|
|
167
147
|
}
|
|
@@ -175,10 +155,6 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
175
155
|
*/
|
|
176
156
|
async restart() {
|
|
177
157
|
__classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_stopPolling).call(this);
|
|
178
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
179
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
158
|
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_startDeprecatedPolling).call(this);
|
|
183
159
|
}
|
|
184
160
|
/**
|
|
@@ -214,12 +190,8 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
214
190
|
* @returns A promise that resolves when this operation completes.
|
|
215
191
|
*/
|
|
216
192
|
async _executePoll({ chainId }) {
|
|
217
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
218
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
193
|
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_waitForInitialization).call(this);
|
|
222
|
-
|
|
194
|
+
return this.fetchTokenList(chainId);
|
|
223
195
|
}
|
|
224
196
|
/**
|
|
225
197
|
* Fetching token list from the Token Service API. This will fetch tokens across chains.
|
|
@@ -228,10 +200,6 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
228
200
|
* @param chainId - The chainId of the current chain triggering the fetch.
|
|
229
201
|
*/
|
|
230
202
|
async fetchTokenList(chainId) {
|
|
231
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
232
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
203
|
if (this.isCacheValid(chainId)) {
|
|
236
204
|
return;
|
|
237
205
|
}
|
|
@@ -285,7 +253,7 @@ class TokenListController extends (0, polling_controller_1.StaticIntervalPolling
|
|
|
285
253
|
}
|
|
286
254
|
}
|
|
287
255
|
exports.TokenListController = TokenListController;
|
|
288
|
-
_a = TokenListController, _TokenListController_persistDebounceTimer = new WeakMap(), _TokenListController_initializePromise = new WeakMap(), _TokenListController_persistInFlightPromise = new WeakMap(), _TokenListController_changedChainsToPersist = new WeakMap(), _TokenListController_previousTokensChainsCache = new WeakMap(), _TokenListController_intervalId = new WeakMap(), _TokenListController_intervalDelay = new WeakMap(), _TokenListController_cacheRefreshThreshold = new WeakMap(), _TokenListController_chainId = new WeakMap(), _TokenListController_abortController = new WeakMap(),
|
|
256
|
+
_a = TokenListController, _TokenListController_persistDebounceTimer = new WeakMap(), _TokenListController_initializePromise = new WeakMap(), _TokenListController_persistInFlightPromise = new WeakMap(), _TokenListController_changedChainsToPersist = new WeakMap(), _TokenListController_previousTokensChainsCache = new WeakMap(), _TokenListController_intervalId = new WeakMap(), _TokenListController_intervalDelay = new WeakMap(), _TokenListController_cacheRefreshThreshold = new WeakMap(), _TokenListController_chainId = new WeakMap(), _TokenListController_abortController = new WeakMap(), _TokenListController_instances = new WeakSet(), _TokenListController_getChainStorageKey = function _TokenListController_getChainStorageKey(chainId) {
|
|
289
257
|
return `${__classPrivateFieldGet(_a, _a, "f", _TokenListController_storageKeyPrefix)}:${chainId}`;
|
|
290
258
|
}, _TokenListController_waitForInitialization =
|
|
291
259
|
/**
|
|
@@ -478,54 +446,6 @@ async function _TokenListController_startDeprecatedPolling() {
|
|
|
478
446
|
__classPrivateFieldSet(this, _TokenListController_intervalId, setInterval(async () => {
|
|
479
447
|
await (0, controller_utils_1.safelyExecute)(() => this.fetchTokenList(__classPrivateFieldGet(this, _TokenListController_chainId, "f")));
|
|
480
448
|
}, __classPrivateFieldGet(this, _TokenListController_intervalDelay, "f")), "f");
|
|
481
|
-
}, _TokenListController_enforceDisabledState =
|
|
482
|
-
/**
|
|
483
|
-
* Fully enforce the disabled state in a single step:
|
|
484
|
-
* 1. Drop in-memory `tokensChainsCache` to `{}`.
|
|
485
|
-
* 2. Cancel any pending debounced persist and clear the changed-chains set so
|
|
486
|
-
* a stale entry can't write old data after the in-memory reset.
|
|
487
|
-
* 3. Overwrite every persisted `tokensChainsCache:*` entry in StorageService
|
|
488
|
-
* with `{ data: {}, timestamp: 0 }`.
|
|
489
|
-
*
|
|
490
|
-
* Called from every fetching entry point when `isDisabled()` is true so that
|
|
491
|
-
* a runtime toggle propagates to both memory and storage immediately, even
|
|
492
|
-
* if `initialize()` was originally invoked while the controller was enabled.
|
|
493
|
-
*
|
|
494
|
-
* @returns A promise that resolves when persisted entries have been cleared.
|
|
495
|
-
*/
|
|
496
|
-
async function _TokenListController_enforceDisabledState() {
|
|
497
|
-
__classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_resetCacheState).call(this);
|
|
498
|
-
if (__classPrivateFieldGet(this, _TokenListController_persistDebounceTimer, "f")) {
|
|
499
|
-
clearTimeout(__classPrivateFieldGet(this, _TokenListController_persistDebounceTimer, "f"));
|
|
500
|
-
__classPrivateFieldSet(this, _TokenListController_persistDebounceTimer, undefined, "f");
|
|
501
|
-
}
|
|
502
|
-
__classPrivateFieldGet(this, _TokenListController_changedChainsToPersist, "f").clear();
|
|
503
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_clearPersistedCache).call(this);
|
|
504
|
-
}, _TokenListController_resetCacheState = function _TokenListController_resetCacheState() {
|
|
505
|
-
if (Object.keys(this.state.tokensChainsCache).length === 0) {
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
this.update((state) => {
|
|
509
|
-
state.tokensChainsCache = {};
|
|
510
|
-
});
|
|
511
|
-
}, _TokenListController_clearPersistedCache =
|
|
512
|
-
/**
|
|
513
|
-
* Overwrite every persisted `tokensChainsCache:*` entry in StorageService
|
|
514
|
-
* with `{ data: {}, timestamp: 0 }` so the on-disk view reflects the
|
|
515
|
-
* disabled state.
|
|
516
|
-
*
|
|
517
|
-
* @returns A promise that resolves when persisted entries have been cleared.
|
|
518
|
-
*/
|
|
519
|
-
async function _TokenListController_clearPersistedCache() {
|
|
520
|
-
try {
|
|
521
|
-
const allKeys = await this.messenger.call('StorageService:getAllKeys', name);
|
|
522
|
-
const cacheKeys = allKeys.filter((key) => key.startsWith(`${__classPrivateFieldGet(_a, _a, "f", _TokenListController_storageKeyPrefix)}:`));
|
|
523
|
-
const emptyDataCache = { data: {}, timestamp: 0 };
|
|
524
|
-
await Promise.all(cacheKeys.map((key) => this.messenger.call('StorageService:setItem', name, key, emptyDataCache)));
|
|
525
|
-
}
|
|
526
|
-
catch (error) {
|
|
527
|
-
console.error('TokenListController: Failed to clear persisted cache:', error);
|
|
528
|
-
}
|
|
529
449
|
};
|
|
530
450
|
/**
|
|
531
451
|
* Debounce delay for persisting state changes (in milliseconds).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenListController.cjs","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerStateChangeEvent,\n NetworkState,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n StorageServiceSetItemAction,\n StorageServiceGetItemAction,\n StorageServiceGetAllKeysAction,\n} from '@metamask/storage-service';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n isTokenListSupportedForNetwork,\n formatAggregatorNames,\n formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { TokenRwaData, fetchTokenListByChainId } from './token-service';\n\n// 4 Hour Interval Cache Refresh Threshold\nconst DEFAULT_INTERVAL = 4 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 4 * 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 rwaData?: TokenRwaData;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\nexport type DataCache = {\n timestamp: number;\n data: TokenListMap;\n};\nexport type TokensChainsCache = {\n [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n tokensChainsCache: TokensChainsCache;\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 =\n | NetworkControllerGetNetworkClientByIdAction\n | StorageServiceSetItemAction\n | StorageServiceGetItemAction\n | StorageServiceGetAllKeysAction;\n\ntype AllowedEvents = NetworkControllerStateChangeEvent;\n\nexport type TokenListControllerMessenger = Messenger<\n typeof name,\n TokenListControllerActions | AllowedActions,\n TokenListControllerEvents | AllowedEvents\n>;\n\nconst metadata: StateMetadata<TokenListState> = {\n tokensChainsCache: {\n includeInStateLogs: false,\n persist: false, // Persisted separately via StorageService\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nexport const getDefaultTokenListState = (): TokenListState => {\n return {\n tokensChainsCache: {},\n };\n};\n\n/** The input to start polling for the {@link TokenListController} */\ntype TokenListPollingInput = {\n chainId: Hex;\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 StaticIntervalPollingController<TokenListPollingInput>()<\n typeof name,\n TokenListState,\n TokenListControllerMessenger\n> {\n /**\n * Debounce timer for persisting state changes to storage.\n */\n #persistDebounceTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Promise for the in-flight initialization sequence.\n */\n #initializePromise?: Promise<void>;\n\n /**\n * Promise that resolves when the current persist operation completes.\n * Used to prevent race conditions between persist operations.\n */\n #persistInFlightPromise?: Promise<void>;\n\n /**\n * Tracks which chains have pending changes to persist.\n * Only changed chains are persisted to reduce write amplification.\n */\n readonly #changedChainsToPersist: Set<Hex> = new Set();\n\n /**\n * Previous tokensChainsCache for detecting which chains changed.\n */\n #previousTokensChainsCache: TokensChainsCache = {};\n\n /**\n * Debounce delay for persisting state changes (in milliseconds).\n */\n static readonly #persistDebounceMs = 500;\n\n // Storage key prefix for per-chain files\n static readonly #storageKeyPrefix = 'tokensChainsCache';\n\n /**\n * Get storage key for a specific chain.\n *\n * @param chainId - The chain ID.\n * @returns Storage key for the chain.\n */\n static #getChainStorageKey(chainId: Hex): string {\n return `${TokenListController.#storageKeyPrefix}:${chainId}`;\n }\n\n #intervalId?: ReturnType<typeof setTimeout>;\n\n readonly #intervalDelay: number;\n\n readonly #cacheRefreshThreshold: number;\n\n #chainId: Hex;\n\n #abortController: AbortController;\n\n readonly #isDisabled: () => boolean;\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.isDisabled - Optional function returning whether the controller should be disabled.\n * When it returns `true`, `tokensChainsCache` is reset to `{}` at construction and at every\n * polling entry point, no fetches are issued, and persisted storage is cleared on initialize.\n * The function is evaluated dynamically on each entry point so it can be toggled at runtime.\n * @param options.messenger - A restricted messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor({\n chainId,\n onNetworkStateChange,\n interval = DEFAULT_INTERVAL,\n cacheRefreshThreshold = DEFAULT_THRESHOLD,\n isDisabled = (): boolean => false,\n messenger,\n state,\n }: {\n chainId: Hex;\n onNetworkStateChange?: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n interval?: number;\n cacheRefreshThreshold?: number;\n isDisabled?: () => boolean;\n messenger: TokenListControllerMessenger;\n state?: Partial<TokenListState>;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...getDefaultTokenListState(), ...state },\n });\n\n this.#intervalDelay = interval;\n this.setIntervalLength(interval);\n this.#cacheRefreshThreshold = cacheRefreshThreshold;\n this.#chainId = chainId;\n this.#abortController = new AbortController();\n this.#isDisabled = isDisabled;\n\n if (this.#isDisabled()) {\n // Fire-and-forget: storage clearing is async but the constructor is sync.\n // Errors are caught inside `#enforceDisabledState`.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#enforceDisabledState();\n }\n\n if (onNetworkStateChange) {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n onNetworkStateChange(async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n });\n } else {\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n },\n );\n }\n }\n\n /**\n * Initialize the controller by loading cache from storage and running migration.\n * This method should be called by clients after construction.\n *\n * @returns A promise that resolves when initialization is complete.\n */\n async initialize(): Promise<void> {\n if (this.#initializePromise) {\n await this.#initializePromise;\n return;\n }\n\n const executeInit = async (): Promise<void> => {\n try {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n\n await this.#synchronizeCacheWithStorage();\n\n // Subscribe to state changes to automatically persist tokensChainsCache\n this.messenger.subscribe(\n 'TokenListController:stateChange',\n (newCache: TokensChainsCache) => this.#onCacheChanged(newCache),\n (controllerState) => controllerState.tokensChainsCache,\n );\n } catch {\n // do nothing\n } finally {\n this.#initializePromise = undefined;\n }\n };\n\n this.#initializePromise = executeInit();\n\n await this.#initializePromise;\n }\n\n /**\n * Waits for any in-flight initialization to complete.\n * Polling should not run against partially initialized state.\n */\n async #waitForInitialization(): Promise<void> {\n try {\n await this.#initializePromise;\n } catch {\n // do nothing\n }\n }\n\n /**\n * Handle tokensChainsCache changes by detecting which chains changed\n * and scheduling debounced persistence.\n *\n * @param newCache - The new tokensChainsCache state.\n */\n #onCacheChanged(newCache: TokensChainsCache): void {\n // Detect which chains changed by comparing with previous cache\n for (const chainId of Object.keys(newCache) as Hex[]) {\n const newData = newCache[chainId];\n const prevData = this.#previousTokensChainsCache[chainId];\n\n // Chain is new or timestamp changed (indicating data update)\n if (prevData?.timestamp !== newData.timestamp) {\n this.#changedChainsToPersist.add(chainId);\n }\n }\n\n // Update previous cache reference\n this.#previousTokensChainsCache = { ...newCache };\n\n // Schedule persistence if there are changes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n }\n\n /**\n * Debounce persistence of changed chains to storage.\n */\n #debouncePersist(): void {\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n }\n\n this.#persistDebounceTimer = setTimeout(() => {\n // Note: #persistChangedChains handles errors internally via #saveChainCacheToStorage,\n // so this promise will not reject.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#persistChangedChains();\n }, TokenListController.#persistDebounceMs);\n }\n\n /**\n * Persist only the chains that have changed to storage.\n * Reduces write amplification by skipping unchanged chains.\n *\n * If a persist operation is already in-flight, this method returns early\n * and reschedules the debounce to ensure accumulated changes are retried\n * after the current operation completes.\n *\n * @returns A promise that resolves when changed chains are persisted.\n */\n async #persistChangedChains(): Promise<void> {\n if (this.#persistInFlightPromise) {\n // Reschedule debounce to retry accumulated changes after in-flight persist completes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n return;\n }\n\n const chainsToPersist = [...this.#changedChainsToPersist];\n this.#changedChainsToPersist.clear();\n\n if (chainsToPersist.length === 0) {\n return;\n }\n\n this.#persistInFlightPromise = Promise.all(\n chainsToPersist.map((chainId) => this.#saveChainCacheToStorage(chainId)),\n ).then(() => undefined);\n\n try {\n await this.#persistInFlightPromise;\n } finally {\n this.#persistInFlightPromise = undefined;\n }\n }\n\n /**\n * Synchronize tokensChainsCache between state and storage bidirectionally.\n *\n * This method:\n * 1. Loads cached chains from storage (per-chain files) in parallel\n * 2. Merges loaded data into state (preferring existing state to avoid overwriting fresh data)\n * 3. Persists any chains that exist in state but not in storage\n *\n * Called during initialization to ensure state and storage are consistent.\n *\n * @returns A promise that resolves when synchronization is complete.\n */\n async #synchronizeCacheWithStorage(): Promise<void> {\n try {\n const allKeys = await this.messenger.call(\n 'StorageService:getAllKeys',\n name,\n );\n\n // Filter keys that belong to tokensChainsCache (per-chain files)\n const cacheKeys = allKeys.filter((key) =>\n key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n );\n\n // Load all chains in parallel\n const chainCaches = await Promise.all(\n cacheKeys.map(async (key) => {\n // Extract chainId from key: 'tokensChainsCache:0x1' → '0x1'\n const chainId = key.split(':')[1] as Hex;\n\n const { result, error } = await this.messenger.call(\n 'StorageService:getItem',\n name,\n key,\n );\n\n if (error) {\n console.error(\n `TokenListController: Error loading cache for ${chainId}:`,\n error,\n );\n return null;\n }\n\n return result ? { chainId, data: result as DataCache } : null;\n }),\n );\n\n // Build complete cache from loaded chains\n const loadedCache: TokensChainsCache = {};\n chainCaches.forEach((chainCache) => {\n if (chainCache) {\n loadedCache[chainCache.chainId] = chainCache.data;\n }\n });\n\n // Chains in state _before loading persisted state_, from a recent update\n const chainsInState = new Set(\n Object.keys(this.state.tokensChainsCache) as Hex[],\n );\n\n // Merge loaded cache with existing state, preferring existing data\n // (which may be fresher if fetched during initialization)\n if (Object.keys(loadedCache).length > 0) {\n this.update((state) => {\n // Only load chains that don't already exist in state\n // This prevents overwriting fresh API data with stale cached data\n for (const [chainId, cacheData] of Object.entries(loadedCache)) {\n if (!state.tokensChainsCache[chainId as Hex]) {\n state.tokensChainsCache[chainId as Hex] = cacheData;\n }\n }\n });\n }\n\n // Persist chains that exist in state but were not loaded from storage.\n // This handles the case where initial state contains chains that don't exist\n // in storage yet (e.g., fresh data from API). Without this, those chains\n // would be lost on the next app restart.\n for (const chainId of chainsInState) {\n this.#changedChainsToPersist.add(chainId);\n }\n\n // Persist any chains that need to be saved\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n\n this.#previousTokensChainsCache = { ...this.state.tokensChainsCache };\n } catch (error) {\n console.error(\n 'TokenListController: Failed to load cache from storage:',\n error,\n );\n }\n }\n\n /**\n * Save a specific chain's cache to StorageService.\n * This persists only the updated chain's data, reducing write amplification.\n *\n * @param chainId - The chain ID to save.\n * @returns A promise that resolves when saving is complete.\n */\n async #saveChainCacheToStorage(chainId: Hex): Promise<void> {\n try {\n const chainData = this.state.tokensChainsCache[chainId];\n\n if (!chainData) {\n console.warn(`TokenListController: No cache data for chain ${chainId}`);\n return;\n }\n\n const storageKey = TokenListController.#getChainStorageKey(chainId);\n\n await this.messenger.call(\n 'StorageService:setItem',\n name,\n storageKey,\n chainData,\n );\n } catch (error) {\n console.error(\n `TokenListController: Failed to save cache for ${chainId}:`,\n error,\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(\n networkControllerState: NetworkState,\n ): Promise<void> {\n const selectedNetworkClient = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkControllerState.selectedNetworkClientId,\n );\n const { chainId } = selectedNetworkClient.configuration;\n\n if (this.#chainId !== chainId) {\n this.#abortController.abort();\n this.#abortController = new AbortController();\n this.#chainId = chainId;\n }\n }\n\n // Eventually we want to remove start/restart/stop controls in favor of new _executePoll API\n // Maintaining these functions for now until we can safely deprecate them for backwards compatibility\n /**\n * Start polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async start(): Promise<void> {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n if (!isTokenListSupportedForNetwork(this.#chainId)) {\n return;\n }\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Restart polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async restart(): Promise<void> {\n this.#stopPolling();\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Stop polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n stop(): void {\n this.#stopPolling();\n }\n\n /**\n * This stops any active polling.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n override destroy(): void {\n super.destroy();\n this.#stopPolling();\n\n // Cancel any pending debounced persistence operations\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n this.#persistDebounceTimer = undefined;\n }\n this.#changedChainsToPersist.clear();\n }\n\n /**\n * This stops any active polling intervals.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n #stopPolling(): void {\n if (this.#intervalId) {\n clearInterval(this.#intervalId);\n }\n }\n\n /**\n * Starts a new polling interval for a given chainId (this should be deprecated in favor of _executePoll)\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async #startDeprecatedPolling(): Promise<void> {\n // renaming this to avoid collision with base class\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n this.#intervalId = setInterval(async () => {\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n }, this.#intervalDelay);\n }\n\n /**\n * This starts a new polling loop for any given chain. Under the hood it is deduping polls\n *\n * @param input - The input for the poll.\n * @param input.chainId - The chainId of the chain to trigger the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll({ chainId }: TokenListPollingInput): Promise<void> {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n await this.#waitForInitialization();\n await this.fetchTokenList(chainId);\n }\n\n /**\n * Fully enforce the disabled state in a single step:\n * 1. Drop in-memory `tokensChainsCache` to `{}`.\n * 2. Cancel any pending debounced persist and clear the changed-chains set so\n * a stale entry can't write old data after the in-memory reset.\n * 3. Overwrite every persisted `tokensChainsCache:*` entry in StorageService\n * with `{ data: {}, timestamp: 0 }`.\n *\n * Called from every fetching entry point when `isDisabled()` is true so that\n * a runtime toggle propagates to both memory and storage immediately, even\n * if `initialize()` was originally invoked while the controller was enabled.\n *\n * @returns A promise that resolves when persisted entries have been cleared.\n */\n async #enforceDisabledState(): Promise<void> {\n this.#resetCacheState();\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n this.#persistDebounceTimer = undefined;\n }\n this.#changedChainsToPersist.clear();\n await this.#clearPersistedCache();\n }\n\n /**\n * Reset in-memory `tokensChainsCache` to an empty object.\n * Used when the controller is disabled to drop any data already in state.\n */\n #resetCacheState(): void {\n if (Object.keys(this.state.tokensChainsCache).length === 0) {\n return;\n }\n this.update((state) => {\n state.tokensChainsCache = {};\n });\n }\n\n /**\n * Overwrite every persisted `tokensChainsCache:*` entry in StorageService\n * with `{ data: {}, timestamp: 0 }` so the on-disk view reflects the\n * disabled state.\n *\n * @returns A promise that resolves when persisted entries have been cleared.\n */\n async #clearPersistedCache(): Promise<void> {\n try {\n const allKeys = await this.messenger.call(\n 'StorageService:getAllKeys',\n name,\n );\n const cacheKeys = allKeys.filter((key) =>\n key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n );\n const emptyDataCache: DataCache = { data: {}, timestamp: 0 };\n await Promise.all(\n cacheKeys.map((key) =>\n this.messenger.call(\n 'StorageService:setItem',\n name,\n key,\n emptyDataCache,\n ),\n ),\n );\n } catch (error) {\n console.error(\n 'TokenListController: Failed to clear persisted cache:',\n error,\n );\n }\n }\n\n /**\n * Fetching token list from the Token Service API. This will fetch tokens across chains.\n * State changes are automatically persisted via the stateChange subscription.\n *\n * @param chainId - The chainId of the current chain triggering the fetch.\n */\n async fetchTokenList(chainId: Hex): Promise<void> {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n if (this.isCacheValid(chainId)) {\n return;\n }\n\n // Fetch fresh token list from the API\n const tokensFromAPI = await safelyExecute(\n () =>\n fetchTokenListByChainId(\n chainId,\n this.#abortController.signal,\n ) as Promise<TokenListToken[]>,\n );\n\n // Have response - process and update list\n if (tokensFromAPI) {\n // Format tokens from API (HTTP) and update tokenList\n const tokenList: TokenListMap = {};\n for (const token of tokensFromAPI) {\n tokenList[token.address] = {\n ...token,\n aggregators: formatAggregatorNames(token.aggregators),\n iconUrl: formatIconUrlWithProxy({\n chainId,\n tokenAddress: token.address,\n }),\n };\n }\n\n // Update state - persistence happens automatically via subscription\n const newDataCache: DataCache = {\n data: tokenList,\n timestamp: Date.now(),\n };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n return;\n }\n\n // No response - fallback to previous state, or initialise empty.\n // Only initialize with a new timestamp if there's no existing cache.\n // If there's existing cache, keep it as-is without updating the timestamp\n // to avoid making stale data appear \"fresh\" and preventing retry attempts.\n if (!tokensFromAPI) {\n const existingCache = this.state.tokensChainsCache[chainId];\n if (!existingCache) {\n // No existing cache - initialize empty (persistence happens automatically)\n const newDataCache: DataCache = { data: {}, timestamp: Date.now() };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n }\n // If there's existing cache, keep it as-is (don't update timestamp or persist)\n }\n }\n\n isCacheValid(chainId: Hex): boolean {\n const { tokensChainsCache }: TokenListState = this.state;\n const timestamp: number | undefined = tokensChainsCache[chainId]?.timestamp;\n const now = Date.now();\n return (\n timestamp !== undefined && now - timestamp < this.#cacheRefreshThreshold\n );\n }\n}\n\nexport default TokenListController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenListController.cjs","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerStateChangeEvent,\n NetworkState,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n StorageServiceSetItemAction,\n StorageServiceGetItemAction,\n StorageServiceGetAllKeysAction,\n} from '@metamask/storage-service';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n isTokenListSupportedForNetwork,\n formatAggregatorNames,\n formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { TokenRwaData, fetchTokenListByChainId } from './token-service';\n\n// 4 Hour Interval Cache Refresh Threshold\nconst DEFAULT_INTERVAL = 4 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 4 * 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 rwaData?: TokenRwaData;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\nexport type DataCache = {\n timestamp: number;\n data: TokenListMap;\n};\nexport type TokensChainsCache = {\n [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n tokensChainsCache: TokensChainsCache;\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 =\n | NetworkControllerGetNetworkClientByIdAction\n | StorageServiceSetItemAction\n | StorageServiceGetItemAction\n | StorageServiceGetAllKeysAction;\n\ntype AllowedEvents = NetworkControllerStateChangeEvent;\n\nexport type TokenListControllerMessenger = Messenger<\n typeof name,\n TokenListControllerActions | AllowedActions,\n TokenListControllerEvents | AllowedEvents\n>;\n\nconst metadata: StateMetadata<TokenListState> = {\n tokensChainsCache: {\n includeInStateLogs: false,\n persist: false, // Persisted separately via StorageService\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nexport const getDefaultTokenListState = (): TokenListState => {\n return {\n tokensChainsCache: {},\n };\n};\n\n/** The input to start polling for the {@link TokenListController} */\ntype TokenListPollingInput = {\n chainId: Hex;\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 StaticIntervalPollingController<TokenListPollingInput>()<\n typeof name,\n TokenListState,\n TokenListControllerMessenger\n> {\n /**\n * Debounce timer for persisting state changes to storage.\n */\n #persistDebounceTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Promise for the in-flight initialization sequence.\n */\n #initializePromise?: Promise<void>;\n\n /**\n * Promise that resolves when the current persist operation completes.\n * Used to prevent race conditions between persist operations.\n */\n #persistInFlightPromise?: Promise<void>;\n\n /**\n * Tracks which chains have pending changes to persist.\n * Only changed chains are persisted to reduce write amplification.\n */\n readonly #changedChainsToPersist: Set<Hex> = new Set();\n\n /**\n * Previous tokensChainsCache for detecting which chains changed.\n */\n #previousTokensChainsCache: TokensChainsCache = {};\n\n /**\n * Debounce delay for persisting state changes (in milliseconds).\n */\n static readonly #persistDebounceMs = 500;\n\n // Storage key prefix for per-chain files\n static readonly #storageKeyPrefix = 'tokensChainsCache';\n\n /**\n * Get storage key for a specific chain.\n *\n * @param chainId - The chain ID.\n * @returns Storage key for the chain.\n */\n static #getChainStorageKey(chainId: Hex): string {\n return `${TokenListController.#storageKeyPrefix}:${chainId}`;\n }\n\n #intervalId?: ReturnType<typeof setTimeout>;\n\n readonly #intervalDelay: number;\n\n readonly #cacheRefreshThreshold: number;\n\n #chainId: Hex;\n\n #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 messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor({\n chainId,\n onNetworkStateChange,\n interval = DEFAULT_INTERVAL,\n cacheRefreshThreshold = DEFAULT_THRESHOLD,\n messenger,\n state,\n }: {\n chainId: Hex;\n onNetworkStateChange?: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n interval?: number;\n cacheRefreshThreshold?: number;\n messenger: TokenListControllerMessenger;\n state?: Partial<TokenListState>;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...getDefaultTokenListState(), ...state },\n });\n\n this.#intervalDelay = interval;\n this.setIntervalLength(interval);\n this.#cacheRefreshThreshold = cacheRefreshThreshold;\n this.#chainId = chainId;\n this.#abortController = new AbortController();\n\n if (onNetworkStateChange) {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n onNetworkStateChange(async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n });\n } else {\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n },\n );\n }\n }\n\n /**\n * Initialize the controller by loading cache from storage and running migration.\n * This method should be called by clients after construction.\n *\n * @returns A promise that resolves when initialization is complete.\n */\n async initialize(): Promise<void> {\n if (this.#initializePromise) {\n await this.#initializePromise;\n return;\n }\n\n const executeInit = async (): Promise<void> => {\n try {\n await this.#synchronizeCacheWithStorage();\n\n // Subscribe to state changes to automatically persist tokensChainsCache\n this.messenger.subscribe(\n 'TokenListController:stateChange',\n (newCache: TokensChainsCache) => this.#onCacheChanged(newCache),\n (controllerState) => controllerState.tokensChainsCache,\n );\n } catch {\n // do nothing\n } finally {\n this.#initializePromise = undefined;\n }\n };\n\n this.#initializePromise = executeInit();\n\n await this.#initializePromise;\n }\n\n /**\n * Waits for any in-flight initialization to complete.\n * Polling should not run against partially initialized state.\n */\n async #waitForInitialization(): Promise<void> {\n try {\n await this.#initializePromise;\n } catch {\n // do nothing\n }\n }\n\n /**\n * Handle tokensChainsCache changes by detecting which chains changed\n * and scheduling debounced persistence.\n *\n * @param newCache - The new tokensChainsCache state.\n */\n #onCacheChanged(newCache: TokensChainsCache): void {\n // Detect which chains changed by comparing with previous cache\n for (const chainId of Object.keys(newCache) as Hex[]) {\n const newData = newCache[chainId];\n const prevData = this.#previousTokensChainsCache[chainId];\n\n // Chain is new or timestamp changed (indicating data update)\n if (prevData?.timestamp !== newData.timestamp) {\n this.#changedChainsToPersist.add(chainId);\n }\n }\n\n // Update previous cache reference\n this.#previousTokensChainsCache = { ...newCache };\n\n // Schedule persistence if there are changes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n }\n\n /**\n * Debounce persistence of changed chains to storage.\n */\n #debouncePersist(): void {\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n }\n\n this.#persistDebounceTimer = setTimeout(() => {\n // Note: #persistChangedChains handles errors internally via #saveChainCacheToStorage,\n // so this promise will not reject.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#persistChangedChains();\n }, TokenListController.#persistDebounceMs);\n }\n\n /**\n * Persist only the chains that have changed to storage.\n * Reduces write amplification by skipping unchanged chains.\n *\n * If a persist operation is already in-flight, this method returns early\n * and reschedules the debounce to ensure accumulated changes are retried\n * after the current operation completes.\n *\n * @returns A promise that resolves when changed chains are persisted.\n */\n async #persistChangedChains(): Promise<void> {\n if (this.#persistInFlightPromise) {\n // Reschedule debounce to retry accumulated changes after in-flight persist completes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n return;\n }\n\n const chainsToPersist = [...this.#changedChainsToPersist];\n this.#changedChainsToPersist.clear();\n\n if (chainsToPersist.length === 0) {\n return;\n }\n\n this.#persistInFlightPromise = Promise.all(\n chainsToPersist.map((chainId) => this.#saveChainCacheToStorage(chainId)),\n ).then(() => undefined);\n\n try {\n await this.#persistInFlightPromise;\n } finally {\n this.#persistInFlightPromise = undefined;\n }\n }\n\n /**\n * Synchronize tokensChainsCache between state and storage bidirectionally.\n *\n * This method:\n * 1. Loads cached chains from storage (per-chain files) in parallel\n * 2. Merges loaded data into state (preferring existing state to avoid overwriting fresh data)\n * 3. Persists any chains that exist in state but not in storage\n *\n * Called during initialization to ensure state and storage are consistent.\n *\n * @returns A promise that resolves when synchronization is complete.\n */\n async #synchronizeCacheWithStorage(): Promise<void> {\n try {\n const allKeys = await this.messenger.call(\n 'StorageService:getAllKeys',\n name,\n );\n\n // Filter keys that belong to tokensChainsCache (per-chain files)\n const cacheKeys = allKeys.filter((key) =>\n key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n );\n\n // Load all chains in parallel\n const chainCaches = await Promise.all(\n cacheKeys.map(async (key) => {\n // Extract chainId from key: 'tokensChainsCache:0x1' → '0x1'\n const chainId = key.split(':')[1] as Hex;\n\n const { result, error } = await this.messenger.call(\n 'StorageService:getItem',\n name,\n key,\n );\n\n if (error) {\n console.error(\n `TokenListController: Error loading cache for ${chainId}:`,\n error,\n );\n return null;\n }\n\n return result ? { chainId, data: result as DataCache } : null;\n }),\n );\n\n // Build complete cache from loaded chains\n const loadedCache: TokensChainsCache = {};\n chainCaches.forEach((chainCache) => {\n if (chainCache) {\n loadedCache[chainCache.chainId] = chainCache.data;\n }\n });\n\n // Chains in state _before loading persisted state_, from a recent update\n const chainsInState = new Set(\n Object.keys(this.state.tokensChainsCache) as Hex[],\n );\n\n // Merge loaded cache with existing state, preferring existing data\n // (which may be fresher if fetched during initialization)\n if (Object.keys(loadedCache).length > 0) {\n this.update((state) => {\n // Only load chains that don't already exist in state\n // This prevents overwriting fresh API data with stale cached data\n for (const [chainId, cacheData] of Object.entries(loadedCache)) {\n if (!state.tokensChainsCache[chainId as Hex]) {\n state.tokensChainsCache[chainId as Hex] = cacheData;\n }\n }\n });\n }\n\n // Persist chains that exist in state but were not loaded from storage.\n // This handles the case where initial state contains chains that don't exist\n // in storage yet (e.g., fresh data from API). Without this, those chains\n // would be lost on the next app restart.\n for (const chainId of chainsInState) {\n this.#changedChainsToPersist.add(chainId);\n }\n\n // Persist any chains that need to be saved\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n\n this.#previousTokensChainsCache = { ...this.state.tokensChainsCache };\n } catch (error) {\n console.error(\n 'TokenListController: Failed to load cache from storage:',\n error,\n );\n }\n }\n\n /**\n * Save a specific chain's cache to StorageService.\n * This persists only the updated chain's data, reducing write amplification.\n *\n * @param chainId - The chain ID to save.\n * @returns A promise that resolves when saving is complete.\n */\n async #saveChainCacheToStorage(chainId: Hex): Promise<void> {\n try {\n const chainData = this.state.tokensChainsCache[chainId];\n\n if (!chainData) {\n console.warn(`TokenListController: No cache data for chain ${chainId}`);\n return;\n }\n\n const storageKey = TokenListController.#getChainStorageKey(chainId);\n\n await this.messenger.call(\n 'StorageService:setItem',\n name,\n storageKey,\n chainData,\n );\n } catch (error) {\n console.error(\n `TokenListController: Failed to save cache for ${chainId}:`,\n error,\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(\n networkControllerState: NetworkState,\n ): Promise<void> {\n const selectedNetworkClient = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkControllerState.selectedNetworkClientId,\n );\n const { chainId } = selectedNetworkClient.configuration;\n\n if (this.#chainId !== chainId) {\n this.#abortController.abort();\n this.#abortController = new AbortController();\n this.#chainId = chainId;\n }\n }\n\n // Eventually we want to remove start/restart/stop controls in favor of new _executePoll API\n // Maintaining these functions for now until we can safely deprecate them for backwards compatibility\n /**\n * Start polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async start(): Promise<void> {\n if (!isTokenListSupportedForNetwork(this.#chainId)) {\n return;\n }\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Restart polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async restart(): Promise<void> {\n this.#stopPolling();\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Stop polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n stop(): void {\n this.#stopPolling();\n }\n\n /**\n * This stops any active polling.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n override destroy(): void {\n super.destroy();\n this.#stopPolling();\n\n // Cancel any pending debounced persistence operations\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n this.#persistDebounceTimer = undefined;\n }\n this.#changedChainsToPersist.clear();\n }\n\n /**\n * This stops any active polling intervals.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n #stopPolling(): void {\n if (this.#intervalId) {\n clearInterval(this.#intervalId);\n }\n }\n\n /**\n * Starts a new polling interval for a given chainId (this should be deprecated in favor of _executePoll)\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async #startDeprecatedPolling(): Promise<void> {\n // renaming this to avoid collision with base class\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n this.#intervalId = setInterval(async () => {\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n }, this.#intervalDelay);\n }\n\n /**\n * This starts a new polling loop for any given chain. Under the hood it is deduping polls\n *\n * @param input - The input for the poll.\n * @param input.chainId - The chainId of the chain to trigger the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll({ chainId }: TokenListPollingInput): Promise<void> {\n await this.#waitForInitialization();\n return this.fetchTokenList(chainId);\n }\n\n /**\n * Fetching token list from the Token Service API. This will fetch tokens across chains.\n * State changes are automatically persisted via the stateChange subscription.\n *\n * @param chainId - The chainId of the current chain triggering the fetch.\n */\n async fetchTokenList(chainId: Hex): Promise<void> {\n if (this.isCacheValid(chainId)) {\n return;\n }\n\n // Fetch fresh token list from the API\n const tokensFromAPI = await safelyExecute(\n () =>\n fetchTokenListByChainId(\n chainId,\n this.#abortController.signal,\n ) as Promise<TokenListToken[]>,\n );\n\n // Have response - process and update list\n if (tokensFromAPI) {\n // Format tokens from API (HTTP) and update tokenList\n const tokenList: TokenListMap = {};\n for (const token of tokensFromAPI) {\n tokenList[token.address] = {\n ...token,\n aggregators: formatAggregatorNames(token.aggregators),\n iconUrl: formatIconUrlWithProxy({\n chainId,\n tokenAddress: token.address,\n }),\n };\n }\n\n // Update state - persistence happens automatically via subscription\n const newDataCache: DataCache = {\n data: tokenList,\n timestamp: Date.now(),\n };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n return;\n }\n\n // No response - fallback to previous state, or initialise empty.\n // Only initialize with a new timestamp if there's no existing cache.\n // If there's existing cache, keep it as-is without updating the timestamp\n // to avoid making stale data appear \"fresh\" and preventing retry attempts.\n if (!tokensFromAPI) {\n const existingCache = this.state.tokensChainsCache[chainId];\n if (!existingCache) {\n // No existing cache - initialize empty (persistence happens automatically)\n const newDataCache: DataCache = { data: {}, timestamp: Date.now() };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n }\n // If there's existing cache, keep it as-is (don't update timestamp or persist)\n }\n }\n\n isCacheValid(chainId: Hex): boolean {\n const { tokensChainsCache }: TokenListState = this.state;\n const timestamp: number | undefined = tokensChainsCache[chainId]?.timestamp;\n const now = Date.now();\n return (\n timestamp !== undefined && now - timestamp < this.#cacheRefreshThreshold\n );\n }\n}\n\nexport default TokenListController;\n"]}
|
|
@@ -66,19 +66,14 @@ export declare class TokenListController extends TokenListController_base<typeof
|
|
|
66
66
|
* @param options.onNetworkStateChange - A function for registering an event handler for network state changes.
|
|
67
67
|
* @param options.interval - The polling interval, in milliseconds.
|
|
68
68
|
* @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.
|
|
69
|
-
* @param options.isDisabled - Optional function returning whether the controller should be disabled.
|
|
70
|
-
* When it returns `true`, `tokensChainsCache` is reset to `{}` at construction and at every
|
|
71
|
-
* polling entry point, no fetches are issued, and persisted storage is cleared on initialize.
|
|
72
|
-
* The function is evaluated dynamically on each entry point so it can be toggled at runtime.
|
|
73
69
|
* @param options.messenger - A restricted messenger.
|
|
74
70
|
* @param options.state - Initial state to set on this controller.
|
|
75
71
|
*/
|
|
76
|
-
constructor({ chainId, onNetworkStateChange, interval, cacheRefreshThreshold,
|
|
72
|
+
constructor({ chainId, onNetworkStateChange, interval, cacheRefreshThreshold, messenger, state, }: {
|
|
77
73
|
chainId: Hex;
|
|
78
74
|
onNetworkStateChange?: (listener: (networkState: NetworkState) => void) => void;
|
|
79
75
|
interval?: number;
|
|
80
76
|
cacheRefreshThreshold?: number;
|
|
81
|
-
isDisabled?: () => boolean;
|
|
82
77
|
messenger: TokenListControllerMessenger;
|
|
83
78
|
state?: Partial<TokenListState>;
|
|
84
79
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenListController.d.cts","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AAEnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,iCAAiC,EACjC,YAAY,EACZ,2CAA2C,EAC5C,qCAAqC;AAEtC,OAAO,KAAK,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,8BAA8B,EAC/B,kCAAkC;AACnC,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAO3C,OAAO,EAAE,YAAY,EAA2B,4BAAwB;AAMxE,QAAA,MAAM,IAAI,wBAAwB,CAAC;AAEnC,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,iBAAiB,EAAE,iBAAiB,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAC3D,OAAO,IAAI,EACX,cAAc,CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG,wBAAwB,CACtD,OAAO,IAAI,EACX,cAAc,CACf,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAE3D,KAAK,cAAc,GACf,2CAA2C,GAC3C,2BAA2B,GAC3B,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC,KAAK,aAAa,GAAG,iCAAiC,CAAC;AAEvD,MAAM,MAAM,4BAA4B,GAAG,SAAS,CAClD,OAAO,IAAI,EACX,0BAA0B,GAAG,cAAc,EAC3C,yBAAyB,GAAG,aAAa,CAC1C,CAAC;AAWF,eAAO,MAAM,wBAAwB,QAAO,cAI3C,CAAC;AAEF,qEAAqE;AACrE,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,GAAG,CAAC;CACd,CAAC;;;;;;;;;;;;;;;;AAEF;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,yBACvC,OAAO,IAAI,EACX,cAAc,EACd,4BAA4B,CAC7B;;
|
|
1
|
+
{"version":3,"file":"TokenListController.d.cts","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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"}
|
|
@@ -66,19 +66,14 @@ export declare class TokenListController extends TokenListController_base<typeof
|
|
|
66
66
|
* @param options.onNetworkStateChange - A function for registering an event handler for network state changes.
|
|
67
67
|
* @param options.interval - The polling interval, in milliseconds.
|
|
68
68
|
* @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.
|
|
69
|
-
* @param options.isDisabled - Optional function returning whether the controller should be disabled.
|
|
70
|
-
* When it returns `true`, `tokensChainsCache` is reset to `{}` at construction and at every
|
|
71
|
-
* polling entry point, no fetches are issued, and persisted storage is cleared on initialize.
|
|
72
|
-
* The function is evaluated dynamically on each entry point so it can be toggled at runtime.
|
|
73
69
|
* @param options.messenger - A restricted messenger.
|
|
74
70
|
* @param options.state - Initial state to set on this controller.
|
|
75
71
|
*/
|
|
76
|
-
constructor({ chainId, onNetworkStateChange, interval, cacheRefreshThreshold,
|
|
72
|
+
constructor({ chainId, onNetworkStateChange, interval, cacheRefreshThreshold, messenger, state, }: {
|
|
77
73
|
chainId: Hex;
|
|
78
74
|
onNetworkStateChange?: (listener: (networkState: NetworkState) => void) => void;
|
|
79
75
|
interval?: number;
|
|
80
76
|
cacheRefreshThreshold?: number;
|
|
81
|
-
isDisabled?: () => boolean;
|
|
82
77
|
messenger: TokenListControllerMessenger;
|
|
83
78
|
state?: Partial<TokenListState>;
|
|
84
79
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenListController.d.mts","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AAEnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,iCAAiC,EACjC,YAAY,EACZ,2CAA2C,EAC5C,qCAAqC;AAEtC,OAAO,KAAK,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,8BAA8B,EAC/B,kCAAkC;AACnC,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAO3C,OAAO,EAAE,YAAY,EAA2B,4BAAwB;AAMxE,QAAA,MAAM,IAAI,wBAAwB,CAAC;AAEnC,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;CACpB,CAAC;AACF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,OAAO,EAAE,GAAG,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,iBAAiB,EAAE,iBAAiB,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAC3D,OAAO,IAAI,EACX,cAAc,CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG,wBAAwB,CACtD,OAAO,IAAI,EACX,cAAc,CACf,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAE3D,KAAK,cAAc,GACf,2CAA2C,GAC3C,2BAA2B,GAC3B,2BAA2B,GAC3B,8BAA8B,CAAC;AAEnC,KAAK,aAAa,GAAG,iCAAiC,CAAC;AAEvD,MAAM,MAAM,4BAA4B,GAAG,SAAS,CAClD,OAAO,IAAI,EACX,0BAA0B,GAAG,cAAc,EAC3C,yBAAyB,GAAG,aAAa,CAC1C,CAAC;AAWF,eAAO,MAAM,wBAAwB,QAAO,cAI3C,CAAC;AAEF,qEAAqE;AACrE,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,GAAG,CAAC;CACd,CAAC;;;;;;;;;;;;;;;;AAEF;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,yBACvC,OAAO,IAAI,EACX,cAAc,EACd,4BAA4B,CAC7B;;
|
|
1
|
+
{"version":3,"file":"TokenListController.d.mts","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
9
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");
|
|
10
10
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
11
|
};
|
|
12
|
-
var _TokenListController_instances, _a, _TokenListController_persistDebounceTimer, _TokenListController_initializePromise, _TokenListController_persistInFlightPromise, _TokenListController_changedChainsToPersist, _TokenListController_previousTokensChainsCache, _TokenListController_persistDebounceMs, _TokenListController_storageKeyPrefix, _TokenListController_getChainStorageKey, _TokenListController_intervalId, _TokenListController_intervalDelay, _TokenListController_cacheRefreshThreshold, _TokenListController_chainId, _TokenListController_abortController,
|
|
12
|
+
var _TokenListController_instances, _a, _TokenListController_persistDebounceTimer, _TokenListController_initializePromise, _TokenListController_persistInFlightPromise, _TokenListController_changedChainsToPersist, _TokenListController_previousTokensChainsCache, _TokenListController_persistDebounceMs, _TokenListController_storageKeyPrefix, _TokenListController_getChainStorageKey, _TokenListController_intervalId, _TokenListController_intervalDelay, _TokenListController_cacheRefreshThreshold, _TokenListController_chainId, _TokenListController_abortController, _TokenListController_waitForInitialization, _TokenListController_onCacheChanged, _TokenListController_debouncePersist, _TokenListController_persistChangedChains, _TokenListController_synchronizeCacheWithStorage, _TokenListController_saveChainCacheToStorage, _TokenListController_onNetworkControllerStateChange, _TokenListController_stopPolling, _TokenListController_startDeprecatedPolling;
|
|
13
13
|
import { safelyExecute } from "@metamask/controller-utils";
|
|
14
14
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
15
15
|
import { isTokenListSupportedForNetwork, formatAggregatorNames, formatIconUrlWithProxy } from "./assetsUtil.mjs";
|
|
@@ -43,14 +43,10 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
43
43
|
* @param options.onNetworkStateChange - A function for registering an event handler for network state changes.
|
|
44
44
|
* @param options.interval - The polling interval, in milliseconds.
|
|
45
45
|
* @param options.cacheRefreshThreshold - The token cache expiry time, in milliseconds.
|
|
46
|
-
* @param options.isDisabled - Optional function returning whether the controller should be disabled.
|
|
47
|
-
* When it returns `true`, `tokensChainsCache` is reset to `{}` at construction and at every
|
|
48
|
-
* polling entry point, no fetches are issued, and persisted storage is cleared on initialize.
|
|
49
|
-
* The function is evaluated dynamically on each entry point so it can be toggled at runtime.
|
|
50
46
|
* @param options.messenger - A restricted messenger.
|
|
51
47
|
* @param options.state - Initial state to set on this controller.
|
|
52
48
|
*/
|
|
53
|
-
constructor({ chainId, onNetworkStateChange, interval = DEFAULT_INTERVAL, cacheRefreshThreshold = DEFAULT_THRESHOLD,
|
|
49
|
+
constructor({ chainId, onNetworkStateChange, interval = DEFAULT_INTERVAL, cacheRefreshThreshold = DEFAULT_THRESHOLD, messenger, state, }) {
|
|
54
50
|
super({
|
|
55
51
|
name,
|
|
56
52
|
metadata,
|
|
@@ -85,19 +81,11 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
85
81
|
_TokenListController_cacheRefreshThreshold.set(this, void 0);
|
|
86
82
|
_TokenListController_chainId.set(this, void 0);
|
|
87
83
|
_TokenListController_abortController.set(this, void 0);
|
|
88
|
-
_TokenListController_isDisabled.set(this, void 0);
|
|
89
84
|
__classPrivateFieldSet(this, _TokenListController_intervalDelay, interval, "f");
|
|
90
85
|
this.setIntervalLength(interval);
|
|
91
86
|
__classPrivateFieldSet(this, _TokenListController_cacheRefreshThreshold, cacheRefreshThreshold, "f");
|
|
92
87
|
__classPrivateFieldSet(this, _TokenListController_chainId, chainId, "f");
|
|
93
88
|
__classPrivateFieldSet(this, _TokenListController_abortController, new AbortController(), "f");
|
|
94
|
-
__classPrivateFieldSet(this, _TokenListController_isDisabled, isDisabled, "f");
|
|
95
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
96
|
-
// Fire-and-forget: storage clearing is async but the constructor is sync.
|
|
97
|
-
// Errors are caught inside `#enforceDisabledState`.
|
|
98
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
99
|
-
__classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
100
|
-
}
|
|
101
89
|
if (onNetworkStateChange) {
|
|
102
90
|
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
|
|
103
91
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
@@ -127,10 +115,6 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
127
115
|
}
|
|
128
116
|
const executeInit = async () => {
|
|
129
117
|
try {
|
|
130
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
131
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
118
|
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_synchronizeCacheWithStorage).call(this);
|
|
135
119
|
// Subscribe to state changes to automatically persist tokensChainsCache
|
|
136
120
|
this.messenger.subscribe('TokenListController:stateChange', (newCache) => __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_onCacheChanged).call(this, newCache), (controllerState) => controllerState.tokensChainsCache);
|
|
@@ -154,10 +138,6 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
154
138
|
* Consider using the new polling approach instead
|
|
155
139
|
*/
|
|
156
140
|
async start() {
|
|
157
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
158
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
141
|
if (!isTokenListSupportedForNetwork(__classPrivateFieldGet(this, _TokenListController_chainId, "f"))) {
|
|
162
142
|
return;
|
|
163
143
|
}
|
|
@@ -171,10 +151,6 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
171
151
|
*/
|
|
172
152
|
async restart() {
|
|
173
153
|
__classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_stopPolling).call(this);
|
|
174
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
175
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
154
|
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_startDeprecatedPolling).call(this);
|
|
179
155
|
}
|
|
180
156
|
/**
|
|
@@ -210,12 +186,8 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
210
186
|
* @returns A promise that resolves when this operation completes.
|
|
211
187
|
*/
|
|
212
188
|
async _executePoll({ chainId }) {
|
|
213
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
214
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
189
|
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_waitForInitialization).call(this);
|
|
218
|
-
|
|
190
|
+
return this.fetchTokenList(chainId);
|
|
219
191
|
}
|
|
220
192
|
/**
|
|
221
193
|
* Fetching token list from the Token Service API. This will fetch tokens across chains.
|
|
@@ -224,10 +196,6 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
224
196
|
* @param chainId - The chainId of the current chain triggering the fetch.
|
|
225
197
|
*/
|
|
226
198
|
async fetchTokenList(chainId) {
|
|
227
|
-
if (__classPrivateFieldGet(this, _TokenListController_isDisabled, "f").call(this)) {
|
|
228
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_enforceDisabledState).call(this);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
199
|
if (this.isCacheValid(chainId)) {
|
|
232
200
|
return;
|
|
233
201
|
}
|
|
@@ -280,7 +248,7 @@ export class TokenListController extends StaticIntervalPollingController() {
|
|
|
280
248
|
return (timestamp !== undefined && now - timestamp < __classPrivateFieldGet(this, _TokenListController_cacheRefreshThreshold, "f"));
|
|
281
249
|
}
|
|
282
250
|
}
|
|
283
|
-
_a = TokenListController, _TokenListController_persistDebounceTimer = new WeakMap(), _TokenListController_initializePromise = new WeakMap(), _TokenListController_persistInFlightPromise = new WeakMap(), _TokenListController_changedChainsToPersist = new WeakMap(), _TokenListController_previousTokensChainsCache = new WeakMap(), _TokenListController_intervalId = new WeakMap(), _TokenListController_intervalDelay = new WeakMap(), _TokenListController_cacheRefreshThreshold = new WeakMap(), _TokenListController_chainId = new WeakMap(), _TokenListController_abortController = new WeakMap(),
|
|
251
|
+
_a = TokenListController, _TokenListController_persistDebounceTimer = new WeakMap(), _TokenListController_initializePromise = new WeakMap(), _TokenListController_persistInFlightPromise = new WeakMap(), _TokenListController_changedChainsToPersist = new WeakMap(), _TokenListController_previousTokensChainsCache = new WeakMap(), _TokenListController_intervalId = new WeakMap(), _TokenListController_intervalDelay = new WeakMap(), _TokenListController_cacheRefreshThreshold = new WeakMap(), _TokenListController_chainId = new WeakMap(), _TokenListController_abortController = new WeakMap(), _TokenListController_instances = new WeakSet(), _TokenListController_getChainStorageKey = function _TokenListController_getChainStorageKey(chainId) {
|
|
284
252
|
return `${__classPrivateFieldGet(_a, _a, "f", _TokenListController_storageKeyPrefix)}:${chainId}`;
|
|
285
253
|
}, _TokenListController_waitForInitialization =
|
|
286
254
|
/**
|
|
@@ -473,54 +441,6 @@ async function _TokenListController_startDeprecatedPolling() {
|
|
|
473
441
|
__classPrivateFieldSet(this, _TokenListController_intervalId, setInterval(async () => {
|
|
474
442
|
await safelyExecute(() => this.fetchTokenList(__classPrivateFieldGet(this, _TokenListController_chainId, "f")));
|
|
475
443
|
}, __classPrivateFieldGet(this, _TokenListController_intervalDelay, "f")), "f");
|
|
476
|
-
}, _TokenListController_enforceDisabledState =
|
|
477
|
-
/**
|
|
478
|
-
* Fully enforce the disabled state in a single step:
|
|
479
|
-
* 1. Drop in-memory `tokensChainsCache` to `{}`.
|
|
480
|
-
* 2. Cancel any pending debounced persist and clear the changed-chains set so
|
|
481
|
-
* a stale entry can't write old data after the in-memory reset.
|
|
482
|
-
* 3. Overwrite every persisted `tokensChainsCache:*` entry in StorageService
|
|
483
|
-
* with `{ data: {}, timestamp: 0 }`.
|
|
484
|
-
*
|
|
485
|
-
* Called from every fetching entry point when `isDisabled()` is true so that
|
|
486
|
-
* a runtime toggle propagates to both memory and storage immediately, even
|
|
487
|
-
* if `initialize()` was originally invoked while the controller was enabled.
|
|
488
|
-
*
|
|
489
|
-
* @returns A promise that resolves when persisted entries have been cleared.
|
|
490
|
-
*/
|
|
491
|
-
async function _TokenListController_enforceDisabledState() {
|
|
492
|
-
__classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_resetCacheState).call(this);
|
|
493
|
-
if (__classPrivateFieldGet(this, _TokenListController_persistDebounceTimer, "f")) {
|
|
494
|
-
clearTimeout(__classPrivateFieldGet(this, _TokenListController_persistDebounceTimer, "f"));
|
|
495
|
-
__classPrivateFieldSet(this, _TokenListController_persistDebounceTimer, undefined, "f");
|
|
496
|
-
}
|
|
497
|
-
__classPrivateFieldGet(this, _TokenListController_changedChainsToPersist, "f").clear();
|
|
498
|
-
await __classPrivateFieldGet(this, _TokenListController_instances, "m", _TokenListController_clearPersistedCache).call(this);
|
|
499
|
-
}, _TokenListController_resetCacheState = function _TokenListController_resetCacheState() {
|
|
500
|
-
if (Object.keys(this.state.tokensChainsCache).length === 0) {
|
|
501
|
-
return;
|
|
502
|
-
}
|
|
503
|
-
this.update((state) => {
|
|
504
|
-
state.tokensChainsCache = {};
|
|
505
|
-
});
|
|
506
|
-
}, _TokenListController_clearPersistedCache =
|
|
507
|
-
/**
|
|
508
|
-
* Overwrite every persisted `tokensChainsCache:*` entry in StorageService
|
|
509
|
-
* with `{ data: {}, timestamp: 0 }` so the on-disk view reflects the
|
|
510
|
-
* disabled state.
|
|
511
|
-
*
|
|
512
|
-
* @returns A promise that resolves when persisted entries have been cleared.
|
|
513
|
-
*/
|
|
514
|
-
async function _TokenListController_clearPersistedCache() {
|
|
515
|
-
try {
|
|
516
|
-
const allKeys = await this.messenger.call('StorageService:getAllKeys', name);
|
|
517
|
-
const cacheKeys = allKeys.filter((key) => key.startsWith(`${__classPrivateFieldGet(_a, _a, "f", _TokenListController_storageKeyPrefix)}:`));
|
|
518
|
-
const emptyDataCache = { data: {}, timestamp: 0 };
|
|
519
|
-
await Promise.all(cacheKeys.map((key) => this.messenger.call('StorageService:setItem', name, key, emptyDataCache)));
|
|
520
|
-
}
|
|
521
|
-
catch (error) {
|
|
522
|
-
console.error('TokenListController: Failed to clear persisted cache:', error);
|
|
523
|
-
}
|
|
524
444
|
};
|
|
525
445
|
/**
|
|
526
446
|
* Debounce delay for persisting state changes (in milliseconds).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenListController.mjs","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerStateChangeEvent,\n NetworkState,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n StorageServiceSetItemAction,\n StorageServiceGetItemAction,\n StorageServiceGetAllKeysAction,\n} from '@metamask/storage-service';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n isTokenListSupportedForNetwork,\n formatAggregatorNames,\n formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { TokenRwaData, fetchTokenListByChainId } from './token-service';\n\n// 4 Hour Interval Cache Refresh Threshold\nconst DEFAULT_INTERVAL = 4 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 4 * 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 rwaData?: TokenRwaData;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\nexport type DataCache = {\n timestamp: number;\n data: TokenListMap;\n};\nexport type TokensChainsCache = {\n [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n tokensChainsCache: TokensChainsCache;\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 =\n | NetworkControllerGetNetworkClientByIdAction\n | StorageServiceSetItemAction\n | StorageServiceGetItemAction\n | StorageServiceGetAllKeysAction;\n\ntype AllowedEvents = NetworkControllerStateChangeEvent;\n\nexport type TokenListControllerMessenger = Messenger<\n typeof name,\n TokenListControllerActions | AllowedActions,\n TokenListControllerEvents | AllowedEvents\n>;\n\nconst metadata: StateMetadata<TokenListState> = {\n tokensChainsCache: {\n includeInStateLogs: false,\n persist: false, // Persisted separately via StorageService\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nexport const getDefaultTokenListState = (): TokenListState => {\n return {\n tokensChainsCache: {},\n };\n};\n\n/** The input to start polling for the {@link TokenListController} */\ntype TokenListPollingInput = {\n chainId: Hex;\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 StaticIntervalPollingController<TokenListPollingInput>()<\n typeof name,\n TokenListState,\n TokenListControllerMessenger\n> {\n /**\n * Debounce timer for persisting state changes to storage.\n */\n #persistDebounceTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Promise for the in-flight initialization sequence.\n */\n #initializePromise?: Promise<void>;\n\n /**\n * Promise that resolves when the current persist operation completes.\n * Used to prevent race conditions between persist operations.\n */\n #persistInFlightPromise?: Promise<void>;\n\n /**\n * Tracks which chains have pending changes to persist.\n * Only changed chains are persisted to reduce write amplification.\n */\n readonly #changedChainsToPersist: Set<Hex> = new Set();\n\n /**\n * Previous tokensChainsCache for detecting which chains changed.\n */\n #previousTokensChainsCache: TokensChainsCache = {};\n\n /**\n * Debounce delay for persisting state changes (in milliseconds).\n */\n static readonly #persistDebounceMs = 500;\n\n // Storage key prefix for per-chain files\n static readonly #storageKeyPrefix = 'tokensChainsCache';\n\n /**\n * Get storage key for a specific chain.\n *\n * @param chainId - The chain ID.\n * @returns Storage key for the chain.\n */\n static #getChainStorageKey(chainId: Hex): string {\n return `${TokenListController.#storageKeyPrefix}:${chainId}`;\n }\n\n #intervalId?: ReturnType<typeof setTimeout>;\n\n readonly #intervalDelay: number;\n\n readonly #cacheRefreshThreshold: number;\n\n #chainId: Hex;\n\n #abortController: AbortController;\n\n readonly #isDisabled: () => boolean;\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.isDisabled - Optional function returning whether the controller should be disabled.\n * When it returns `true`, `tokensChainsCache` is reset to `{}` at construction and at every\n * polling entry point, no fetches are issued, and persisted storage is cleared on initialize.\n * The function is evaluated dynamically on each entry point so it can be toggled at runtime.\n * @param options.messenger - A restricted messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor({\n chainId,\n onNetworkStateChange,\n interval = DEFAULT_INTERVAL,\n cacheRefreshThreshold = DEFAULT_THRESHOLD,\n isDisabled = (): boolean => false,\n messenger,\n state,\n }: {\n chainId: Hex;\n onNetworkStateChange?: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n interval?: number;\n cacheRefreshThreshold?: number;\n isDisabled?: () => boolean;\n messenger: TokenListControllerMessenger;\n state?: Partial<TokenListState>;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...getDefaultTokenListState(), ...state },\n });\n\n this.#intervalDelay = interval;\n this.setIntervalLength(interval);\n this.#cacheRefreshThreshold = cacheRefreshThreshold;\n this.#chainId = chainId;\n this.#abortController = new AbortController();\n this.#isDisabled = isDisabled;\n\n if (this.#isDisabled()) {\n // Fire-and-forget: storage clearing is async but the constructor is sync.\n // Errors are caught inside `#enforceDisabledState`.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#enforceDisabledState();\n }\n\n if (onNetworkStateChange) {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n onNetworkStateChange(async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n });\n } else {\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n },\n );\n }\n }\n\n /**\n * Initialize the controller by loading cache from storage and running migration.\n * This method should be called by clients after construction.\n *\n * @returns A promise that resolves when initialization is complete.\n */\n async initialize(): Promise<void> {\n if (this.#initializePromise) {\n await this.#initializePromise;\n return;\n }\n\n const executeInit = async (): Promise<void> => {\n try {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n\n await this.#synchronizeCacheWithStorage();\n\n // Subscribe to state changes to automatically persist tokensChainsCache\n this.messenger.subscribe(\n 'TokenListController:stateChange',\n (newCache: TokensChainsCache) => this.#onCacheChanged(newCache),\n (controllerState) => controllerState.tokensChainsCache,\n );\n } catch {\n // do nothing\n } finally {\n this.#initializePromise = undefined;\n }\n };\n\n this.#initializePromise = executeInit();\n\n await this.#initializePromise;\n }\n\n /**\n * Waits for any in-flight initialization to complete.\n * Polling should not run against partially initialized state.\n */\n async #waitForInitialization(): Promise<void> {\n try {\n await this.#initializePromise;\n } catch {\n // do nothing\n }\n }\n\n /**\n * Handle tokensChainsCache changes by detecting which chains changed\n * and scheduling debounced persistence.\n *\n * @param newCache - The new tokensChainsCache state.\n */\n #onCacheChanged(newCache: TokensChainsCache): void {\n // Detect which chains changed by comparing with previous cache\n for (const chainId of Object.keys(newCache) as Hex[]) {\n const newData = newCache[chainId];\n const prevData = this.#previousTokensChainsCache[chainId];\n\n // Chain is new or timestamp changed (indicating data update)\n if (prevData?.timestamp !== newData.timestamp) {\n this.#changedChainsToPersist.add(chainId);\n }\n }\n\n // Update previous cache reference\n this.#previousTokensChainsCache = { ...newCache };\n\n // Schedule persistence if there are changes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n }\n\n /**\n * Debounce persistence of changed chains to storage.\n */\n #debouncePersist(): void {\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n }\n\n this.#persistDebounceTimer = setTimeout(() => {\n // Note: #persistChangedChains handles errors internally via #saveChainCacheToStorage,\n // so this promise will not reject.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#persistChangedChains();\n }, TokenListController.#persistDebounceMs);\n }\n\n /**\n * Persist only the chains that have changed to storage.\n * Reduces write amplification by skipping unchanged chains.\n *\n * If a persist operation is already in-flight, this method returns early\n * and reschedules the debounce to ensure accumulated changes are retried\n * after the current operation completes.\n *\n * @returns A promise that resolves when changed chains are persisted.\n */\n async #persistChangedChains(): Promise<void> {\n if (this.#persistInFlightPromise) {\n // Reschedule debounce to retry accumulated changes after in-flight persist completes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n return;\n }\n\n const chainsToPersist = [...this.#changedChainsToPersist];\n this.#changedChainsToPersist.clear();\n\n if (chainsToPersist.length === 0) {\n return;\n }\n\n this.#persistInFlightPromise = Promise.all(\n chainsToPersist.map((chainId) => this.#saveChainCacheToStorage(chainId)),\n ).then(() => undefined);\n\n try {\n await this.#persistInFlightPromise;\n } finally {\n this.#persistInFlightPromise = undefined;\n }\n }\n\n /**\n * Synchronize tokensChainsCache between state and storage bidirectionally.\n *\n * This method:\n * 1. Loads cached chains from storage (per-chain files) in parallel\n * 2. Merges loaded data into state (preferring existing state to avoid overwriting fresh data)\n * 3. Persists any chains that exist in state but not in storage\n *\n * Called during initialization to ensure state and storage are consistent.\n *\n * @returns A promise that resolves when synchronization is complete.\n */\n async #synchronizeCacheWithStorage(): Promise<void> {\n try {\n const allKeys = await this.messenger.call(\n 'StorageService:getAllKeys',\n name,\n );\n\n // Filter keys that belong to tokensChainsCache (per-chain files)\n const cacheKeys = allKeys.filter((key) =>\n key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n );\n\n // Load all chains in parallel\n const chainCaches = await Promise.all(\n cacheKeys.map(async (key) => {\n // Extract chainId from key: 'tokensChainsCache:0x1' → '0x1'\n const chainId = key.split(':')[1] as Hex;\n\n const { result, error } = await this.messenger.call(\n 'StorageService:getItem',\n name,\n key,\n );\n\n if (error) {\n console.error(\n `TokenListController: Error loading cache for ${chainId}:`,\n error,\n );\n return null;\n }\n\n return result ? { chainId, data: result as DataCache } : null;\n }),\n );\n\n // Build complete cache from loaded chains\n const loadedCache: TokensChainsCache = {};\n chainCaches.forEach((chainCache) => {\n if (chainCache) {\n loadedCache[chainCache.chainId] = chainCache.data;\n }\n });\n\n // Chains in state _before loading persisted state_, from a recent update\n const chainsInState = new Set(\n Object.keys(this.state.tokensChainsCache) as Hex[],\n );\n\n // Merge loaded cache with existing state, preferring existing data\n // (which may be fresher if fetched during initialization)\n if (Object.keys(loadedCache).length > 0) {\n this.update((state) => {\n // Only load chains that don't already exist in state\n // This prevents overwriting fresh API data with stale cached data\n for (const [chainId, cacheData] of Object.entries(loadedCache)) {\n if (!state.tokensChainsCache[chainId as Hex]) {\n state.tokensChainsCache[chainId as Hex] = cacheData;\n }\n }\n });\n }\n\n // Persist chains that exist in state but were not loaded from storage.\n // This handles the case where initial state contains chains that don't exist\n // in storage yet (e.g., fresh data from API). Without this, those chains\n // would be lost on the next app restart.\n for (const chainId of chainsInState) {\n this.#changedChainsToPersist.add(chainId);\n }\n\n // Persist any chains that need to be saved\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n\n this.#previousTokensChainsCache = { ...this.state.tokensChainsCache };\n } catch (error) {\n console.error(\n 'TokenListController: Failed to load cache from storage:',\n error,\n );\n }\n }\n\n /**\n * Save a specific chain's cache to StorageService.\n * This persists only the updated chain's data, reducing write amplification.\n *\n * @param chainId - The chain ID to save.\n * @returns A promise that resolves when saving is complete.\n */\n async #saveChainCacheToStorage(chainId: Hex): Promise<void> {\n try {\n const chainData = this.state.tokensChainsCache[chainId];\n\n if (!chainData) {\n console.warn(`TokenListController: No cache data for chain ${chainId}`);\n return;\n }\n\n const storageKey = TokenListController.#getChainStorageKey(chainId);\n\n await this.messenger.call(\n 'StorageService:setItem',\n name,\n storageKey,\n chainData,\n );\n } catch (error) {\n console.error(\n `TokenListController: Failed to save cache for ${chainId}:`,\n error,\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(\n networkControllerState: NetworkState,\n ): Promise<void> {\n const selectedNetworkClient = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkControllerState.selectedNetworkClientId,\n );\n const { chainId } = selectedNetworkClient.configuration;\n\n if (this.#chainId !== chainId) {\n this.#abortController.abort();\n this.#abortController = new AbortController();\n this.#chainId = chainId;\n }\n }\n\n // Eventually we want to remove start/restart/stop controls in favor of new _executePoll API\n // Maintaining these functions for now until we can safely deprecate them for backwards compatibility\n /**\n * Start polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async start(): Promise<void> {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n if (!isTokenListSupportedForNetwork(this.#chainId)) {\n return;\n }\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Restart polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async restart(): Promise<void> {\n this.#stopPolling();\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Stop polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n stop(): void {\n this.#stopPolling();\n }\n\n /**\n * This stops any active polling.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n override destroy(): void {\n super.destroy();\n this.#stopPolling();\n\n // Cancel any pending debounced persistence operations\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n this.#persistDebounceTimer = undefined;\n }\n this.#changedChainsToPersist.clear();\n }\n\n /**\n * This stops any active polling intervals.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n #stopPolling(): void {\n if (this.#intervalId) {\n clearInterval(this.#intervalId);\n }\n }\n\n /**\n * Starts a new polling interval for a given chainId (this should be deprecated in favor of _executePoll)\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async #startDeprecatedPolling(): Promise<void> {\n // renaming this to avoid collision with base class\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n this.#intervalId = setInterval(async () => {\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n }, this.#intervalDelay);\n }\n\n /**\n * This starts a new polling loop for any given chain. Under the hood it is deduping polls\n *\n * @param input - The input for the poll.\n * @param input.chainId - The chainId of the chain to trigger the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll({ chainId }: TokenListPollingInput): Promise<void> {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n await this.#waitForInitialization();\n await this.fetchTokenList(chainId);\n }\n\n /**\n * Fully enforce the disabled state in a single step:\n * 1. Drop in-memory `tokensChainsCache` to `{}`.\n * 2. Cancel any pending debounced persist and clear the changed-chains set so\n * a stale entry can't write old data after the in-memory reset.\n * 3. Overwrite every persisted `tokensChainsCache:*` entry in StorageService\n * with `{ data: {}, timestamp: 0 }`.\n *\n * Called from every fetching entry point when `isDisabled()` is true so that\n * a runtime toggle propagates to both memory and storage immediately, even\n * if `initialize()` was originally invoked while the controller was enabled.\n *\n * @returns A promise that resolves when persisted entries have been cleared.\n */\n async #enforceDisabledState(): Promise<void> {\n this.#resetCacheState();\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n this.#persistDebounceTimer = undefined;\n }\n this.#changedChainsToPersist.clear();\n await this.#clearPersistedCache();\n }\n\n /**\n * Reset in-memory `tokensChainsCache` to an empty object.\n * Used when the controller is disabled to drop any data already in state.\n */\n #resetCacheState(): void {\n if (Object.keys(this.state.tokensChainsCache).length === 0) {\n return;\n }\n this.update((state) => {\n state.tokensChainsCache = {};\n });\n }\n\n /**\n * Overwrite every persisted `tokensChainsCache:*` entry in StorageService\n * with `{ data: {}, timestamp: 0 }` so the on-disk view reflects the\n * disabled state.\n *\n * @returns A promise that resolves when persisted entries have been cleared.\n */\n async #clearPersistedCache(): Promise<void> {\n try {\n const allKeys = await this.messenger.call(\n 'StorageService:getAllKeys',\n name,\n );\n const cacheKeys = allKeys.filter((key) =>\n key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n );\n const emptyDataCache: DataCache = { data: {}, timestamp: 0 };\n await Promise.all(\n cacheKeys.map((key) =>\n this.messenger.call(\n 'StorageService:setItem',\n name,\n key,\n emptyDataCache,\n ),\n ),\n );\n } catch (error) {\n console.error(\n 'TokenListController: Failed to clear persisted cache:',\n error,\n );\n }\n }\n\n /**\n * Fetching token list from the Token Service API. This will fetch tokens across chains.\n * State changes are automatically persisted via the stateChange subscription.\n *\n * @param chainId - The chainId of the current chain triggering the fetch.\n */\n async fetchTokenList(chainId: Hex): Promise<void> {\n if (this.#isDisabled()) {\n await this.#enforceDisabledState();\n return;\n }\n if (this.isCacheValid(chainId)) {\n return;\n }\n\n // Fetch fresh token list from the API\n const tokensFromAPI = await safelyExecute(\n () =>\n fetchTokenListByChainId(\n chainId,\n this.#abortController.signal,\n ) as Promise<TokenListToken[]>,\n );\n\n // Have response - process and update list\n if (tokensFromAPI) {\n // Format tokens from API (HTTP) and update tokenList\n const tokenList: TokenListMap = {};\n for (const token of tokensFromAPI) {\n tokenList[token.address] = {\n ...token,\n aggregators: formatAggregatorNames(token.aggregators),\n iconUrl: formatIconUrlWithProxy({\n chainId,\n tokenAddress: token.address,\n }),\n };\n }\n\n // Update state - persistence happens automatically via subscription\n const newDataCache: DataCache = {\n data: tokenList,\n timestamp: Date.now(),\n };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n return;\n }\n\n // No response - fallback to previous state, or initialise empty.\n // Only initialize with a new timestamp if there's no existing cache.\n // If there's existing cache, keep it as-is without updating the timestamp\n // to avoid making stale data appear \"fresh\" and preventing retry attempts.\n if (!tokensFromAPI) {\n const existingCache = this.state.tokensChainsCache[chainId];\n if (!existingCache) {\n // No existing cache - initialize empty (persistence happens automatically)\n const newDataCache: DataCache = { data: {}, timestamp: Date.now() };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n }\n // If there's existing cache, keep it as-is (don't update timestamp or persist)\n }\n }\n\n isCacheValid(chainId: Hex): boolean {\n const { tokensChainsCache }: TokenListState = this.state;\n const timestamp: number | undefined = tokensChainsCache[chainId]?.timestamp;\n const now = Date.now();\n return (\n timestamp !== undefined && now - timestamp < this.#cacheRefreshThreshold\n );\n }\n}\n\nexport default TokenListController;\n"]}
|
|
1
|
+
{"version":3,"file":"TokenListController.mjs","sourceRoot":"","sources":["../src/TokenListController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { safelyExecute } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n NetworkControllerStateChangeEvent,\n NetworkState,\n NetworkControllerGetNetworkClientByIdAction,\n} from '@metamask/network-controller';\nimport { StaticIntervalPollingController } from '@metamask/polling-controller';\nimport type {\n StorageServiceSetItemAction,\n StorageServiceGetItemAction,\n StorageServiceGetAllKeysAction,\n} from '@metamask/storage-service';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n isTokenListSupportedForNetwork,\n formatAggregatorNames,\n formatIconUrlWithProxy,\n} from './assetsUtil';\nimport { TokenRwaData, fetchTokenListByChainId } from './token-service';\n\n// 4 Hour Interval Cache Refresh Threshold\nconst DEFAULT_INTERVAL = 4 * 60 * 60 * 1000;\nconst DEFAULT_THRESHOLD = 4 * 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 rwaData?: TokenRwaData;\n};\n\nexport type TokenListMap = Record<string, TokenListToken>;\n\nexport type DataCache = {\n timestamp: number;\n data: TokenListMap;\n};\nexport type TokensChainsCache = {\n [chainId: Hex]: DataCache;\n};\n\nexport type TokenListState = {\n tokensChainsCache: TokensChainsCache;\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 =\n | NetworkControllerGetNetworkClientByIdAction\n | StorageServiceSetItemAction\n | StorageServiceGetItemAction\n | StorageServiceGetAllKeysAction;\n\ntype AllowedEvents = NetworkControllerStateChangeEvent;\n\nexport type TokenListControllerMessenger = Messenger<\n typeof name,\n TokenListControllerActions | AllowedActions,\n TokenListControllerEvents | AllowedEvents\n>;\n\nconst metadata: StateMetadata<TokenListState> = {\n tokensChainsCache: {\n includeInStateLogs: false,\n persist: false, // Persisted separately via StorageService\n includeInDebugSnapshot: true,\n usedInUi: true,\n },\n};\n\nexport const getDefaultTokenListState = (): TokenListState => {\n return {\n tokensChainsCache: {},\n };\n};\n\n/** The input to start polling for the {@link TokenListController} */\ntype TokenListPollingInput = {\n chainId: Hex;\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 StaticIntervalPollingController<TokenListPollingInput>()<\n typeof name,\n TokenListState,\n TokenListControllerMessenger\n> {\n /**\n * Debounce timer for persisting state changes to storage.\n */\n #persistDebounceTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Promise for the in-flight initialization sequence.\n */\n #initializePromise?: Promise<void>;\n\n /**\n * Promise that resolves when the current persist operation completes.\n * Used to prevent race conditions between persist operations.\n */\n #persistInFlightPromise?: Promise<void>;\n\n /**\n * Tracks which chains have pending changes to persist.\n * Only changed chains are persisted to reduce write amplification.\n */\n readonly #changedChainsToPersist: Set<Hex> = new Set();\n\n /**\n * Previous tokensChainsCache for detecting which chains changed.\n */\n #previousTokensChainsCache: TokensChainsCache = {};\n\n /**\n * Debounce delay for persisting state changes (in milliseconds).\n */\n static readonly #persistDebounceMs = 500;\n\n // Storage key prefix for per-chain files\n static readonly #storageKeyPrefix = 'tokensChainsCache';\n\n /**\n * Get storage key for a specific chain.\n *\n * @param chainId - The chain ID.\n * @returns Storage key for the chain.\n */\n static #getChainStorageKey(chainId: Hex): string {\n return `${TokenListController.#storageKeyPrefix}:${chainId}`;\n }\n\n #intervalId?: ReturnType<typeof setTimeout>;\n\n readonly #intervalDelay: number;\n\n readonly #cacheRefreshThreshold: number;\n\n #chainId: Hex;\n\n #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 messenger.\n * @param options.state - Initial state to set on this controller.\n */\n constructor({\n chainId,\n onNetworkStateChange,\n interval = DEFAULT_INTERVAL,\n cacheRefreshThreshold = DEFAULT_THRESHOLD,\n messenger,\n state,\n }: {\n chainId: Hex;\n onNetworkStateChange?: (\n listener: (networkState: NetworkState) => void,\n ) => void;\n interval?: number;\n cacheRefreshThreshold?: number;\n messenger: TokenListControllerMessenger;\n state?: Partial<TokenListState>;\n }) {\n super({\n name,\n metadata,\n messenger,\n state: { ...getDefaultTokenListState(), ...state },\n });\n\n this.#intervalDelay = interval;\n this.setIntervalLength(interval);\n this.#cacheRefreshThreshold = cacheRefreshThreshold;\n this.#chainId = chainId;\n this.#abortController = new AbortController();\n\n if (onNetworkStateChange) {\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n onNetworkStateChange(async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n });\n } else {\n this.messenger.subscribe(\n 'NetworkController:stateChange',\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n async (networkControllerState) => {\n await this.#onNetworkControllerStateChange(networkControllerState);\n },\n );\n }\n }\n\n /**\n * Initialize the controller by loading cache from storage and running migration.\n * This method should be called by clients after construction.\n *\n * @returns A promise that resolves when initialization is complete.\n */\n async initialize(): Promise<void> {\n if (this.#initializePromise) {\n await this.#initializePromise;\n return;\n }\n\n const executeInit = async (): Promise<void> => {\n try {\n await this.#synchronizeCacheWithStorage();\n\n // Subscribe to state changes to automatically persist tokensChainsCache\n this.messenger.subscribe(\n 'TokenListController:stateChange',\n (newCache: TokensChainsCache) => this.#onCacheChanged(newCache),\n (controllerState) => controllerState.tokensChainsCache,\n );\n } catch {\n // do nothing\n } finally {\n this.#initializePromise = undefined;\n }\n };\n\n this.#initializePromise = executeInit();\n\n await this.#initializePromise;\n }\n\n /**\n * Waits for any in-flight initialization to complete.\n * Polling should not run against partially initialized state.\n */\n async #waitForInitialization(): Promise<void> {\n try {\n await this.#initializePromise;\n } catch {\n // do nothing\n }\n }\n\n /**\n * Handle tokensChainsCache changes by detecting which chains changed\n * and scheduling debounced persistence.\n *\n * @param newCache - The new tokensChainsCache state.\n */\n #onCacheChanged(newCache: TokensChainsCache): void {\n // Detect which chains changed by comparing with previous cache\n for (const chainId of Object.keys(newCache) as Hex[]) {\n const newData = newCache[chainId];\n const prevData = this.#previousTokensChainsCache[chainId];\n\n // Chain is new or timestamp changed (indicating data update)\n if (prevData?.timestamp !== newData.timestamp) {\n this.#changedChainsToPersist.add(chainId);\n }\n }\n\n // Update previous cache reference\n this.#previousTokensChainsCache = { ...newCache };\n\n // Schedule persistence if there are changes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n }\n\n /**\n * Debounce persistence of changed chains to storage.\n */\n #debouncePersist(): void {\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n }\n\n this.#persistDebounceTimer = setTimeout(() => {\n // Note: #persistChangedChains handles errors internally via #saveChainCacheToStorage,\n // so this promise will not reject.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.#persistChangedChains();\n }, TokenListController.#persistDebounceMs);\n }\n\n /**\n * Persist only the chains that have changed to storage.\n * Reduces write amplification by skipping unchanged chains.\n *\n * If a persist operation is already in-flight, this method returns early\n * and reschedules the debounce to ensure accumulated changes are retried\n * after the current operation completes.\n *\n * @returns A promise that resolves when changed chains are persisted.\n */\n async #persistChangedChains(): Promise<void> {\n if (this.#persistInFlightPromise) {\n // Reschedule debounce to retry accumulated changes after in-flight persist completes\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n return;\n }\n\n const chainsToPersist = [...this.#changedChainsToPersist];\n this.#changedChainsToPersist.clear();\n\n if (chainsToPersist.length === 0) {\n return;\n }\n\n this.#persistInFlightPromise = Promise.all(\n chainsToPersist.map((chainId) => this.#saveChainCacheToStorage(chainId)),\n ).then(() => undefined);\n\n try {\n await this.#persistInFlightPromise;\n } finally {\n this.#persistInFlightPromise = undefined;\n }\n }\n\n /**\n * Synchronize tokensChainsCache between state and storage bidirectionally.\n *\n * This method:\n * 1. Loads cached chains from storage (per-chain files) in parallel\n * 2. Merges loaded data into state (preferring existing state to avoid overwriting fresh data)\n * 3. Persists any chains that exist in state but not in storage\n *\n * Called during initialization to ensure state and storage are consistent.\n *\n * @returns A promise that resolves when synchronization is complete.\n */\n async #synchronizeCacheWithStorage(): Promise<void> {\n try {\n const allKeys = await this.messenger.call(\n 'StorageService:getAllKeys',\n name,\n );\n\n // Filter keys that belong to tokensChainsCache (per-chain files)\n const cacheKeys = allKeys.filter((key) =>\n key.startsWith(`${TokenListController.#storageKeyPrefix}:`),\n );\n\n // Load all chains in parallel\n const chainCaches = await Promise.all(\n cacheKeys.map(async (key) => {\n // Extract chainId from key: 'tokensChainsCache:0x1' → '0x1'\n const chainId = key.split(':')[1] as Hex;\n\n const { result, error } = await this.messenger.call(\n 'StorageService:getItem',\n name,\n key,\n );\n\n if (error) {\n console.error(\n `TokenListController: Error loading cache for ${chainId}:`,\n error,\n );\n return null;\n }\n\n return result ? { chainId, data: result as DataCache } : null;\n }),\n );\n\n // Build complete cache from loaded chains\n const loadedCache: TokensChainsCache = {};\n chainCaches.forEach((chainCache) => {\n if (chainCache) {\n loadedCache[chainCache.chainId] = chainCache.data;\n }\n });\n\n // Chains in state _before loading persisted state_, from a recent update\n const chainsInState = new Set(\n Object.keys(this.state.tokensChainsCache) as Hex[],\n );\n\n // Merge loaded cache with existing state, preferring existing data\n // (which may be fresher if fetched during initialization)\n if (Object.keys(loadedCache).length > 0) {\n this.update((state) => {\n // Only load chains that don't already exist in state\n // This prevents overwriting fresh API data with stale cached data\n for (const [chainId, cacheData] of Object.entries(loadedCache)) {\n if (!state.tokensChainsCache[chainId as Hex]) {\n state.tokensChainsCache[chainId as Hex] = cacheData;\n }\n }\n });\n }\n\n // Persist chains that exist in state but were not loaded from storage.\n // This handles the case where initial state contains chains that don't exist\n // in storage yet (e.g., fresh data from API). Without this, those chains\n // would be lost on the next app restart.\n for (const chainId of chainsInState) {\n this.#changedChainsToPersist.add(chainId);\n }\n\n // Persist any chains that need to be saved\n if (this.#changedChainsToPersist.size > 0) {\n this.#debouncePersist();\n }\n\n this.#previousTokensChainsCache = { ...this.state.tokensChainsCache };\n } catch (error) {\n console.error(\n 'TokenListController: Failed to load cache from storage:',\n error,\n );\n }\n }\n\n /**\n * Save a specific chain's cache to StorageService.\n * This persists only the updated chain's data, reducing write amplification.\n *\n * @param chainId - The chain ID to save.\n * @returns A promise that resolves when saving is complete.\n */\n async #saveChainCacheToStorage(chainId: Hex): Promise<void> {\n try {\n const chainData = this.state.tokensChainsCache[chainId];\n\n if (!chainData) {\n console.warn(`TokenListController: No cache data for chain ${chainId}`);\n return;\n }\n\n const storageKey = TokenListController.#getChainStorageKey(chainId);\n\n await this.messenger.call(\n 'StorageService:setItem',\n name,\n storageKey,\n chainData,\n );\n } catch (error) {\n console.error(\n `TokenListController: Failed to save cache for ${chainId}:`,\n error,\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(\n networkControllerState: NetworkState,\n ): Promise<void> {\n const selectedNetworkClient = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkControllerState.selectedNetworkClientId,\n );\n const { chainId } = selectedNetworkClient.configuration;\n\n if (this.#chainId !== chainId) {\n this.#abortController.abort();\n this.#abortController = new AbortController();\n this.#chainId = chainId;\n }\n }\n\n // Eventually we want to remove start/restart/stop controls in favor of new _executePoll API\n // Maintaining these functions for now until we can safely deprecate them for backwards compatibility\n /**\n * Start polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async start(): Promise<void> {\n if (!isTokenListSupportedForNetwork(this.#chainId)) {\n return;\n }\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Restart polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async restart(): Promise<void> {\n this.#stopPolling();\n await this.#startDeprecatedPolling();\n }\n\n /**\n * Stop polling for the token list.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n stop(): void {\n this.#stopPolling();\n }\n\n /**\n * This stops any active polling.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n override destroy(): void {\n super.destroy();\n this.#stopPolling();\n\n // Cancel any pending debounced persistence operations\n if (this.#persistDebounceTimer) {\n clearTimeout(this.#persistDebounceTimer);\n this.#persistDebounceTimer = undefined;\n }\n this.#changedChainsToPersist.clear();\n }\n\n /**\n * This stops any active polling intervals.\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n #stopPolling(): void {\n if (this.#intervalId) {\n clearInterval(this.#intervalId);\n }\n }\n\n /**\n * Starts a new polling interval for a given chainId (this should be deprecated in favor of _executePoll)\n *\n * @deprecated This method is deprecated and will be removed in the future.\n * Consider using the new polling approach instead\n */\n async #startDeprecatedPolling(): Promise<void> {\n // renaming this to avoid collision with base class\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n this.#intervalId = setInterval(async () => {\n await safelyExecute(() => this.fetchTokenList(this.#chainId));\n }, this.#intervalDelay);\n }\n\n /**\n * This starts a new polling loop for any given chain. Under the hood it is deduping polls\n *\n * @param input - The input for the poll.\n * @param input.chainId - The chainId of the chain to trigger the fetch.\n * @returns A promise that resolves when this operation completes.\n */\n async _executePoll({ chainId }: TokenListPollingInput): Promise<void> {\n await this.#waitForInitialization();\n return this.fetchTokenList(chainId);\n }\n\n /**\n * Fetching token list from the Token Service API. This will fetch tokens across chains.\n * State changes are automatically persisted via the stateChange subscription.\n *\n * @param chainId - The chainId of the current chain triggering the fetch.\n */\n async fetchTokenList(chainId: Hex): Promise<void> {\n if (this.isCacheValid(chainId)) {\n return;\n }\n\n // Fetch fresh token list from the API\n const tokensFromAPI = await safelyExecute(\n () =>\n fetchTokenListByChainId(\n chainId,\n this.#abortController.signal,\n ) as Promise<TokenListToken[]>,\n );\n\n // Have response - process and update list\n if (tokensFromAPI) {\n // Format tokens from API (HTTP) and update tokenList\n const tokenList: TokenListMap = {};\n for (const token of tokensFromAPI) {\n tokenList[token.address] = {\n ...token,\n aggregators: formatAggregatorNames(token.aggregators),\n iconUrl: formatIconUrlWithProxy({\n chainId,\n tokenAddress: token.address,\n }),\n };\n }\n\n // Update state - persistence happens automatically via subscription\n const newDataCache: DataCache = {\n data: tokenList,\n timestamp: Date.now(),\n };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n return;\n }\n\n // No response - fallback to previous state, or initialise empty.\n // Only initialize with a new timestamp if there's no existing cache.\n // If there's existing cache, keep it as-is without updating the timestamp\n // to avoid making stale data appear \"fresh\" and preventing retry attempts.\n if (!tokensFromAPI) {\n const existingCache = this.state.tokensChainsCache[chainId];\n if (!existingCache) {\n // No existing cache - initialize empty (persistence happens automatically)\n const newDataCache: DataCache = { data: {}, timestamp: Date.now() };\n this.update((state) => {\n state.tokensChainsCache[chainId] = newDataCache;\n });\n }\n // If there's existing cache, keep it as-is (don't update timestamp or persist)\n }\n }\n\n isCacheValid(chainId: Hex): boolean {\n const { tokensChainsCache }: TokenListState = this.state;\n const timestamp: number | undefined = tokensChainsCache[chainId]?.timestamp;\n const now = Date.now();\n return (\n timestamp !== undefined && now - timestamp < this.#cacheRefreshThreshold\n );\n }\n}\n\nexport default TokenListController;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/assets-controllers",
|
|
3
|
-
"version": "108.3.0-preview-
|
|
3
|
+
"version": "108.3.0-preview-1e1b476aa",
|
|
4
4
|
"description": "Controllers which manage interactions involving ERC-20, ERC-721, and ERC-1155 tokens (including NFTs)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|