@metamask/assets-controller 4.0.0 → 5.0.1
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 +38 -1
- package/dist/AssetsController.cjs +90 -31
- package/dist/AssetsController.cjs.map +1 -1
- package/dist/AssetsController.d.cts +5 -5
- package/dist/AssetsController.d.cts.map +1 -1
- package/dist/AssetsController.d.mts +5 -5
- package/dist/AssetsController.d.mts.map +1 -1
- package/dist/AssetsController.mjs +90 -31
- package/dist/AssetsController.mjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.cjs +1 -1
- package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.cts +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.mts +1 -1
- package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
- package/dist/data-sources/AccountsApiDataSource.mjs +1 -1
- package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.cjs +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.d.cts +2 -2
- package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.d.mts +2 -2
- package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.mjs +1 -1
- package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
- package/dist/data-sources/PriceDataSource.cjs +63 -38
- package/dist/data-sources/PriceDataSource.cjs.map +1 -1
- package/dist/data-sources/PriceDataSource.d.cts +1 -1
- package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
- package/dist/data-sources/PriceDataSource.d.mts +1 -1
- package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
- package/dist/data-sources/PriceDataSource.mjs +63 -38
- package/dist/data-sources/PriceDataSource.mjs.map +1 -1
- package/dist/data-sources/RpcDataSource.cjs +28 -61
- package/dist/data-sources/RpcDataSource.cjs.map +1 -1
- package/dist/data-sources/RpcDataSource.d.cts +3 -4
- package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
- package/dist/data-sources/RpcDataSource.d.mts +3 -4
- package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
- package/dist/data-sources/RpcDataSource.mjs +29 -62
- package/dist/data-sources/RpcDataSource.mjs.map +1 -1
- package/dist/data-sources/SnapDataSource.cjs +1 -1
- package/dist/data-sources/SnapDataSource.cjs.map +1 -1
- package/dist/data-sources/SnapDataSource.d.cts +2 -2
- package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
- package/dist/data-sources/SnapDataSource.d.mts +2 -2
- package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
- package/dist/data-sources/SnapDataSource.mjs +1 -1
- package/dist/data-sources/SnapDataSource.mjs.map +1 -1
- package/dist/data-sources/StakedBalanceDataSource.cjs +10 -3
- package/dist/data-sources/StakedBalanceDataSource.cjs.map +1 -1
- package/dist/data-sources/StakedBalanceDataSource.d.cts +2 -2
- package/dist/data-sources/StakedBalanceDataSource.d.cts.map +1 -1
- package/dist/data-sources/StakedBalanceDataSource.d.mts +2 -2
- package/dist/data-sources/StakedBalanceDataSource.d.mts.map +1 -1
- package/dist/data-sources/StakedBalanceDataSource.mjs +10 -3
- package/dist/data-sources/StakedBalanceDataSource.mjs.map +1 -1
- package/dist/data-sources/TokenDataSource.cjs +62 -31
- package/dist/data-sources/TokenDataSource.cjs.map +1 -1
- package/dist/data-sources/TokenDataSource.d.cts.map +1 -1
- package/dist/data-sources/TokenDataSource.d.mts.map +1 -1
- package/dist/data-sources/TokenDataSource.mjs +63 -32
- package/dist/data-sources/TokenDataSource.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.cjs +67 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.cjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.cts +23 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.cts.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.mts +23 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.d.mts.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.mjs +63 -0
- package/dist/data-sources/evm-rpc-services/clients/TokensApiClient.mjs.map +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.cjs +3 -1
- package/dist/data-sources/evm-rpc-services/clients/index.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/index.d.cts +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/index.d.mts +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/clients/index.mjs +1 -0
- package/dist/data-sources/evm-rpc-services/clients/index.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/index.cjs +2 -1
- package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/index.d.cts +1 -1
- package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/index.d.mts +1 -1
- package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/index.mjs +1 -1
- package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +47 -50
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +12 -9
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +12 -9
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +47 -50
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/index.d.cts +1 -1
- package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/index.d.mts +1 -1
- package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -1
- package/dist/utils/formatStateForTransactionPay.cjs.map +1 -1
- package/dist/utils/formatStateForTransactionPay.d.cts +1 -1
- package/dist/utils/formatStateForTransactionPay.d.cts.map +1 -1
- package/dist/utils/formatStateForTransactionPay.d.mts +1 -1
- package/dist/utils/formatStateForTransactionPay.d.mts.map +1 -1
- package/dist/utils/formatStateForTransactionPay.mjs.map +1 -1
- package/package.json +21 -20
|
@@ -9,16 +9,19 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _PriceDataSource_instances, _PriceDataSource_getSelectedCurrency, _PriceDataSource_pollInterval, _PriceDataSource_apiClient, _PriceDataSource_activeSubscriptions, _PriceDataSource_fetchSpotPrices, _PriceDataSource_getAssetIdsFromBalanceState;
|
|
12
|
+
var _PriceDataSource_instances, _PriceDataSource_getSelectedCurrency, _PriceDataSource_pollInterval, _PriceDataSource_apiClient, _PriceDataSource_activeSubscriptions, _PriceDataSource_fetchSpotPricesBatch, _PriceDataSource_fetchSpotPrices, _PriceDataSource_getAssetIdsFromBalanceState;
|
|
13
13
|
import { ApiPlatformClient } from "@metamask/core-backend";
|
|
14
14
|
import { parseCaipAssetType } from "@metamask/utils";
|
|
15
15
|
import { projectLogger, createModuleLogger } from "../logger.mjs";
|
|
16
16
|
import { forDataTypes } from "../types.mjs";
|
|
17
|
+
import { reduceInBatchesSerially } from "./evm-rpc-services/index.mjs";
|
|
17
18
|
// ============================================================================
|
|
18
19
|
// CONSTANTS
|
|
19
20
|
// ============================================================================
|
|
20
21
|
const CONTROLLER_NAME = 'PriceDataSource';
|
|
21
22
|
const DEFAULT_POLL_INTERVAL = 60000; // 1 minute for price updates
|
|
23
|
+
/** Maximum number of asset IDs per Price API request. */
|
|
24
|
+
const PRICE_API_BATCH_SIZE = 50;
|
|
22
25
|
const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
|
|
23
26
|
// ============================================================================
|
|
24
27
|
// HELPER FUNCTIONS
|
|
@@ -28,8 +31,13 @@ const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
|
|
|
28
31
|
* These are internal resource tracking values without market prices.
|
|
29
32
|
*/
|
|
30
33
|
const NON_PRICEABLE_ASSET_PATTERNS = [
|
|
31
|
-
//
|
|
32
|
-
|
|
34
|
+
// Synthetic slip44 staking-position assets: the Price API only knows about
|
|
35
|
+
// pure numeric coin-type references (e.g. slip44:195). Any suffix after the
|
|
36
|
+
// number (e.g. slip44:195-ready-for-withdrawal, slip44:195-in-lock-period,
|
|
37
|
+
// slip44:195-staking-rewards, slip44:195-staked-for-…) is a MetaMask-internal
|
|
38
|
+
// synthetic asset that has no market price.
|
|
39
|
+
/\/slip44:\d+-/u,
|
|
40
|
+
// Tron non-price resource assets (bandwidth, energy)
|
|
33
41
|
/\/slip44:bandwidth$/u,
|
|
34
42
|
/\/slip44:energy$/u,
|
|
35
43
|
/\/slip44:maximum-bandwidth$/u,
|
|
@@ -54,7 +62,6 @@ function isPriceableAsset(assetId) {
|
|
|
54
62
|
function isValidMarketData(data) {
|
|
55
63
|
return (typeof data === 'object' &&
|
|
56
64
|
data !== null &&
|
|
57
|
-
'price' in data &&
|
|
58
65
|
typeof data.price === 'number');
|
|
59
66
|
}
|
|
60
67
|
// ============================================================================
|
|
@@ -258,53 +265,71 @@ export class PriceDataSource {
|
|
|
258
265
|
__classPrivateFieldGet(this, _PriceDataSource_activeSubscriptions, "f").clear();
|
|
259
266
|
}
|
|
260
267
|
}
|
|
261
|
-
_PriceDataSource_getSelectedCurrency = new WeakMap(), _PriceDataSource_pollInterval = new WeakMap(), _PriceDataSource_apiClient = new WeakMap(), _PriceDataSource_activeSubscriptions = new WeakMap(), _PriceDataSource_instances = new WeakSet(),
|
|
268
|
+
_PriceDataSource_getSelectedCurrency = new WeakMap(), _PriceDataSource_pollInterval = new WeakMap(), _PriceDataSource_apiClient = new WeakMap(), _PriceDataSource_activeSubscriptions = new WeakMap(), _PriceDataSource_instances = new WeakSet(), _PriceDataSource_fetchSpotPricesBatch =
|
|
262
269
|
// ============================================================================
|
|
263
270
|
// HELPERS
|
|
264
271
|
// ============================================================================
|
|
265
272
|
/**
|
|
266
|
-
* Fetch spot prices
|
|
273
|
+
* Fetch spot prices for a single batch of asset IDs (must be ≤ PRICE_API_BATCH_SIZE).
|
|
267
274
|
*
|
|
268
|
-
* @param assetIds - Array of CAIP-19 asset IDs
|
|
269
|
-
* @
|
|
275
|
+
* @param assetIds - Array of CAIP-19 asset IDs (already within batch size limit).
|
|
276
|
+
* @param selectedCurrency - The user's selected display currency.
|
|
277
|
+
* @returns Raw spot-prices responses for the selected currency and USD.
|
|
270
278
|
*/
|
|
271
|
-
async function
|
|
272
|
-
const selectedCurrency = __classPrivateFieldGet(this, _PriceDataSource_getSelectedCurrency, "f").call(this);
|
|
273
|
-
let selectedCurrencyPrices;
|
|
274
|
-
let usdPrices;
|
|
279
|
+
async function _PriceDataSource_fetchSpotPricesBatch(assetIds, selectedCurrency) {
|
|
275
280
|
if (selectedCurrency === 'usd') {
|
|
276
|
-
selectedCurrencyPrices = await __classPrivateFieldGet(this, _PriceDataSource_apiClient, "f").prices.fetchV3SpotPrices(assetIds, {
|
|
281
|
+
const selectedCurrencyPrices = await __classPrivateFieldGet(this, _PriceDataSource_apiClient, "f").prices.fetchV3SpotPrices(assetIds, {
|
|
277
282
|
currency: selectedCurrency,
|
|
278
283
|
includeMarketData: true,
|
|
279
284
|
});
|
|
280
|
-
usdPrices
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
[selectedCurrencyPrices, usdPrices] = await Promise.all([
|
|
284
|
-
__classPrivateFieldGet(this, _PriceDataSource_apiClient, "f").prices.fetchV3SpotPrices(assetIds, {
|
|
285
|
-
currency: selectedCurrency,
|
|
286
|
-
includeMarketData: true,
|
|
287
|
-
}),
|
|
288
|
-
__classPrivateFieldGet(this, _PriceDataSource_apiClient, "f").prices.fetchV3SpotPrices(assetIds, {
|
|
289
|
-
currency: 'usd',
|
|
290
|
-
includeMarketData: true,
|
|
291
|
-
}),
|
|
292
|
-
]);
|
|
285
|
+
return { selectedCurrencyPrices, usdPrices: selectedCurrencyPrices };
|
|
293
286
|
}
|
|
287
|
+
const [selectedCurrencyPrices, usdPrices] = await Promise.all([
|
|
288
|
+
__classPrivateFieldGet(this, _PriceDataSource_apiClient, "f").prices.fetchV3SpotPrices(assetIds, {
|
|
289
|
+
currency: selectedCurrency,
|
|
290
|
+
includeMarketData: true,
|
|
291
|
+
}),
|
|
292
|
+
__classPrivateFieldGet(this, _PriceDataSource_apiClient, "f").prices.fetchV3SpotPrices(assetIds, {
|
|
293
|
+
currency: 'usd',
|
|
294
|
+
includeMarketData: true,
|
|
295
|
+
}),
|
|
296
|
+
]);
|
|
297
|
+
return { selectedCurrencyPrices, usdPrices };
|
|
298
|
+
}, _PriceDataSource_fetchSpotPrices =
|
|
299
|
+
/**
|
|
300
|
+
* Fetch spot prices for all provided asset IDs, splitting into batches of
|
|
301
|
+
* PRICE_API_BATCH_SIZE to respect API limits.
|
|
302
|
+
*
|
|
303
|
+
* @param assetIds - Array of CAIP-19 asset IDs
|
|
304
|
+
* @returns Spot prices response
|
|
305
|
+
*/
|
|
306
|
+
async function _PriceDataSource_fetchSpotPrices(assetIds) {
|
|
307
|
+
const selectedCurrency = __classPrivateFieldGet(this, _PriceDataSource_getSelectedCurrency, "f").call(this);
|
|
308
|
+
const batchResults = await reduceInBatchesSerially({
|
|
309
|
+
values: assetIds,
|
|
310
|
+
batchSize: PRICE_API_BATCH_SIZE,
|
|
311
|
+
eachBatch: async (workingResult, batch) => {
|
|
312
|
+
const result = await __classPrivateFieldGet(this, _PriceDataSource_instances, "m", _PriceDataSource_fetchSpotPricesBatch).call(this, batch, selectedCurrency);
|
|
313
|
+
return [...workingResult, result];
|
|
314
|
+
},
|
|
315
|
+
initialResult: [],
|
|
316
|
+
});
|
|
294
317
|
const prices = {};
|
|
295
|
-
for (const
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
318
|
+
for (const { selectedCurrencyPrices, usdPrices } of batchResults) {
|
|
319
|
+
for (const [assetId, marketData] of Object.entries(selectedCurrencyPrices)) {
|
|
320
|
+
const usdMarketData = usdPrices[assetId];
|
|
321
|
+
if (!isValidMarketData(marketData) ||
|
|
322
|
+
!isValidMarketData(usdMarketData)) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const caipAssetId = assetId;
|
|
326
|
+
prices[caipAssetId] = {
|
|
327
|
+
...marketData,
|
|
328
|
+
assetPriceType: 'fungible',
|
|
329
|
+
usdPrice: usdMarketData.price,
|
|
330
|
+
lastUpdated: Date.now(),
|
|
331
|
+
};
|
|
300
332
|
}
|
|
301
|
-
const caipAssetId = assetId;
|
|
302
|
-
prices[caipAssetId] = {
|
|
303
|
-
...marketData,
|
|
304
|
-
assetPriceType: 'fungible',
|
|
305
|
-
usdPrice: usdMarketData.price,
|
|
306
|
-
lastUpdated: Date.now(),
|
|
307
|
-
};
|
|
308
333
|
}
|
|
309
334
|
return prices;
|
|
310
335
|
}, _PriceDataSource_getAssetIdsFromBalanceState = function _PriceDataSource_getAssetIdsFromBalanceState(request, getAssetsState) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PriceDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/PriceDataSource.ts"],"names":[],"mappings":";;;;;;;;;;;;AAIA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAC3D,OAAO,EAAE,kBAAkB,EAAE,wBAAwB;AAGrD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,sBAAkB;AAC9D,OAAO,EAAE,YAAY,EAAE,qBAAiB;AAUxC,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAC1C,MAAM,qBAAqB,GAAG,KAAM,CAAC,CAAC,6BAA6B;AAEnE,MAAM,GAAG,GAAG,kBAAkB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;AAmB/D,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,4BAA4B,GAAG;IACnC,2DAA2D;IAC3D,2BAA2B;IAC3B,sBAAsB;IACtB,mBAAmB;IACnB,8BAA8B;IAC9B,2BAA2B;CAC5B,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,OAAsB;IAC9C,OAAO,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;AAChF,CAAC;AAQD;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,IAAa;IACtC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACb,OAAO,IAAI,IAAI;QACf,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAC/B,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;;;;;;;;GASG;AACH,MAAM,OAAO,eAAe;IAG1B,OAAO;QACL,OAAO,eAAe,CAAC,cAAc,CAAC;IACxC,CAAC;IAoBD,YAAY,OAA+B;;QAlBlC,uDAA8C;QAE9C,gDAAsB;QAE/B,6CAA6C;QACpC,6CAA8B;QAEvC,iCAAiC;QACxB,+CAQL,IAAI,GAAG,EAAE,EAAC;QAGZ,uBAAA,IAAI,wCAAwB,OAAO,CAAC,mBAAmB,MAAA,CAAC;QACxD,uBAAA,IAAI,iCAAiB,OAAO,CAAC,YAAY,IAAI,qBAAqB,MAAA,CAAC;QACnE,uBAAA,IAAI,8BAAc,OAAO,CAAC,cAAc,MAAA,CAAC;IAC3C,CAAC;IAED,+EAA+E;IAC/E,aAAa;IACb,+EAA+E;IAE/E;;;;;;;;;;;;;;OAcG;IACH,IAAI,gBAAgB;QAClB,OAAO,YAAY,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACjD,gCAAgC;YAChC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;YAElC,kEAAkE;YAClE,mEAAmE;YACnE,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,EAAE,CAAC;gBACtE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;YAC1C,KAAK,MAAM,qBAAqB,IAAI,MAAM,CAAC,MAAM,CAC/C,QAAQ,CAAC,cAAc,IAAI,EAAE,CAC9B,EAAE,CAAC;gBACF,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;oBAC5C,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;YAED,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,oBAAoB,IAAI,EAAE,EAAE,CAAC;gBACzD,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YAED,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YAED,kCAAkC;YAClC,MAAM,iBAAiB,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;YAEjE,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,uBAAA,IAAI,oEAAiB,MAArB,IAAI,EAAkB,iBAAiB,CAAC,CAAC;gBAClE,QAAQ,CAAC,WAAW,GAAG;oBACrB,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;oBAC/B,GAAG,UAAU;iBACd,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,uCAAuC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC1D,CAAC;YAED,0DAA0D;YAC1D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAqID,+EAA+E;IAC/E,QAAQ;IACR,+EAA+E;IAE/E;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CACT,OAAoB,EACpB,cAAoD;QAEpD,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,iEAAiE;QACjE,MAAM,WAAW,GAAG,uBAAA,IAAI,gFAA6B,MAAjC,IAAI,EACtB,OAAO,EACP,cAAc,CACf,CAAC;QAEF,0EAA0E;QAC1E,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAEtD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,uBAAA,IAAI,oEAAiB,MAArB,IAAI,EAAkB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;YAE9D,QAAQ,CAAC,WAAW,GAAG;gBACrB,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;gBAC/B,GAAG,UAAU;aACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,wBAAwB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,+EAA+E;IAC/E,YAAY;IACZ,+EAA+E;IAE/E;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CAAC,mBAAwC;QACtD,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CAAC;QAElE,uDAAuD;QACvD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,uBAAA,IAAI,4CAAqB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC/D,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC;gBAC3B,OAAO;YACT,CAAC;QACH,CAAC;QAED,iCAAiC;QACjC,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QAEvC,MAAM,YAAY,GAAG,OAAO,CAAC,cAAc,IAAI,uBAAA,IAAI,qCAAc,CAAC;QAElE,+EAA+E;QAC/E,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;YACvC,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,uBAAA,IAAI,4CAAqB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBACnE,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,OAAO;gBACT,CAAC;gBAED,oFAAoF;gBACpF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,KAAK,CACpC,YAAY,CAAC,OAAO,EACpB,YAAY,CAAC,cAAc,CAC5B,CAAC;gBAEF,+BAA+B;gBAC/B,IACE,aAAa,CAAC,WAAW;oBACzB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,MAAM,GAAG,CAAC,EACjD,CAAC;oBACD,MAAM,YAAY,CAAC,cAAc,CAAC;wBAChC,GAAG,aAAa;wBAChB,UAAU,EAAE,OAAO;qBACpB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,0BAA0B,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC,CAAC;QAEF,iBAAiB;QACjB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC,EAAE,YAAY,CAAC,CAAC;QAEjB,6EAA6E;QAC7E,uBAAA,IAAI,4CAAqB,CAAC,GAAG,CAAC,cAAc,EAAE;YAC5C,OAAO,EAAE,GAAG,EAAE;gBACZ,aAAa,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;YACD,OAAO;YACP,cAAc,EAAE,mBAAmB,CAAC,cAAc;YAClD,cAAc,EAAE,mBAAmB,CAAC,cAAc;SACnD,CAAC,CAAC;QAEH,gBAAgB;QAChB,MAAM,MAAM,EAAE,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,cAAsB;QACtC,MAAM,YAAY,GAAG,uBAAA,IAAI,4CAAqB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACnE,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,CAAC,OAAO,EAAE,CAAC;YACvB,uBAAA,IAAI,4CAAqB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,OAAO;QACL,KAAK,MAAM,YAAY,IAAI,uBAAA,IAAI,4CAAqB,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9D,YAAY,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;QACD,uBAAA,IAAI,4CAAqB,CAAC,KAAK,EAAE,CAAC;IACpC,CAAC;;;AAhRD,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;;;;GAKG;AACH,KAAK,2CACH,QAAkB;IAElB,MAAM,gBAAgB,GAAG,uBAAA,IAAI,4CAAqB,MAAzB,IAAI,CAAuB,CAAC;IAErD,IAAI,sBAA4C,CAAC;IACjD,IAAI,SAA+B,CAAC;IACpC,IAAI,gBAAgB,KAAK,KAAK,EAAE,CAAC;QAC/B,sBAAsB,GAAG,MAAM,uBAAA,IAAI,kCAAW,CAAC,MAAM,CAAC,iBAAiB,CACrE,QAAQ,EACR;YACE,QAAQ,EAAE,gBAAgB;YAC1B,iBAAiB,EAAE,IAAI;SACxB,CACF,CAAC;QACF,SAAS,GAAG,sBAAsB,CAAC;IACrC,CAAC;SAAM,CAAC;QACN,CAAC,sBAAsB,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtD,uBAAA,IAAI,kCAAW,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE;gBACjD,QAAQ,EAAE,gBAAgB;gBAC1B,iBAAiB,EAAE,IAAI;aACxB,CAAC;YACF,uBAAA,IAAI,kCAAW,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE;gBACjD,QAAQ,EAAE,KAAK;gBACf,iBAAiB,EAAE,IAAI;aACxB,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAA8C,EAAE,CAAC;IAE7D,KAAK,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAChD,sBAAsB,CACvB,EAAE,CAAC;QACF,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzC,2GAA2G;QAC3G,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC;YACxE,SAAS;QACX,CAAC;QAED,MAAM,WAAW,GAAG,OAAwB,CAAC;QAC7C,MAAM,CAAC,WAAW,CAAC,GAAG;YACpB,GAAG,UAAU;YACb,cAAc,EAAE,UAAU;YAC1B,QAAQ,EAAE,aAAa,CAAC,KAAK;YAC7B,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;SACxB,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,uGAWC,OAAoB,EACpB,cAAoD;IAEpD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiB,CAAC;QAE1C,MAAM,UAAU,GAAG,OAAO,CAAC,2BAA2B,CAAC,GAAG,CACxD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CACpB,CAAC;QACF,MAAM,aAAa,GACjB,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1D,MAAM,WAAW,GACf,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEtE,IAAI,KAAK,EAAE,aAAa,EAAE,CAAC;YACzB,KAAK,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,IAAI,MAAM,CAAC,OAAO,CACvD,KAAK,CAAC,aAAa,CACpB,EAAE,CAAC;gBACF,iCAAiC;gBACjC,IAAI,aAAa,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBACnD,SAAS;gBACX,CAAC;gBAED,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAC/B,eAA0C,CAC3C,EAAE,CAAC;oBACF,6EAA6E;oBAC7E,IAAI,WAAW,EAAE,CAAC;wBAChB,IAAI,CAAC;4BACH,MAAM,EAAE,OAAO,EAAE,GAAG,kBAAkB,CACpC,OAAwB,CACzB,CAAC;4BACF,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gCAC9B,SAAS;4BACX,CAAC;wBACH,CAAC;wBAAC,OAAO,KAAK,EAAE,CAAC;4BACf,GAAG,CAAC,8CAA8C,EAAE;gCAClD,OAAO;gCACP,KAAK;6BACN,CAAC,CAAC;4BACH,SAAS;wBACX,CAAC;oBACH,CAAC;oBACD,QAAQ,CAAC,GAAG,CAAC,OAAwB,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AApOe,8BAAc,GAAG,eAAe,AAAlB,CAAmB","sourcesContent":["import type {\n SupportedCurrency,\n V3SpotPricesResponse,\n} from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport { parseCaipAssetType } from '@metamask/utils';\n\nimport type { SubscriptionRequest } from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type {\n Caip19AssetId,\n DataRequest,\n DataResponse,\n FungibleAssetPrice,\n Middleware,\n AssetsControllerStateInternal,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'PriceDataSource';\nconst DEFAULT_POLL_INTERVAL = 60_000; // 1 minute for price updates\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\n/** Optional configuration for PriceDataSource. */\nexport type PriceDataSourceConfig = {\n /** Polling interval in ms (default: 60000) */\n pollInterval?: number;\n};\n\nexport type PriceDataSourceOptions = PriceDataSourceConfig & {\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Function returning the currently-active ISO 4217 currency code */\n getSelectedCurrency: () => SupportedCurrency;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Asset reference patterns that should NOT be sent to the Price API.\n * These are internal resource tracking values without market prices.\n */\nconst NON_PRICEABLE_ASSET_PATTERNS = [\n // Tron resource assets (bandwidth, energy, staking states)\n /\\/slip44:\\d+-staked-for-/u,\n /\\/slip44:bandwidth$/u,\n /\\/slip44:energy$/u,\n /\\/slip44:maximum-bandwidth$/u,\n /\\/slip44:maximum-energy$/u,\n];\n\n/**\n * Check if an asset ID represents a priceable asset.\n * Filters out internal resource tracking values that don't have market prices.\n *\n * @param assetId - The CAIP-19 asset ID to check.\n * @returns True if the asset has market price data.\n */\nfunction isPriceableAsset(assetId: Caip19AssetId): boolean {\n return !NON_PRICEABLE_ASSET_PATTERNS.some((pattern) => pattern.test(assetId));\n}\n\n/** Market data item from spot prices response (same as FungibleAssetPrice without lastUpdated) */\ntype SpotPriceMarketData = Omit<\n FungibleAssetPrice,\n 'lastUpdated' | 'assetPriceType'\n>;\n\n/**\n * Type guard to check if market data has a valid price\n *\n * @param data - The data to check.\n * @returns True if data is valid SpotPriceMarketData.\n */\nfunction isValidMarketData(data: unknown): data is SpotPriceMarketData {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'price' in data &&\n typeof data.price === 'number'\n );\n}\n\n// ============================================================================\n// PRICE DATA SOURCE\n// ============================================================================\n\n/**\n * PriceDataSource fetches asset prices from the Price API.\n *\n * This data source:\n * - Fetches prices from Price API v3 spot-prices endpoint\n * - Supports one-time fetch and subscription-based polling\n * - In subscribe mode, uses getAssetsState from SubscriptionRequest to read assetsBalance and fetch prices\n *\n * Usage: Create with queryApiClient; subscribe() requires getAssetsState in the request for balance-based pricing.\n */\nexport class PriceDataSource {\n static readonly controllerName = CONTROLLER_NAME;\n\n getName(): string {\n return PriceDataSource.controllerName;\n }\n\n readonly #getSelectedCurrency: () => SupportedCurrency;\n\n readonly #pollInterval: number;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Active subscriptions by ID */\n readonly #activeSubscriptions: Map<\n string,\n {\n cleanup: () => void;\n request: DataRequest;\n onAssetsUpdate: (response: DataResponse) => void | Promise<void>;\n getAssetsState?: () => AssetsControllerStateInternal;\n }\n > = new Map();\n\n constructor(options: PriceDataSourceOptions) {\n this.#getSelectedCurrency = options.getSelectedCurrency;\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#apiClient = options.queryApiClient;\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for enriching responses with price data.\n *\n * This middleware:\n * 1. Extracts the response from context\n * 2. Fetches prices for detected assets (assets without metadata)\n * 3. Enriches the response with fetched prices\n * 4. Calls next() at the end to continue the middleware chain\n *\n * Note: This middleware ONLY fetches prices for detected assets.\n * For fetching prices for all assets, use the subscription mechanism\n * which polls prices for all assets in the balance state.\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['price'], async (ctx, next) => {\n // Extract response from context\n const { response, request } = ctx;\n\n // Only fetch prices for detected assets (assets without metadata)\n // The subscription handles fetching prices for all existing assets\n if (!response.detectedAssets && !request.assetsForPriceUpdate?.length) {\n return next(ctx);\n }\n\n const assetIds = new Set<Caip19AssetId>();\n for (const detectedAccountAssets of Object.values(\n response.detectedAssets ?? {},\n )) {\n for (const assetId of detectedAccountAssets) {\n assetIds.add(assetId);\n }\n }\n\n for (const assetId of request.assetsForPriceUpdate ?? []) {\n assetIds.add(assetId);\n }\n\n if (assetIds.size === 0) {\n return next(ctx);\n }\n\n // Filter to only priceable assets\n const priceableAssetIds = [...assetIds].filter(isPriceableAsset);\n\n if (priceableAssetIds.length === 0) {\n return next(ctx);\n }\n\n try {\n const spotPrices = await this.#fetchSpotPrices(priceableAssetIds);\n response.assetsPrice = {\n ...(response.assetsPrice ?? {}),\n ...spotPrices,\n };\n } catch (error) {\n log('Failed to fetch prices via middleware', { error });\n }\n\n // Call next() at the end to continue the middleware chain\n return next(ctx);\n });\n }\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Fetch spot prices with caching and deduplication via query service.\n *\n * @param assetIds - Array of CAIP-19 asset IDs\n * @returns Spot prices response\n */\n async #fetchSpotPrices(\n assetIds: string[],\n ): Promise<Record<Caip19AssetId, FungibleAssetPrice>> {\n const selectedCurrency = this.#getSelectedCurrency();\n\n let selectedCurrencyPrices: V3SpotPricesResponse;\n let usdPrices: V3SpotPricesResponse;\n if (selectedCurrency === 'usd') {\n selectedCurrencyPrices = await this.#apiClient.prices.fetchV3SpotPrices(\n assetIds,\n {\n currency: selectedCurrency,\n includeMarketData: true,\n },\n );\n usdPrices = selectedCurrencyPrices;\n } else {\n [selectedCurrencyPrices, usdPrices] = await Promise.all([\n this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: selectedCurrency,\n includeMarketData: true,\n }),\n this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: 'usd',\n includeMarketData: true,\n }),\n ]);\n }\n\n const prices: Record<Caip19AssetId, FungibleAssetPrice> = {};\n\n for (const [assetId, marketData] of Object.entries(\n selectedCurrencyPrices,\n )) {\n const usdMarketData = usdPrices[assetId];\n\n // Skip assets with invalid market data (API doesn't have price for this asset is selected currency or USD)\n if (!isValidMarketData(marketData) || !isValidMarketData(usdMarketData)) {\n continue;\n }\n\n const caipAssetId = assetId as Caip19AssetId;\n prices[caipAssetId] = {\n ...marketData,\n assetPriceType: 'fungible',\n usdPrice: usdMarketData.price,\n lastUpdated: Date.now(),\n };\n }\n\n return prices;\n }\n\n /**\n * Get unique asset IDs from the assetsBalance state.\n * Filters by accounts and chains from the request.\n *\n * @param request - Data request with accounts and chainIds filters.\n * @param getAssetsState - State access; when omitted, returns [].\n * @returns Array of CAIP-19 asset IDs from balance state.\n */\n #getAssetIdsFromBalanceState(\n request: DataRequest,\n getAssetsState?: () => AssetsControllerStateInternal,\n ): Caip19AssetId[] {\n if (!getAssetsState) {\n return [];\n }\n try {\n const state = getAssetsState();\n const assetIds = new Set<Caip19AssetId>();\n\n const accountIds = request.accountsWithSupportedChains.map(\n (a) => a.account.id,\n );\n const accountFilter =\n accountIds.length > 0 ? new Set(accountIds) : undefined;\n const chainFilter =\n request.chainIds.length > 0 ? new Set(request.chainIds) : undefined;\n\n if (state?.assetsBalance) {\n for (const [accountId, accountBalances] of Object.entries(\n state.assetsBalance,\n )) {\n // Filter by account if specified\n if (accountFilter && !accountFilter.has(accountId)) {\n continue;\n }\n\n for (const assetId of Object.keys(\n accountBalances as Record<string, unknown>,\n )) {\n // Filter by chain if specified; skip malformed asset IDs for this entry only\n if (chainFilter) {\n try {\n const { chainId } = parseCaipAssetType(\n assetId as Caip19AssetId,\n );\n if (!chainFilter.has(chainId)) {\n continue;\n }\n } catch (error) {\n log('Skipping malformed asset ID in balance state', {\n assetId,\n error,\n });\n continue;\n }\n }\n assetIds.add(assetId as Caip19AssetId);\n }\n }\n }\n\n return [...assetIds];\n } catch (error) {\n log('Failed to get asset IDs from balance state', { error });\n return [];\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n /**\n * Fetch prices for assets held by the accounts and chains in the request.\n * When getAssetsState is provided, gets asset IDs from balance state; otherwise returns empty.\n *\n * @param request - The data request specifying accounts and chains.\n * @param getAssetsState - Optional state access (e.g. from SubscriptionRequest).\n * @returns DataResponse containing asset prices.\n */\n async fetch(\n request: DataRequest,\n getAssetsState?: () => AssetsControllerStateInternal,\n ): Promise<DataResponse> {\n const response: DataResponse = {};\n\n // Get asset IDs from balance state when state access is provided\n const rawAssetIds = this.#getAssetIdsFromBalanceState(\n request,\n getAssetsState,\n );\n\n // Filter out non-priceable assets (e.g., Tron bandwidth/energy resources)\n const assetIds = rawAssetIds.filter(isPriceableAsset);\n\n if (assetIds.length === 0) {\n return response;\n }\n\n try {\n const spotPrices = await this.#fetchSpotPrices([...assetIds]);\n\n response.assetsPrice = {\n ...(response.assetsPrice ?? {}),\n ...spotPrices,\n };\n } catch (error) {\n log('Failed to fetch prices', { error });\n }\n\n return response;\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n /**\n * Subscribe to price updates.\n * Sets up polling that fetches prices for all assets in assetsBalance state.\n *\n * @param subscriptionRequest - The subscription request configuration.\n */\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Handle subscription update - just update the request\n if (isUpdate) {\n const existing = this.#activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.request = request;\n return;\n }\n }\n\n // Clean up existing subscription\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function - fetches prices using getAssetsState from subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (!subscription) {\n return;\n }\n\n // Fetch prices for all assets in balance state (uses subscription's getAssetsState)\n const fetchResponse = await this.fetch(\n subscription.request,\n subscription.getAssetsState,\n );\n\n // Only report if we got prices\n if (\n fetchResponse.assetsPrice &&\n Object.keys(fetchResponse.assetsPrice).length > 0\n ) {\n await subscription.onAssetsUpdate({\n ...fetchResponse,\n updateMode: 'merge',\n });\n }\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription (getAssetsState from request for balance-based pricing)\n this.#activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n getAssetsState: subscriptionRequest.getAssetsState,\n });\n\n // Initial fetch\n await pollFn();\n }\n\n /**\n * Unsubscribe from price updates.\n *\n * @param subscriptionId - The ID of the subscription to cancel.\n */\n async unsubscribe(subscriptionId: string): Promise<void> {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (subscription) {\n subscription.cleanup();\n this.#activeSubscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Destroy the data source and clean up all subscriptions.\n */\n destroy(): void {\n for (const subscription of this.#activeSubscriptions.values()) {\n subscription.cleanup();\n }\n this.#activeSubscriptions.clear();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"PriceDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/PriceDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n SupportedCurrency,\n V3SpotPricesResponse,\n} from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport { parseCaipAssetType } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type {\n Caip19AssetId,\n DataRequest,\n DataResponse,\n FungibleAssetPrice,\n Middleware,\n AssetsControllerStateInternal,\n} from '../types';\nimport type { SubscriptionRequest } from './AbstractDataSource';\nimport { reduceInBatchesSerially } from './evm-rpc-services';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'PriceDataSource';\nconst DEFAULT_POLL_INTERVAL = 60_000; // 1 minute for price updates\n\n/** Maximum number of asset IDs per Price API request. */\nconst PRICE_API_BATCH_SIZE = 50;\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\n/** Optional configuration for PriceDataSource. */\nexport type PriceDataSourceConfig = {\n /** Polling interval in ms (default: 60000) */\n pollInterval?: number;\n};\n\nexport type PriceDataSourceOptions = PriceDataSourceConfig & {\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n /** Function returning the currently-active ISO 4217 currency code */\n getSelectedCurrency: () => SupportedCurrency;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Asset reference patterns that should NOT be sent to the Price API.\n * These are internal resource tracking values without market prices.\n */\nconst NON_PRICEABLE_ASSET_PATTERNS = [\n // Synthetic slip44 staking-position assets: the Price API only knows about\n // pure numeric coin-type references (e.g. slip44:195). Any suffix after the\n // number (e.g. slip44:195-ready-for-withdrawal, slip44:195-in-lock-period,\n // slip44:195-staking-rewards, slip44:195-staked-for-…) is a MetaMask-internal\n // synthetic asset that has no market price.\n /\\/slip44:\\d+-/u,\n // Tron non-price resource assets (bandwidth, energy)\n /\\/slip44:bandwidth$/u,\n /\\/slip44:energy$/u,\n /\\/slip44:maximum-bandwidth$/u,\n /\\/slip44:maximum-energy$/u,\n];\n\n/**\n * Check if an asset ID represents a priceable asset.\n * Filters out internal resource tracking values that don't have market prices.\n *\n * @param assetId - The CAIP-19 asset ID to check.\n * @returns True if the asset has market price data.\n */\nfunction isPriceableAsset(assetId: Caip19AssetId): boolean {\n return !NON_PRICEABLE_ASSET_PATTERNS.some((pattern) => pattern.test(assetId));\n}\n\n/** Market data item from spot prices response (same as FungibleAssetPrice without lastUpdated) */\ntype SpotPriceMarketData = Omit<\n FungibleAssetPrice,\n 'lastUpdated' | 'assetPriceType'\n>;\n\n/**\n * Type guard to check if market data has a valid price\n *\n * @param data - The data to check.\n * @returns True if data is valid SpotPriceMarketData.\n */\nfunction isValidMarketData(data: unknown): data is SpotPriceMarketData {\n return (\n typeof data === 'object' &&\n data !== null &&\n typeof (data as Record<string, unknown>).price === 'number'\n );\n}\n\n// ============================================================================\n// PRICE DATA SOURCE\n// ============================================================================\n\n/**\n * PriceDataSource fetches asset prices from the Price API.\n *\n * This data source:\n * - Fetches prices from Price API v3 spot-prices endpoint\n * - Supports one-time fetch and subscription-based polling\n * - In subscribe mode, uses getAssetsState from SubscriptionRequest to read assetsBalance and fetch prices\n *\n * Usage: Create with queryApiClient; subscribe() requires getAssetsState in the request for balance-based pricing.\n */\nexport class PriceDataSource {\n static readonly controllerName = CONTROLLER_NAME;\n\n getName(): string {\n return PriceDataSource.controllerName;\n }\n\n readonly #getSelectedCurrency: () => SupportedCurrency;\n\n readonly #pollInterval: number;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n /** Active subscriptions by ID */\n readonly #activeSubscriptions: Map<\n string,\n {\n cleanup: () => void;\n request: DataRequest;\n onAssetsUpdate: (response: DataResponse) => void | Promise<void>;\n getAssetsState?: () => AssetsControllerStateInternal;\n }\n > = new Map();\n\n constructor(options: PriceDataSourceOptions) {\n this.#getSelectedCurrency = options.getSelectedCurrency;\n this.#pollInterval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;\n this.#apiClient = options.queryApiClient;\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for enriching responses with price data.\n *\n * This middleware:\n * 1. Extracts the response from context\n * 2. Fetches prices for detected assets (assets without metadata)\n * 3. Enriches the response with fetched prices\n * 4. Calls next() at the end to continue the middleware chain\n *\n * Note: This middleware ONLY fetches prices for detected assets.\n * For fetching prices for all assets, use the subscription mechanism\n * which polls prices for all assets in the balance state.\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['price'], async (ctx, next) => {\n // Extract response from context\n const { response, request } = ctx;\n\n // Only fetch prices for detected assets (assets without metadata)\n // The subscription handles fetching prices for all existing assets\n if (!response.detectedAssets && !request.assetsForPriceUpdate?.length) {\n return next(ctx);\n }\n\n const assetIds = new Set<Caip19AssetId>();\n for (const detectedAccountAssets of Object.values(\n response.detectedAssets ?? {},\n )) {\n for (const assetId of detectedAccountAssets) {\n assetIds.add(assetId);\n }\n }\n\n for (const assetId of request.assetsForPriceUpdate ?? []) {\n assetIds.add(assetId);\n }\n\n if (assetIds.size === 0) {\n return next(ctx);\n }\n\n // Filter to only priceable assets\n const priceableAssetIds = [...assetIds].filter(isPriceableAsset);\n\n if (priceableAssetIds.length === 0) {\n return next(ctx);\n }\n\n try {\n const spotPrices = await this.#fetchSpotPrices(priceableAssetIds);\n response.assetsPrice = {\n ...(response.assetsPrice ?? {}),\n ...spotPrices,\n };\n } catch (error) {\n log('Failed to fetch prices via middleware', { error });\n }\n\n // Call next() at the end to continue the middleware chain\n return next(ctx);\n });\n }\n\n // ============================================================================\n // HELPERS\n // ============================================================================\n\n /**\n * Fetch spot prices for a single batch of asset IDs (must be ≤ PRICE_API_BATCH_SIZE).\n *\n * @param assetIds - Array of CAIP-19 asset IDs (already within batch size limit).\n * @param selectedCurrency - The user's selected display currency.\n * @returns Raw spot-prices responses for the selected currency and USD.\n */\n async #fetchSpotPricesBatch(\n assetIds: string[],\n selectedCurrency: SupportedCurrency,\n ): Promise<{\n selectedCurrencyPrices: V3SpotPricesResponse;\n usdPrices: V3SpotPricesResponse;\n }> {\n if (selectedCurrency === 'usd') {\n const selectedCurrencyPrices =\n await this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: selectedCurrency,\n includeMarketData: true,\n });\n return { selectedCurrencyPrices, usdPrices: selectedCurrencyPrices };\n }\n\n const [selectedCurrencyPrices, usdPrices] = await Promise.all([\n this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: selectedCurrency,\n includeMarketData: true,\n }),\n this.#apiClient.prices.fetchV3SpotPrices(assetIds, {\n currency: 'usd',\n includeMarketData: true,\n }),\n ]);\n\n return { selectedCurrencyPrices, usdPrices };\n }\n\n /**\n * Fetch spot prices for all provided asset IDs, splitting into batches of\n * PRICE_API_BATCH_SIZE to respect API limits.\n *\n * @param assetIds - Array of CAIP-19 asset IDs\n * @returns Spot prices response\n */\n async #fetchSpotPrices(\n assetIds: string[],\n ): Promise<Record<Caip19AssetId, FungibleAssetPrice>> {\n const selectedCurrency = this.#getSelectedCurrency();\n\n type BatchResult = {\n selectedCurrencyPrices: V3SpotPricesResponse;\n usdPrices: V3SpotPricesResponse;\n };\n\n const batchResults = await reduceInBatchesSerially<string, BatchResult[]>({\n values: assetIds,\n batchSize: PRICE_API_BATCH_SIZE,\n eachBatch: async (workingResult, batch) => {\n const result = await this.#fetchSpotPricesBatch(\n batch,\n selectedCurrency,\n );\n return [...(workingResult as BatchResult[]), result];\n },\n initialResult: [],\n });\n\n const prices: Record<Caip19AssetId, FungibleAssetPrice> = {};\n\n for (const { selectedCurrencyPrices, usdPrices } of batchResults) {\n for (const [assetId, marketData] of Object.entries(\n selectedCurrencyPrices,\n )) {\n const usdMarketData = usdPrices[assetId];\n\n if (\n !isValidMarketData(marketData) ||\n !isValidMarketData(usdMarketData)\n ) {\n continue;\n }\n\n const caipAssetId = assetId as Caip19AssetId;\n prices[caipAssetId] = {\n ...marketData,\n assetPriceType: 'fungible',\n usdPrice: usdMarketData.price,\n lastUpdated: Date.now(),\n };\n }\n }\n\n return prices;\n }\n\n /**\n * Get unique asset IDs from the assetsBalance state.\n * Filters by accounts and chains from the request.\n *\n * @param request - Data request with accounts and chainIds filters.\n * @param getAssetsState - State access; when omitted, returns [].\n * @returns Array of CAIP-19 asset IDs from balance state.\n */\n #getAssetIdsFromBalanceState(\n request: DataRequest,\n getAssetsState?: () => AssetsControllerStateInternal,\n ): Caip19AssetId[] {\n if (!getAssetsState) {\n return [];\n }\n try {\n const state = getAssetsState();\n const assetIds = new Set<Caip19AssetId>();\n\n const accountIds = request.accountsWithSupportedChains.map(\n (a) => a.account.id,\n );\n const accountFilter =\n accountIds.length > 0 ? new Set(accountIds) : undefined;\n const chainFilter =\n request.chainIds.length > 0 ? new Set(request.chainIds) : undefined;\n\n if (state?.assetsBalance) {\n for (const [accountId, accountBalances] of Object.entries(\n state.assetsBalance,\n )) {\n // Filter by account if specified\n if (accountFilter && !accountFilter.has(accountId)) {\n continue;\n }\n\n for (const assetId of Object.keys(\n accountBalances as Record<string, unknown>,\n )) {\n // Filter by chain if specified; skip malformed asset IDs for this entry only\n if (chainFilter) {\n try {\n const { chainId } = parseCaipAssetType(\n assetId as Caip19AssetId,\n );\n if (!chainFilter.has(chainId)) {\n continue;\n }\n } catch (error) {\n log('Skipping malformed asset ID in balance state', {\n assetId,\n error,\n });\n continue;\n }\n }\n assetIds.add(assetId as Caip19AssetId);\n }\n }\n }\n\n return [...assetIds];\n } catch (error) {\n log('Failed to get asset IDs from balance state', { error });\n return [];\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n /**\n * Fetch prices for assets held by the accounts and chains in the request.\n * When getAssetsState is provided, gets asset IDs from balance state; otherwise returns empty.\n *\n * @param request - The data request specifying accounts and chains.\n * @param getAssetsState - Optional state access (e.g. from SubscriptionRequest).\n * @returns DataResponse containing asset prices.\n */\n async fetch(\n request: DataRequest,\n getAssetsState?: () => AssetsControllerStateInternal,\n ): Promise<DataResponse> {\n const response: DataResponse = {};\n\n // Get asset IDs from balance state when state access is provided\n const rawAssetIds = this.#getAssetIdsFromBalanceState(\n request,\n getAssetsState,\n );\n\n // Filter out non-priceable assets (e.g., Tron bandwidth/energy resources)\n const assetIds = rawAssetIds.filter(isPriceableAsset);\n\n if (assetIds.length === 0) {\n return response;\n }\n\n try {\n const spotPrices = await this.#fetchSpotPrices([...assetIds]);\n\n response.assetsPrice = {\n ...(response.assetsPrice ?? {}),\n ...spotPrices,\n };\n } catch (error) {\n log('Failed to fetch prices', { error });\n }\n\n return response;\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n /**\n * Subscribe to price updates.\n * Sets up polling that fetches prices for all assets in assetsBalance state.\n *\n * @param subscriptionRequest - The subscription request configuration.\n */\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Handle subscription update - just update the request\n if (isUpdate) {\n const existing = this.#activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.request = request;\n return;\n }\n }\n\n // Clean up existing subscription\n await this.unsubscribe(subscriptionId);\n\n const pollInterval = request.updateInterval ?? this.#pollInterval;\n\n // Create poll function - fetches prices using getAssetsState from subscription\n const pollFn = async (): Promise<void> => {\n try {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (!subscription) {\n return;\n }\n\n // Fetch prices for all assets in balance state (uses subscription's getAssetsState)\n const fetchResponse = await this.fetch(\n subscription.request,\n subscription.getAssetsState,\n );\n\n // Only report if we got prices\n if (\n fetchResponse.assetsPrice &&\n Object.keys(fetchResponse.assetsPrice).length > 0\n ) {\n await subscription.onAssetsUpdate({\n ...fetchResponse,\n updateMode: 'merge',\n });\n }\n } catch (error) {\n log('Subscription poll failed', { subscriptionId, error });\n }\n };\n\n // Set up polling\n const timer = setInterval(() => {\n pollFn().catch(console.error);\n }, pollInterval);\n\n // Store subscription (getAssetsState from request for balance-based pricing)\n this.#activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n clearInterval(timer);\n },\n request,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n getAssetsState: subscriptionRequest.getAssetsState,\n });\n\n // Initial fetch\n await pollFn();\n }\n\n /**\n * Unsubscribe from price updates.\n *\n * @param subscriptionId - The ID of the subscription to cancel.\n */\n async unsubscribe(subscriptionId: string): Promise<void> {\n const subscription = this.#activeSubscriptions.get(subscriptionId);\n if (subscription) {\n subscription.cleanup();\n this.#activeSubscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Destroy the data source and clean up all subscriptions.\n */\n destroy(): void {\n for (const subscription of this.#activeSubscriptions.values()) {\n subscription.cleanup();\n }\n this.#activeSubscriptions.clear();\n }\n}\n"]}
|
|
@@ -13,18 +13,18 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
|
-
var _RpcDataSource_instances, _RpcDataSource_messenger, _RpcDataSource_onActiveChainsUpdated, _RpcDataSource_timeout, _RpcDataSource_tokenDetectionEnabled, _RpcDataSource_useExternalService, _RpcDataSource_activeChains, _RpcDataSource_chainStatuses, _RpcDataSource_providerCache, _RpcDataSource_activeSubscriptions, _RpcDataSource_unsubscribeTransactionConfirmed, _RpcDataSource_unsubscribeIncomingTransactions, _RpcDataSource_multicallClient, _RpcDataSource_balanceFetcher, _RpcDataSource_tokenDetector, _RpcDataSource_convertToHumanReadable, _RpcDataSource_collectMetadataForBalances, _RpcDataSource_handleBalanceUpdate, _RpcDataSource_handleDetectionUpdate, _RpcDataSource_subscribeToNetworkController, _RpcDataSource_subscribeToTransactionEvents, _RpcDataSource_onTransactionConfirmed, _RpcDataSource_onIncomingTransactions, _RpcDataSource_refreshBalanceForChains, _RpcDataSource_initializeFromNetworkController, _RpcDataSource_updateFromNetworkState, _RpcDataSource_getProvider, _RpcDataSource_getMulticallProvider, _RpcDataSource_clearProviderCache, _RpcDataSource_fetchDecimalsViaRpc, _RpcDataSource_buildNativeAssetId, _RpcDataSource_getExistingAssetsMetadata
|
|
16
|
+
var _RpcDataSource_instances, _RpcDataSource_messenger, _RpcDataSource_onActiveChainsUpdated, _RpcDataSource_timeout, _RpcDataSource_tokenDetectionEnabled, _RpcDataSource_useExternalService, _RpcDataSource_activeChains, _RpcDataSource_chainStatuses, _RpcDataSource_providerCache, _RpcDataSource_activeSubscriptions, _RpcDataSource_unsubscribeTransactionConfirmed, _RpcDataSource_unsubscribeIncomingTransactions, _RpcDataSource_multicallClient, _RpcDataSource_balanceFetcher, _RpcDataSource_tokenDetector, _RpcDataSource_convertToHumanReadable, _RpcDataSource_collectMetadataForBalances, _RpcDataSource_handleBalanceUpdate, _RpcDataSource_handleDetectionUpdate, _RpcDataSource_subscribeToNetworkController, _RpcDataSource_subscribeToTransactionEvents, _RpcDataSource_onTransactionConfirmed, _RpcDataSource_onIncomingTransactions, _RpcDataSource_refreshBalanceForChains, _RpcDataSource_initializeFromNetworkController, _RpcDataSource_updateFromNetworkState, _RpcDataSource_getProvider, _RpcDataSource_getMulticallProvider, _RpcDataSource_clearProviderCache, _RpcDataSource_fetchDecimalsViaRpc, _RpcDataSource_buildNativeAssetId, _RpcDataSource_getExistingAssetsMetadata;
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.createRpcDataSource = exports.RpcDataSource = exports.caipChainIdToHex = void 0;
|
|
19
19
|
const providers_1 = require("@ethersproject/providers");
|
|
20
20
|
const controller_utils_1 = require("@metamask/controller-utils");
|
|
21
21
|
const utils_1 = require("@metamask/utils");
|
|
22
22
|
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
23
|
-
const AbstractDataSource_1 = require("./AbstractDataSource.cjs");
|
|
24
|
-
const evm_rpc_services_1 = require("./evm-rpc-services/index.cjs");
|
|
25
23
|
const logger_1 = require("../logger.cjs");
|
|
26
24
|
const utils_2 = require("../utils/index.cjs");
|
|
27
25
|
const constants_1 = require("../utils/constants.cjs");
|
|
26
|
+
const AbstractDataSource_1 = require("./AbstractDataSource.cjs");
|
|
27
|
+
const evm_rpc_services_1 = require("./evm-rpc-services/index.cjs");
|
|
28
28
|
const CONTROLLER_NAME = 'RpcDataSource';
|
|
29
29
|
const DEFAULT_BALANCE_INTERVAL = 30000; // 30 seconds
|
|
30
30
|
const DEFAULT_DETECTION_INTERVAL = 180000; // 3 minutes
|
|
@@ -113,21 +113,33 @@ class RpcDataSource extends AbstractDataSource_1.AbstractDataSource {
|
|
|
113
113
|
};
|
|
114
114
|
},
|
|
115
115
|
};
|
|
116
|
-
const tokenDetectorMessenger = {
|
|
117
|
-
call: (_action) => {
|
|
118
|
-
return __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('TokenListController:getState');
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
116
|
// Initialize BalanceFetcher with polling interval
|
|
122
117
|
__classPrivateFieldSet(this, _RpcDataSource_balanceFetcher, new evm_rpc_services_1.BalanceFetcher(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), balanceFetcherMessenger, { pollingInterval: balanceInterval }), "f");
|
|
123
|
-
|
|
118
|
+
// Polling controller awaits this callback; rejections must not become unhandled.
|
|
119
|
+
__classPrivateFieldGet(this, _RpcDataSource_balanceFetcher, "f").setOnBalanceUpdate(async (result) => {
|
|
120
|
+
try {
|
|
121
|
+
await __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_handleBalanceUpdate).call(this, result);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
log('Balance update handler failed', { error });
|
|
125
|
+
}
|
|
126
|
+
});
|
|
124
127
|
// Initialize TokenDetector with polling interval
|
|
125
|
-
|
|
128
|
+
const tokensApiClient = new evm_rpc_services_1.TokensApiClient();
|
|
129
|
+
__classPrivateFieldSet(this, _RpcDataSource_tokenDetector, new evm_rpc_services_1.TokenDetector(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), tokensApiClient, {
|
|
126
130
|
pollingInterval: detectionInterval,
|
|
127
131
|
tokenDetectionEnabled: __classPrivateFieldGet(this, _RpcDataSource_tokenDetectionEnabled, "f"),
|
|
128
132
|
useExternalService: __classPrivateFieldGet(this, _RpcDataSource_useExternalService, "f"),
|
|
129
133
|
}), "f");
|
|
130
|
-
|
|
134
|
+
// Sync throw in the detector would reject the poll tick if uncaught.
|
|
135
|
+
__classPrivateFieldGet(this, _RpcDataSource_tokenDetector, "f").setOnDetectionUpdate((result) => {
|
|
136
|
+
try {
|
|
137
|
+
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_handleDetectionUpdate).call(this, result);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
log('Detection update handler failed', { error });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
131
143
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_subscribeToNetworkController).call(this);
|
|
132
144
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_subscribeToTransactionEvents).call(this);
|
|
133
145
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_initializeFromNetworkController).call(this);
|
|
@@ -227,8 +239,7 @@ class RpcDataSource extends AbstractDataSource_1.AbstractDataSource {
|
|
|
227
239
|
parsed.assetNamespace === 'erc20') {
|
|
228
240
|
const tokenAddress = parsed.assetReference.toLowerCase();
|
|
229
241
|
const normalizedId = (0, utils_2.normalizeAssetId)(assetId);
|
|
230
|
-
const decimals = existingMetadata[normalizedId]?.decimals
|
|
231
|
-
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_getTokenMetadataFromTokenList).call(this, normalizedId)?.decimals;
|
|
242
|
+
const decimals = existingMetadata[normalizedId]?.decimals;
|
|
232
243
|
assetsToFetch.push({
|
|
233
244
|
assetId,
|
|
234
245
|
address: tokenAddress,
|
|
@@ -588,20 +599,12 @@ _RpcDataSource_messenger = new WeakMap(), _RpcDataSource_onActiveChainsUpdated =
|
|
|
588
599
|
}
|
|
589
600
|
}
|
|
590
601
|
else {
|
|
591
|
-
// For ERC20 tokens,
|
|
602
|
+
// For ERC20 tokens, use existing metadata from state if available.
|
|
603
|
+
// Unknown ERC-20s are omitted until TokenDataSource enriches them.
|
|
592
604
|
const existingMeta = existingMetadata[balance.assetId];
|
|
593
605
|
if (existingMeta) {
|
|
594
606
|
assetsInfo[balance.assetId] = existingMeta;
|
|
595
607
|
}
|
|
596
|
-
else {
|
|
597
|
-
// Fallback to token list if not in state
|
|
598
|
-
const tokenListMeta = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_getTokenMetadataFromTokenList).call(this, balance.assetId);
|
|
599
|
-
if (tokenListMeta) {
|
|
600
|
-
assetsInfo[balance.assetId] = tokenListMeta;
|
|
601
|
-
}
|
|
602
|
-
// Unknown ERC-20: omit from assetsInfo until decimals are known.
|
|
603
|
-
// #handleBalanceUpdate resolves decimals via RPC or omits the balance.
|
|
604
|
-
}
|
|
605
608
|
}
|
|
606
609
|
}
|
|
607
610
|
return assetsInfo;
|
|
@@ -927,46 +930,10 @@ async function _RpcDataSource_fetchDecimalsViaRpc(chainId, tokenAddress) {
|
|
|
927
930
|
const state = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('AssetsController:getState');
|
|
928
931
|
return state.assetsInfo ?? {};
|
|
929
932
|
}
|
|
930
|
-
catch {
|
|
931
|
-
|
|
933
|
+
catch (error) {
|
|
934
|
+
log('Failed to get existing assets metadata', { error });
|
|
932
935
|
return {};
|
|
933
936
|
}
|
|
934
|
-
}, _RpcDataSource_getTokenMetadataFromTokenList = function _RpcDataSource_getTokenMetadataFromTokenList(assetId) {
|
|
935
|
-
try {
|
|
936
|
-
const parsed = (0, utils_1.parseCaipAssetType)(assetId);
|
|
937
|
-
if (parsed.assetNamespace !== 'erc20') {
|
|
938
|
-
return undefined;
|
|
939
|
-
}
|
|
940
|
-
const tokenAddress = parsed.assetReference;
|
|
941
|
-
const { reference } = (0, utils_1.parseCaipChainId)(parsed.chainId);
|
|
942
|
-
const hexChainId = (0, utils_1.numberToHex)(parseInt(reference, 10));
|
|
943
|
-
const tokenListState = __classPrivateFieldGet(this, _RpcDataSource_messenger, "f").call('TokenListController:getState');
|
|
944
|
-
const chainCacheEntry = tokenListState?.tokensChainsCache?.[hexChainId];
|
|
945
|
-
const chainTokenList = chainCacheEntry?.data;
|
|
946
|
-
if (!chainTokenList) {
|
|
947
|
-
return undefined;
|
|
948
|
-
}
|
|
949
|
-
// Look up token by address (case-insensitive)
|
|
950
|
-
const lowerAddress = tokenAddress.toLowerCase();
|
|
951
|
-
for (const [address, tokenData] of Object.entries(chainTokenList)) {
|
|
952
|
-
if (address.toLowerCase() === lowerAddress) {
|
|
953
|
-
const token = tokenData;
|
|
954
|
-
if (token.symbol && token.decimals !== undefined) {
|
|
955
|
-
return {
|
|
956
|
-
type: 'erc20',
|
|
957
|
-
symbol: token.symbol,
|
|
958
|
-
name: token.name ?? token.symbol,
|
|
959
|
-
decimals: token.decimals,
|
|
960
|
-
image: token.iconUrl,
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
return undefined;
|
|
966
|
-
}
|
|
967
|
-
catch {
|
|
968
|
-
return undefined;
|
|
969
|
-
}
|
|
970
937
|
};
|
|
971
938
|
function createRpcDataSource(options) {
|
|
972
939
|
return new RpcDataSource(options);
|