@metamask-previews/assets-controller 0.0.0-preview-e09bf49f → 0.0.0-preview-9c68b732
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 +3 -0
- package/dist/AssetsController.cjs +26 -32
- package/dist/AssetsController.cjs.map +1 -1
- package/dist/AssetsController.d.cts +5 -1
- package/dist/AssetsController.d.cts.map +1 -1
- package/dist/AssetsController.d.mts +5 -1
- package/dist/AssetsController.d.mts.map +1 -1
- package/dist/AssetsController.mjs +26 -32
- package/dist/AssetsController.mjs.map +1 -1
- package/dist/data-sources/PriceDataSource.cjs +12 -19
- package/dist/data-sources/PriceDataSource.cjs.map +1 -1
- package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
- package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
- package/dist/data-sources/PriceDataSource.mjs +12 -19
- package/dist/data-sources/PriceDataSource.mjs.map +1 -1
- package/dist/data-sources/RpcDataSource.cjs +232 -42
- package/dist/data-sources/RpcDataSource.cjs.map +1 -1
- package/dist/data-sources/RpcDataSource.d.cts +5 -1
- package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
- package/dist/data-sources/RpcDataSource.d.mts +5 -1
- package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
- package/dist/data-sources/RpcDataSource.mjs +229 -42
- package/dist/data-sources/RpcDataSource.mjs.map +1 -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.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs +32 -27
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts +12 -5
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts +12 -5
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs +32 -27
- package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +23 -12
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +8 -3
- 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 +8 -3
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +23 -12
- 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 +2 -2
- 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 +2 -2
- 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/data-sources/evm-rpc-services/types/index.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/index.d.cts +1 -1
- package/dist/data-sources/evm-rpc-services/types/index.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/index.d.mts +1 -1
- package/dist/data-sources/evm-rpc-services/types/index.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/index.mjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.d.cts +9 -24
- package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.d.mts +9 -24
- package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -1
- package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +40 -6
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +40 -6
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -9,11 +9,12 @@ 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 _RpcDataSource_instances, _RpcDataSource_timeout, _RpcDataSource_tokenDetectionEnabled, _RpcDataSource_activeChains, _RpcDataSource_chainStatuses, _RpcDataSource_providerCache, _RpcDataSource_activeSubscriptions, _RpcDataSource_multicallClient, _RpcDataSource_balanceFetcher, _RpcDataSource_tokenDetector, _RpcDataSource_handleBalanceUpdate, _RpcDataSource_handleDetectionUpdate, _RpcDataSource_registerActionHandlers, _RpcDataSource_subscribeToNetworkController, _RpcDataSource_initializeFromNetworkController, _RpcDataSource_updateFromNetworkState, _RpcDataSource_getProvider, _RpcDataSource_getMulticallProvider, _RpcDataSource_clearProviderCache, _RpcDataSource_accountSupportsChain, _RpcDataSource_buildNativeAssetId;
|
|
12
|
+
var _RpcDataSource_instances, _RpcDataSource_timeout, _RpcDataSource_tokenDetectionEnabled, _RpcDataSource_activeChains, _RpcDataSource_chainStatuses, _RpcDataSource_providerCache, _RpcDataSource_activeSubscriptions, _RpcDataSource_multicallClient, _RpcDataSource_balanceFetcher, _RpcDataSource_tokenDetector, _RpcDataSource_convertToHumanReadable, _RpcDataSource_collectMetadataForBalances, _RpcDataSource_handleBalanceUpdate, _RpcDataSource_handleDetectionUpdate, _RpcDataSource_registerActionHandlers, _RpcDataSource_subscribeToNetworkController, _RpcDataSource_initializeFromNetworkController, _RpcDataSource_updateFromNetworkState, _RpcDataSource_getProvider, _RpcDataSource_getMulticallProvider, _RpcDataSource_clearProviderCache, _RpcDataSource_accountSupportsChain, _RpcDataSource_buildNativeAssetId, _RpcDataSource_getExistingAssetsMetadata, _RpcDataSource_getTokenMetadataFromTokenList;
|
|
13
13
|
import { Web3Provider } from "@ethersproject/providers";
|
|
14
14
|
import { BaseController } from "@metamask/base-controller";
|
|
15
15
|
import { toHex } from "@metamask/controller-utils";
|
|
16
16
|
import { isStrictHexString, isCaipChainId, parseCaipChainId } from "@metamask/utils";
|
|
17
|
+
import BigNumberJS from "bignumber.js";
|
|
17
18
|
import { BalanceFetcher, MulticallClient, TokenDetector } from "./evm-rpc-services/index.mjs";
|
|
18
19
|
import { projectLogger, createModuleLogger } from "../logger.mjs";
|
|
19
20
|
const CONTROLLER_NAME = 'RpcDataSource';
|
|
@@ -90,24 +91,25 @@ export class RpcDataSource extends BaseController {
|
|
|
90
91
|
__classPrivateFieldSet(this, _RpcDataSource_multicallClient, new MulticallClient((hexChainId) => {
|
|
91
92
|
return __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_getMulticallProvider).call(this, hexChainId);
|
|
92
93
|
}), "f");
|
|
93
|
-
// Create
|
|
94
|
-
const
|
|
95
|
-
|
|
94
|
+
// Create messenger adapters for BalanceFetcher and TokenDetector
|
|
95
|
+
const balanceFetcherMessenger = {
|
|
96
|
+
call: (_action) => {
|
|
97
|
+
const state = this.messenger.call('AssetsController:getState');
|
|
98
|
+
return {
|
|
99
|
+
assetsBalance: (state.assetsBalance ?? {}),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
96
102
|
};
|
|
97
|
-
const
|
|
98
|
-
|
|
103
|
+
const tokenDetectorMessenger = {
|
|
104
|
+
call: (_action) => {
|
|
105
|
+
return this.messenger.call('TokenListController:getState');
|
|
106
|
+
},
|
|
99
107
|
};
|
|
100
108
|
// Initialize BalanceFetcher with polling interval
|
|
101
|
-
__classPrivateFieldSet(this, _RpcDataSource_balanceFetcher, new BalanceFetcher(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), {
|
|
102
|
-
pollingInterval: balanceInterval,
|
|
103
|
-
}), "f");
|
|
104
|
-
__classPrivateFieldGet(this, _RpcDataSource_balanceFetcher, "f").setUserTokensStateGetter(getUserTokensState);
|
|
109
|
+
__classPrivateFieldSet(this, _RpcDataSource_balanceFetcher, new BalanceFetcher(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), balanceFetcherMessenger, { pollingInterval: balanceInterval }), "f");
|
|
105
110
|
__classPrivateFieldGet(this, _RpcDataSource_balanceFetcher, "f").setOnBalanceUpdate(__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_handleBalanceUpdate).bind(this));
|
|
106
111
|
// Initialize TokenDetector with polling interval
|
|
107
|
-
__classPrivateFieldSet(this, _RpcDataSource_tokenDetector, new TokenDetector(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), {
|
|
108
|
-
pollingInterval: detectionInterval,
|
|
109
|
-
}), "f");
|
|
110
|
-
__classPrivateFieldGet(this, _RpcDataSource_tokenDetector, "f").setTokenListStateGetter(getTokenListState);
|
|
112
|
+
__classPrivateFieldSet(this, _RpcDataSource_tokenDetector, new TokenDetector(__classPrivateFieldGet(this, _RpcDataSource_multicallClient, "f"), tokenDetectorMessenger, { pollingInterval: detectionInterval }), "f");
|
|
111
113
|
__classPrivateFieldGet(this, _RpcDataSource_tokenDetector, "f").setOnDetectionUpdate(__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_handleDetectionUpdate).bind(this));
|
|
112
114
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_registerActionHandlers).call(this);
|
|
113
115
|
__classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_subscribeToNetworkController).call(this);
|
|
@@ -193,6 +195,7 @@ export class RpcDataSource extends BaseController {
|
|
|
193
195
|
return response;
|
|
194
196
|
}
|
|
195
197
|
const assetsBalance = {};
|
|
198
|
+
const assetsMetadata = {};
|
|
196
199
|
const failedChains = [];
|
|
197
200
|
// Fetch balances for each chain using BalanceFetcher
|
|
198
201
|
for (const chainId of chainsToFetch) {
|
|
@@ -209,10 +212,17 @@ export class RpcDataSource extends BaseController {
|
|
|
209
212
|
if (!assetsBalance[accountId]) {
|
|
210
213
|
assetsBalance[accountId] = {};
|
|
211
214
|
}
|
|
212
|
-
//
|
|
215
|
+
// Collect metadata for all balances
|
|
216
|
+
const balanceMetadata = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_collectMetadataForBalances).call(this, result.balances, chainId);
|
|
217
|
+
Object.assign(assetsMetadata, balanceMetadata);
|
|
218
|
+
// Convert balances to human-readable format
|
|
213
219
|
for (const balance of result.balances) {
|
|
220
|
+
const metadata = assetsMetadata[balance.assetId];
|
|
221
|
+
// Default to 18 decimals (ERC20 standard) for consistent human-readable format
|
|
222
|
+
const decimals = metadata?.decimals ?? 18;
|
|
223
|
+
const humanReadableAmount = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_convertToHumanReadable).call(this, balance.balance, decimals);
|
|
214
224
|
assetsBalance[accountId][balance.assetId] = {
|
|
215
|
-
amount:
|
|
225
|
+
amount: humanReadableAmount,
|
|
216
226
|
};
|
|
217
227
|
}
|
|
218
228
|
}
|
|
@@ -223,6 +233,16 @@ export class RpcDataSource extends BaseController {
|
|
|
223
233
|
}
|
|
224
234
|
const nativeAssetId = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_buildNativeAssetId).call(this, chainId);
|
|
225
235
|
assetsBalance[accountId][nativeAssetId] = { amount: '0' };
|
|
236
|
+
// Even on error, include native token metadata
|
|
237
|
+
const chainStatus = __classPrivateFieldGet(this, _RpcDataSource_chainStatuses, "f")[chainId];
|
|
238
|
+
if (chainStatus) {
|
|
239
|
+
assetsMetadata[nativeAssetId] = {
|
|
240
|
+
type: 'native',
|
|
241
|
+
symbol: chainStatus.nativeCurrency,
|
|
242
|
+
name: chainStatus.nativeCurrency,
|
|
243
|
+
decimals: 18,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
226
246
|
if (!failedChains.includes(chainId)) {
|
|
227
247
|
failedChains.push(chainId);
|
|
228
248
|
}
|
|
@@ -246,6 +266,10 @@ export class RpcDataSource extends BaseController {
|
|
|
246
266
|
});
|
|
247
267
|
}
|
|
248
268
|
response.assetsBalance = assetsBalance;
|
|
269
|
+
// Include metadata for native tokens if we have any
|
|
270
|
+
if (Object.keys(assetsMetadata).length > 0) {
|
|
271
|
+
response.assetsMetadata = assetsMetadata;
|
|
272
|
+
}
|
|
249
273
|
return response;
|
|
250
274
|
}
|
|
251
275
|
/**
|
|
@@ -275,10 +299,27 @@ export class RpcDataSource extends BaseController {
|
|
|
275
299
|
});
|
|
276
300
|
// Convert detected assets to DataResponse format
|
|
277
301
|
const balances = {};
|
|
278
|
-
|
|
302
|
+
const assetsMetadata = {};
|
|
303
|
+
// Build metadata from detected assets
|
|
304
|
+
for (const asset of result.detectedAssets) {
|
|
305
|
+
if (asset.symbol && asset.decimals !== undefined) {
|
|
306
|
+
assetsMetadata[asset.assetId] = {
|
|
307
|
+
type: 'erc20',
|
|
308
|
+
symbol: asset.symbol,
|
|
309
|
+
name: asset.name ?? asset.symbol,
|
|
310
|
+
decimals: asset.decimals,
|
|
311
|
+
image: asset.image,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Add balances for detected tokens (converted to human-readable format)
|
|
279
316
|
for (const balance of result.detectedBalances) {
|
|
317
|
+
const detectedAsset = result.detectedAssets.find((asset) => asset.assetId === balance.assetId);
|
|
318
|
+
// Default to 18 decimals (ERC20 standard) for consistent human-readable format
|
|
319
|
+
const decimals = detectedAsset?.decimals ?? 18;
|
|
320
|
+
const humanReadableAmount = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_convertToHumanReadable).call(this, balance.balance, decimals);
|
|
280
321
|
balances[balance.assetId] = {
|
|
281
|
-
amount:
|
|
322
|
+
amount: humanReadableAmount,
|
|
282
323
|
};
|
|
283
324
|
}
|
|
284
325
|
const response = {
|
|
@@ -289,6 +330,10 @@ export class RpcDataSource extends BaseController {
|
|
|
289
330
|
[accountId]: balances,
|
|
290
331
|
},
|
|
291
332
|
};
|
|
333
|
+
// Include metadata if we have any
|
|
334
|
+
if (Object.keys(assetsMetadata).length > 0) {
|
|
335
|
+
response.assetsMetadata = assetsMetadata;
|
|
336
|
+
}
|
|
292
337
|
return response;
|
|
293
338
|
}
|
|
294
339
|
catch (error) {
|
|
@@ -298,7 +343,7 @@ export class RpcDataSource extends BaseController {
|
|
|
298
343
|
}
|
|
299
344
|
get assetsMiddleware() {
|
|
300
345
|
return async (context, next) => {
|
|
301
|
-
var _a, _b;
|
|
346
|
+
var _a, _b, _c;
|
|
302
347
|
const { request } = context;
|
|
303
348
|
const supportedChains = request.chainIds.filter((chainId) => __classPrivateFieldGet(this, _RpcDataSource_activeChains, "f").includes(chainId));
|
|
304
349
|
if (supportedChains.length === 0) {
|
|
@@ -323,6 +368,13 @@ export class RpcDataSource extends BaseController {
|
|
|
323
368
|
};
|
|
324
369
|
}
|
|
325
370
|
}
|
|
371
|
+
if (response.assetsMetadata) {
|
|
372
|
+
(_c = context.response).assetsMetadata ?? (_c.assetsMetadata = {});
|
|
373
|
+
context.response.assetsMetadata = {
|
|
374
|
+
...context.response.assetsMetadata,
|
|
375
|
+
...response.assetsMetadata,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
326
378
|
const failedChains = new Set(Object.keys(response.errors ?? {}));
|
|
327
379
|
successfullyHandledChains = supportedChains.filter((chainId) => !failedChains.has(chainId));
|
|
328
380
|
if (successfullyHandledChains.length > 0) {
|
|
@@ -357,20 +409,19 @@ export class RpcDataSource extends BaseController {
|
|
|
357
409
|
log('No active chains to subscribe');
|
|
358
410
|
return;
|
|
359
411
|
}
|
|
360
|
-
// Handle subscription update
|
|
412
|
+
// Handle subscription update - restart polling for new chains
|
|
361
413
|
if (isUpdate) {
|
|
362
414
|
const existing = __classPrivateFieldGet(this, _RpcDataSource_activeSubscriptions, "f").get(subscriptionId);
|
|
363
415
|
if (existing) {
|
|
364
|
-
log('Updating existing subscription', {
|
|
416
|
+
log('Updating existing subscription - restarting polling', {
|
|
365
417
|
subscriptionId,
|
|
366
|
-
|
|
418
|
+
existingChains: existing.chains,
|
|
419
|
+
newChains: chainsToSubscribe,
|
|
367
420
|
});
|
|
368
|
-
|
|
369
|
-
existing.accounts = request.accounts;
|
|
370
|
-
return;
|
|
421
|
+
// Don't return early - continue to unsubscribe and restart polling
|
|
371
422
|
}
|
|
372
423
|
}
|
|
373
|
-
// Clean up existing subscription
|
|
424
|
+
// Clean up existing subscription (stops old polling)
|
|
374
425
|
await this.unsubscribe(subscriptionId);
|
|
375
426
|
// Start polling through BalanceFetcher and TokenDetector
|
|
376
427
|
const balancePollingTokens = [];
|
|
@@ -450,18 +501,82 @@ export class RpcDataSource extends BaseController {
|
|
|
450
501
|
__classPrivateFieldGet(this, _RpcDataSource_providerCache, "f").clear();
|
|
451
502
|
}
|
|
452
503
|
}
|
|
453
|
-
_RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = new WeakMap(), _RpcDataSource_activeChains = new WeakMap(), _RpcDataSource_chainStatuses = new WeakMap(), _RpcDataSource_providerCache = new WeakMap(), _RpcDataSource_activeSubscriptions = new WeakMap(), _RpcDataSource_multicallClient = new WeakMap(), _RpcDataSource_balanceFetcher = new WeakMap(), _RpcDataSource_tokenDetector = new WeakMap(), _RpcDataSource_instances = new WeakSet(),
|
|
454
|
-
const
|
|
504
|
+
_RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = new WeakMap(), _RpcDataSource_activeChains = new WeakMap(), _RpcDataSource_chainStatuses = new WeakMap(), _RpcDataSource_providerCache = new WeakMap(), _RpcDataSource_activeSubscriptions = new WeakMap(), _RpcDataSource_multicallClient = new WeakMap(), _RpcDataSource_balanceFetcher = new WeakMap(), _RpcDataSource_tokenDetector = new WeakMap(), _RpcDataSource_instances = new WeakSet(), _RpcDataSource_convertToHumanReadable = function _RpcDataSource_convertToHumanReadable(rawBalance, decimals) {
|
|
505
|
+
const rawAmount = new BigNumberJS(rawBalance);
|
|
506
|
+
const divisor = new BigNumberJS(10).pow(decimals);
|
|
507
|
+
return rawAmount.dividedBy(divisor).toString();
|
|
508
|
+
}, _RpcDataSource_collectMetadataForBalances = function _RpcDataSource_collectMetadataForBalances(balances, chainId) {
|
|
509
|
+
const assetsMetadata = {};
|
|
510
|
+
const existingMetadata = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_getExistingAssetsMetadata).call(this);
|
|
511
|
+
for (const balance of balances) {
|
|
512
|
+
const isNative = balance.assetId.includes('/slip44:');
|
|
513
|
+
if (isNative) {
|
|
514
|
+
const chainStatus = __classPrivateFieldGet(this, _RpcDataSource_chainStatuses, "f")[chainId];
|
|
515
|
+
if (chainStatus) {
|
|
516
|
+
assetsMetadata[balance.assetId] = {
|
|
517
|
+
type: 'native',
|
|
518
|
+
symbol: chainStatus.nativeCurrency,
|
|
519
|
+
name: chainStatus.nativeCurrency,
|
|
520
|
+
decimals: 18,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
// For ERC20 tokens, try existing metadata from state first
|
|
526
|
+
const existingMeta = existingMetadata[balance.assetId];
|
|
527
|
+
if (existingMeta) {
|
|
528
|
+
assetsMetadata[balance.assetId] = existingMeta;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
// Fallback to token list if not in state
|
|
532
|
+
const tokenListMeta = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_getTokenMetadataFromTokenList).call(this, balance.assetId);
|
|
533
|
+
if (tokenListMeta) {
|
|
534
|
+
assetsMetadata[balance.assetId] = tokenListMeta;
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
// Default metadata for unknown ERC20 tokens.
|
|
538
|
+
// Use 18 decimals (the standard for most ERC20 tokens)
|
|
539
|
+
// to ensure consistent human-readable balance format.
|
|
540
|
+
assetsMetadata[balance.assetId] = {
|
|
541
|
+
type: 'erc20',
|
|
542
|
+
symbol: '',
|
|
543
|
+
name: '',
|
|
544
|
+
decimals: 18,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return assetsMetadata;
|
|
551
|
+
}, _RpcDataSource_handleBalanceUpdate = function _RpcDataSource_handleBalanceUpdate(result) {
|
|
552
|
+
const newBalances = {};
|
|
553
|
+
// Convert hex chain ID to CAIP-2 format
|
|
554
|
+
const chainIdDecimal = parseInt(result.chainId, 16);
|
|
555
|
+
const caipChainId = `eip155:${chainIdDecimal}`;
|
|
556
|
+
// Collect metadata for all balances
|
|
557
|
+
const assetsMetadata = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_collectMetadataForBalances).call(this, result.balances, caipChainId);
|
|
558
|
+
// Convert balances to human-readable format using metadata
|
|
455
559
|
for (const balance of result.balances) {
|
|
456
|
-
|
|
457
|
-
|
|
560
|
+
const metadata = assetsMetadata[balance.assetId];
|
|
561
|
+
// Default to 18 decimals (ERC20 standard) for consistent human-readable format
|
|
562
|
+
const decimals = metadata?.decimals ?? 18;
|
|
563
|
+
const humanReadableAmount = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_convertToHumanReadable).call(this, balance.balance, decimals);
|
|
564
|
+
newBalances[balance.assetId] = {
|
|
565
|
+
amount: humanReadableAmount,
|
|
458
566
|
};
|
|
459
567
|
}
|
|
568
|
+
// Only send new data to AssetsController - it handles merging atomically
|
|
569
|
+
// to avoid race conditions when concurrent updates occur for the same account
|
|
460
570
|
const response = {
|
|
461
571
|
assetsBalance: {
|
|
462
|
-
[result.accountId]:
|
|
572
|
+
[result.accountId]: newBalances,
|
|
463
573
|
},
|
|
574
|
+
assetsMetadata,
|
|
464
575
|
};
|
|
576
|
+
log('Balance update response', {
|
|
577
|
+
accountId: result.accountId,
|
|
578
|
+
newBalanceCount: Object.keys(newBalances).length,
|
|
579
|
+
});
|
|
465
580
|
this.messenger
|
|
466
581
|
.call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)
|
|
467
582
|
.catch((error) => {
|
|
@@ -471,22 +586,47 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
471
586
|
log('Detected new tokens', {
|
|
472
587
|
count: result.detectedAssets.length,
|
|
473
588
|
});
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
589
|
+
// Build new metadata from detected assets
|
|
590
|
+
const newMetadata = {};
|
|
591
|
+
if (result.detectedAssets.length > 0) {
|
|
592
|
+
for (const asset of result.detectedAssets) {
|
|
593
|
+
// Only include if we have metadata (symbol and decimals at minimum)
|
|
594
|
+
if (asset.symbol && asset.decimals !== undefined) {
|
|
595
|
+
newMetadata[asset.assetId] = {
|
|
596
|
+
type: 'erc20',
|
|
597
|
+
symbol: asset.symbol,
|
|
598
|
+
name: asset.name ?? asset.symbol,
|
|
599
|
+
decimals: asset.decimals,
|
|
600
|
+
image: asset.image,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
// Build new balances from detected tokens
|
|
606
|
+
const newBalances = {};
|
|
480
607
|
if (result.detectedBalances.length > 0) {
|
|
481
|
-
response.assetsBalance = {
|
|
482
|
-
[result.accountId]: {},
|
|
483
|
-
};
|
|
484
608
|
for (const balance of result.detectedBalances) {
|
|
485
|
-
|
|
486
|
-
|
|
609
|
+
// Get decimals from the detected asset metadata
|
|
610
|
+
const detectedAsset = result.detectedAssets.find((asset) => asset.assetId === balance.assetId);
|
|
611
|
+
// Default to 18 decimals (ERC20 standard) for consistent human-readable format
|
|
612
|
+
const decimals = detectedAsset?.decimals ?? 18;
|
|
613
|
+
const humanReadableAmount = __classPrivateFieldGet(this, _RpcDataSource_instances, "m", _RpcDataSource_convertToHumanReadable).call(this, balance.balance, decimals);
|
|
614
|
+
newBalances[balance.assetId] = {
|
|
615
|
+
amount: humanReadableAmount,
|
|
487
616
|
};
|
|
488
617
|
}
|
|
489
618
|
}
|
|
619
|
+
// Only send new data to AssetsController - it handles merging atomically
|
|
620
|
+
// to avoid race conditions when concurrent updates occur for the same account
|
|
621
|
+
const response = {
|
|
622
|
+
detectedAssets: {
|
|
623
|
+
[result.accountId]: result.detectedAssets.map((asset) => asset.assetId),
|
|
624
|
+
},
|
|
625
|
+
assetsMetadata: newMetadata,
|
|
626
|
+
assetsBalance: {
|
|
627
|
+
[result.accountId]: newBalances,
|
|
628
|
+
},
|
|
629
|
+
};
|
|
490
630
|
this.messenger
|
|
491
631
|
.call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)
|
|
492
632
|
.catch((error) => {
|
|
@@ -626,6 +766,53 @@ _RpcDataSource_timeout = new WeakMap(), _RpcDataSource_tokenDetectionEnabled = n
|
|
|
626
766
|
const { nativeAssetIdentifiers } = this.messenger.call('NetworkEnablementController:getState');
|
|
627
767
|
return (nativeAssetIdentifiers[chainId] ??
|
|
628
768
|
`${chainId}/slip44:60`);
|
|
769
|
+
}, _RpcDataSource_getExistingAssetsMetadata = function _RpcDataSource_getExistingAssetsMetadata() {
|
|
770
|
+
try {
|
|
771
|
+
const state = this.messenger.call('AssetsController:getState');
|
|
772
|
+
return (state.assetsMetadata ?? {});
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
// If AssetsController:getState fails, return empty metadata
|
|
776
|
+
return {};
|
|
777
|
+
}
|
|
778
|
+
}, _RpcDataSource_getTokenMetadataFromTokenList = function _RpcDataSource_getTokenMetadataFromTokenList(assetId) {
|
|
779
|
+
try {
|
|
780
|
+
// Parse asset ID to get chain and token address
|
|
781
|
+
// Format: eip155:{chainId}/erc20:{address}
|
|
782
|
+
const [chainPart, assetPart] = assetId.split('/');
|
|
783
|
+
if (!assetPart?.startsWith('erc20:')) {
|
|
784
|
+
return undefined;
|
|
785
|
+
}
|
|
786
|
+
const tokenAddress = assetPart.slice(6); // Remove 'erc20:' prefix
|
|
787
|
+
const chainIdDecimal = chainPart.split(':')[1];
|
|
788
|
+
const hexChainId = `0x${parseInt(chainIdDecimal, 10).toString(16)}`;
|
|
789
|
+
const tokenListState = this.messenger.call('TokenListController:getState');
|
|
790
|
+
const chainCacheEntry = tokenListState.tokensChainsCache[hexChainId];
|
|
791
|
+
const chainTokenList = chainCacheEntry?.data;
|
|
792
|
+
if (!chainTokenList) {
|
|
793
|
+
return undefined;
|
|
794
|
+
}
|
|
795
|
+
// Look up token by address (case-insensitive)
|
|
796
|
+
const lowerAddress = tokenAddress.toLowerCase();
|
|
797
|
+
for (const [address, tokenData] of Object.entries(chainTokenList)) {
|
|
798
|
+
if (address.toLowerCase() === lowerAddress) {
|
|
799
|
+
const token = tokenData;
|
|
800
|
+
if (token.symbol && token.decimals !== undefined) {
|
|
801
|
+
return {
|
|
802
|
+
type: 'erc20',
|
|
803
|
+
symbol: token.symbol,
|
|
804
|
+
name: token.name ?? token.symbol,
|
|
805
|
+
decimals: token.decimals,
|
|
806
|
+
image: token.iconUrl,
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
return undefined;
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
return undefined;
|
|
815
|
+
}
|
|
629
816
|
};
|
|
630
817
|
export function createRpcDataSource(options) {
|
|
631
818
|
return new RpcDataSource(options);
|