@talismn/balances 0.0.0-pr2043-20250619165342 → 0.0.0-pr2043-20250620074243

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.
@@ -16,17 +16,16 @@ var txwrapperCore = require('@substrate/txwrapper-core');
16
16
  var scale = require('@talismn/scale');
17
17
  var camelCase = require('lodash/camelCase');
18
18
  var PQueue = require('p-queue');
19
+ var z = require('zod/v4');
19
20
  var sapi = require('@talismn/sapi');
20
21
  var types = require('@polkadot/types');
21
22
  var groupBy = require('lodash/groupBy');
22
23
  var utils = require('@polkadot-api/utils');
23
24
  var polkadotApi = require('polkadot-api');
24
- var PromisePool = require('@supercharge/promise-pool');
25
25
  var chainConnector = require('@talismn/chain-connector');
26
26
  var rxjs = require('rxjs');
27
27
  var scaleTs = require('scale-ts');
28
28
  var upperFirst = require('lodash/upperFirst');
29
- var z = require('zod/v4');
30
29
  var apiContract = require('@polkadot/api-contract');
31
30
 
32
31
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -37,10 +36,9 @@ var pako__default = /*#__PURE__*/_interopDefault(pako);
37
36
  var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
38
37
  var camelCase__default = /*#__PURE__*/_interopDefault(camelCase);
39
38
  var PQueue__default = /*#__PURE__*/_interopDefault(PQueue);
39
+ var z__default = /*#__PURE__*/_interopDefault(z);
40
40
  var groupBy__default = /*#__PURE__*/_interopDefault(groupBy);
41
- var PromisePool__default = /*#__PURE__*/_interopDefault(PromisePool);
42
41
  var upperFirst__default = /*#__PURE__*/_interopDefault(upperFirst);
43
- var z__default = /*#__PURE__*/_interopDefault(z);
44
42
 
45
43
  // Record<string, unknown> | undefined
46
44
 
@@ -128,7 +126,7 @@ class EvmTokenFetcher {
128
126
 
129
127
  var pkg = {
130
128
  name: "@talismn/balances",
131
- version: "0.0.0-pr2043-20250619165342"};
129
+ version: "0.0.0-pr2043-20250620074243"};
132
130
 
133
131
  var log = anylogger__default.default(pkg.name);
134
132
 
@@ -1032,6 +1030,10 @@ class TalismanBalancesDatabase extends dexie.Dexie {
1032
1030
  }
1033
1031
  const db = new TalismanBalancesDatabase();
1034
1032
 
1033
+ const TokenConfigBaseSchema = chaindataProvider.TokenBaseSchema.partial().omit({
1034
+ id: true
1035
+ });
1036
+
1035
1037
  const erc20Abi = [{
1036
1038
  constant: true,
1037
1039
  inputs: [],
@@ -1263,6 +1265,20 @@ const erc20Abi = [{
1263
1265
  const erc20BalancesAggregatorAbi = viem.parseAbi(["struct AccountToken {address account; address token;}", "function balances(AccountToken[] memory accountTokens) public view returns (uint256[] memory)"]);
1264
1266
 
1265
1267
  const moduleType$7 = "evm-erc20";
1268
+ // {
1269
+ // tokens?: Array<
1270
+ // {
1271
+ // symbol?: string
1272
+ // decimals?: number
1273
+ // name?: string
1274
+ // contractAddress?: `0x${string}`
1275
+ // } & BalancesConfigTokenParams
1276
+ // >
1277
+ // }
1278
+
1279
+ const EvmErc20TokenConfigSchema = TokenConfigBaseSchema.extend({
1280
+ contractAddress: chaindataProvider.EvmErc20TokenSchema.shape.contractAddress
1281
+ });
1266
1282
  const EvmErc20Module = hydrate => {
1267
1283
  const {
1268
1284
  chainConnectors,
@@ -1308,11 +1324,11 @@ const EvmErc20Module = hydrate => {
1308
1324
  * This method is currently executed on [a squid](https://github.com/TalismanSociety/chaindata-squid/blob/0ee02818bf5caa7362e3f3664e55ef05ec8df078/src/steps/updateEvmNetworksFromGithub.ts#L338-L343).
1309
1325
  * In a future version of the balance libraries, we may build some kind of async scheduling system which will keep the list of tokens for each chain up to date without relying on a squid.
1310
1326
  */
1311
- async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig) {
1327
+ async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig, tokens) {
1312
1328
  //const { isTestnet } = chainMeta
1313
1329
 
1314
1330
  const chainTokens = {};
1315
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
1331
+ for (const tokenConfig of tokens ?? []) {
1316
1332
  const {
1317
1333
  contractAddress,
1318
1334
  symbol: contractSymbol,
@@ -1596,6 +1612,7 @@ function groupAddressesByTokenByEvmNetwork$1(addressesByToken, tokens) {
1596
1612
  const abiMulticall = viem.parseAbi(["struct Call { address target; bytes callData; }", "struct Call3 { address target; bool allowFailure; bytes callData; }", "struct Call3Value { address target; bool allowFailure; uint256 value; bytes callData; }", "struct Result { bool success; bytes returnData; }", "function aggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes[] memory returnData)", "function aggregate3(Call3[] calldata calls) public payable returns (Result[] memory returnData)", "function aggregate3Value(Call3Value[] calldata calls) public payable returns (Result[] memory returnData)", "function blockAndAggregate(Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)", "function getBasefee() view returns (uint256 basefee)", "function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)", "function getBlockNumber() view returns (uint256 blockNumber)", "function getChainId() view returns (uint256 chainid)", "function getCurrentBlockCoinbase() view returns (address coinbase)", "function getCurrentBlockDifficulty() view returns (uint256 difficulty)", "function getCurrentBlockGasLimit() view returns (uint256 gaslimit)", "function getCurrentBlockTimestamp() view returns (uint256 timestamp)", "function getEthBalance(address addr) view returns (uint256 balance)", "function getLastBlockHash() view returns (bytes32 blockHash)", "function tryAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (Result[] memory returnData)", "function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) public payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData)"]);
1597
1613
 
1598
1614
  const moduleType$6 = "evm-native";
1615
+ const EvmNativeTokenConfigSchema = TokenConfigBaseSchema;
1599
1616
  const EvmNativeModule = hydrate => {
1600
1617
  const {
1601
1618
  chainConnectors,
@@ -1623,29 +1640,34 @@ const EvmNativeModule = hydrate => {
1623
1640
  * This method is currently executed on [a squid](https://github.com/TalismanSociety/chaindata-squid/blob/0ee02818bf5caa7362e3f3664e55ef05ec8df078/src/steps/updateEvmNetworksFromGithub.ts#L338-L343).
1624
1641
  * In a future version of the balance libraries, we may build some kind of async scheduling system which will keep the list of tokens for each chain up to date without relying on a squid.
1625
1642
  */
1626
- async fetchEvmChainTokens(networkId, chainMeta, moduleConfig) {
1627
- const symbol = moduleConfig?.symbol ?? "ETH";
1628
- const decimals = typeof moduleConfig?.decimals === "number" ? moduleConfig.decimals : 18;
1629
- const name = moduleConfig?.name ?? symbol;
1630
- const id = chaindataProvider.evmNativeTokenId(networkId);
1631
- const nativeToken = {
1632
- platform: "ethereum",
1633
- id,
1634
- type: "evm-native",
1635
- isDefault: true,
1636
- symbol,
1637
- decimals,
1638
- name,
1639
- logo: moduleConfig?.logo,
1640
- networkId
1641
- };
1642
- if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol;
1643
- if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId;
1644
- if (moduleConfig?.mirrorOf) nativeToken.mirrorOf = moduleConfig?.mirrorOf;
1645
- if (moduleConfig?.noDiscovery) nativeToken.noDiscovery = moduleConfig?.noDiscovery;
1646
- return {
1647
- [nativeToken.id]: nativeToken
1648
- };
1643
+ async fetchEvmChainTokens() {
1644
+ // networkId, chainMeta, moduleConfig, tokens
1645
+ // TODO ? seems unneeded, this info is set on the EthNetworkConfig["nativeCurrency"] field
1646
+ return {};
1647
+
1648
+ // const symbol = moduleConfig?.symbol ?? "ETH"
1649
+ // const decimals = typeof moduleConfig?.decimals === "number" ? moduleConfig.decimals : 18
1650
+ // const name = moduleConfig?.name ?? symbol
1651
+
1652
+ // const id = evmNativeTokenId(networkId)
1653
+ // const nativeToken: EvmNativeToken = {
1654
+ // platform: "ethereum",
1655
+ // id,
1656
+ // type: "evm-native",
1657
+ // isDefault: true,
1658
+ // symbol,
1659
+ // decimals,
1660
+ // name,
1661
+ // logo: moduleConfig?.logo,
1662
+ // networkId,
1663
+ // }
1664
+
1665
+ // if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol
1666
+ // if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId
1667
+ // if (moduleConfig?.mirrorOf) nativeToken.mirrorOf = moduleConfig?.mirrorOf
1668
+ // if (moduleConfig?.noDiscovery) nativeToken.noDiscovery = moduleConfig?.noDiscovery
1669
+
1670
+ // return { [nativeToken.id]: nativeToken }
1649
1671
  },
1650
1672
  async subscribeBalances({
1651
1673
  addressesByToken,
@@ -2387,6 +2409,36 @@ const uniswapV2PairAbi = [{
2387
2409
  }];
2388
2410
 
2389
2411
  const moduleType$5 = "evm-uniswapv2";
2412
+ const EvmUniswapV2TokenConfigSchema = TokenConfigBaseSchema.extend({
2413
+ contractAddress: chaindataProvider.EvmUniswapV2TokenSchema.shape.contractAddress,
2414
+ // the ones below are unused and prone to error, feels better to always fetch these from chain
2415
+ symbol0: chaindataProvider.EvmUniswapV2TokenSchema.shape.symbol0.optional(),
2416
+ symbol1: chaindataProvider.EvmUniswapV2TokenSchema.shape.symbol1.optional(),
2417
+ decimals0: chaindataProvider.EvmUniswapV2TokenSchema.shape.decimals0.optional(),
2418
+ decimals1: chaindataProvider.EvmUniswapV2TokenSchema.shape.decimals1.optional(),
2419
+ tokenAddress0: chaindataProvider.EvmUniswapV2TokenSchema.shape.tokenAddress0.optional(),
2420
+ tokenAddress1: chaindataProvider.EvmUniswapV2TokenSchema.shape.tokenAddress1.optional(),
2421
+ coingeckoId0: chaindataProvider.EvmUniswapV2TokenSchema.shape.coingeckoId0.optional(),
2422
+ coingeckoId1: chaindataProvider.EvmUniswapV2TokenSchema.shape.coingeckoId1.optional()
2423
+ });
2424
+
2425
+ // {
2426
+ // pools?: Array<
2427
+ // {
2428
+ // contractAddress?: `0x${string}`
2429
+ // decimals?: number
2430
+ // symbol0?: string
2431
+ // symbol1?: string
2432
+ // decimals0?: number
2433
+ // decimals1?: number
2434
+ // tokenAddress0?: `0x${string}`
2435
+ // tokenAddress1?: `0x${string}`
2436
+ // coingeckoId0?: string
2437
+ // coingeckoId1?: string
2438
+ // } & BalancesConfigTokenParams
2439
+ // >
2440
+ // }
2441
+
2390
2442
  const EvmUniswapV2Module = hydrate => {
2391
2443
  const {
2392
2444
  chainConnectors,
@@ -2402,9 +2454,9 @@ const EvmUniswapV2Module = hydrate => {
2402
2454
  extra: null
2403
2455
  };
2404
2456
  },
2405
- async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig) {
2457
+ async fetchEvmChainTokens(chainId, _chainMeta, moduleConfig, pools) {
2406
2458
  const tokens = {};
2407
- for (const tokenConfig of moduleConfig?.pools ?? []) {
2459
+ for (const tokenConfig of pools ?? []) {
2408
2460
  const {
2409
2461
  contractAddress,
2410
2462
  decimals,
@@ -2694,12 +2746,15 @@ const getSpecVersion = async (chainConnector, networkId) => {
2694
2746
  const CACHE$1 = new Map();
2695
2747
  const getMetadataRpc = async (chainConnector, networkId) => {
2696
2748
  if (CACHE$1.has(networkId)) return CACHE$1.get(networkId);
2697
- const pResult = sapi.fetchBestMetadata((...args) => chainConnector.send(networkId, ...args), true // allow fallback to 14 as modules dont use any v15 or v16 specifics yet
2749
+ const pResult = sapi.fetchBestMetadata((method, params, isCacheable) => chainConnector.send(networkId, method, params, isCacheable, {
2750
+ expectErrors: true
2751
+ }), true // allow fallback to 14 as modules dont use any v15 or v16 specifics yet
2698
2752
  );
2699
2753
  CACHE$1.set(networkId, pResult);
2700
2754
  try {
2701
2755
  return await pResult;
2702
2756
  } catch (cause) {
2757
+ if (util.isAbortError(cause)) throw cause;
2703
2758
  throw new Error(`Failed to fetch metadataRpc for network ${networkId}`, {
2704
2759
  cause
2705
2760
  });
@@ -2717,6 +2772,8 @@ const POOL = new PQueue__default.default({
2717
2772
  });
2718
2773
  const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, specVersion, signal) => {
2719
2774
  if (CACHE.has(networkId)) return CACHE.get(networkId);
2775
+ if (!signal) log.warn("[miniMetadata] getMiniMetadatas called without signal, this may hang the updates", new Error("No signal provided") // this will show the stack trace
2776
+ );
2720
2777
  const pResult = POOL.add(() => fetchMiniMetadatas(chainConnector, chaindataProvider, networkId, specVersion), {
2721
2778
  signal
2722
2779
  });
@@ -2724,6 +2781,7 @@ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, sp
2724
2781
  try {
2725
2782
  return await pResult;
2726
2783
  } catch (cause) {
2784
+ if (util.isAbortError(cause)) throw cause;
2727
2785
  throw new Error(`Failed to fetch metadataRpc for network ${networkId}`, {
2728
2786
  cause
2729
2787
  });
@@ -2731,9 +2789,10 @@ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, sp
2731
2789
  CACHE.delete(networkId);
2732
2790
  }
2733
2791
  };
2792
+ const DotBalanceModuleTypeSchema = z__default.default.keyof(chaindataProvider.DotNetworkBalancesConfigSchema);
2734
2793
  const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, specVersion, signal) => {
2735
2794
  const start = performance.now();
2736
- log.debug("[miniMetadata] fetching minimetadatas for %s", chainId);
2795
+ log.info("[miniMetadata] fetching minimetadatas for %s", chainId);
2737
2796
  try {
2738
2797
  const metadataRpc = await getMetadataRpc(chainConnector, chainId);
2739
2798
  signal?.throwIfAborted();
@@ -2744,10 +2803,14 @@ const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, sp
2744
2803
  const modules = defaultBalanceModules.map(mod => mod({
2745
2804
  chainConnectors,
2746
2805
  chaindataProvider
2747
- })).filter(mod => mod.type.startsWith("substrate-"));
2806
+ })).filter(mod => DotBalanceModuleTypeSchema.safeParse(mod.type).success);
2748
2807
  return Promise.all(modules.map(async mod => {
2749
2808
  const source = mod.type;
2750
- const chainMeta = await mod.fetchSubstrateChainMeta(chainId, {}, metadataRpc);
2809
+ const chain = await chaindataProvider.chainById(chainId);
2810
+ const balancesConfig = chain?.balancesConfig?.[mod.type];
2811
+ const chainMeta = await mod.fetchSubstrateChainMeta(chainId, balancesConfig,
2812
+ // TODO fix typings
2813
+ metadataRpc);
2751
2814
  return {
2752
2815
  id: deriveMiniMetadataId({
2753
2816
  source,
@@ -3073,10 +3136,23 @@ const decompress = data => {
3073
3136
  };
3074
3137
 
3075
3138
  const moduleType$4 = "substrate-assets";
3139
+ const SubAssetsTokenConfigSchema = TokenConfigBaseSchema.extend({
3140
+ assetId: chaindataProvider.SubAssetsTokenSchema.shape.assetId,
3141
+ existentialDeposit: chaindataProvider.SubAssetsTokenSchema.shape.existentialDeposit
3142
+ });
3076
3143
  const UNSUPPORTED_CHAIN_META$3 = {
3077
3144
  miniMetadata: null,
3078
3145
  extra: null
3079
3146
  };
3147
+
3148
+ // {
3149
+ // tokens?: Array<
3150
+ // {
3151
+ // assetId: number | string
3152
+ // } & BalancesConfigTokenParams
3153
+ // >
3154
+ // }
3155
+
3080
3156
  const SubAssetsModule = hydrate => {
3081
3157
  const {
3082
3158
  chainConnectors,
@@ -3100,8 +3176,8 @@ const SubAssetsModule = hydrate => {
3100
3176
  extra: null
3101
3177
  };
3102
3178
  },
3103
- async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
3104
- if ((moduleConfig?.tokens ?? []).length < 1) return {};
3179
+ async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
3180
+ if (!tokens?.length) return {};
3105
3181
  const {
3106
3182
  miniMetadata
3107
3183
  } = chainMeta;
@@ -3110,8 +3186,8 @@ const SubAssetsModule = hydrate => {
3110
3186
  const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
3111
3187
  const assetCoder = scaleBuilder.buildStorage("Assets", "Asset");
3112
3188
  const metadataCoder = scaleBuilder.buildStorage("Assets", "Metadata");
3113
- const tokens = {};
3114
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
3189
+ const tokenList = {};
3190
+ for (const tokenConfig of tokens ?? []) {
3115
3191
  try {
3116
3192
  const assetId = String(tokenConfig.assetId);
3117
3193
  const assetStateKey = tryEncode(assetCoder, BigInt(assetId)) ?? tryEncode(assetCoder, assetId);
@@ -3143,13 +3219,13 @@ const SubAssetsModule = hydrate => {
3143
3219
  }
3144
3220
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
3145
3221
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
3146
- tokens[token.id] = token;
3222
+ tokenList[token.id] = token;
3147
3223
  } catch (error) {
3148
3224
  log.error(`Failed to build substrate-assets token ${tokenConfig.assetId} (${tokenConfig.symbol}) on ${chainId}`, error);
3149
3225
  continue;
3150
3226
  }
3151
3227
  }
3152
- return tokens;
3228
+ return tokenList;
3153
3229
  },
3154
3230
  // TODO: Don't create empty subscriptions
3155
3231
  async subscribeBalances({
@@ -3179,10 +3255,10 @@ const SubAssetsModule = hydrate => {
3179
3255
  }
3180
3256
  }));
3181
3257
  return () => {
3182
- controller.abort();
3183
3258
  pUnsubs.then(unsubs => {
3184
3259
  unsubs.forEach(unsubscribe => unsubscribe());
3185
3260
  });
3261
+ controller.abort();
3186
3262
  };
3187
3263
  },
3188
3264
  async fetchBalances(addressesByToken) {
@@ -3366,6 +3442,19 @@ const UNSUPPORTED_CHAIN_META$2 = {
3366
3442
  miniMetadata: null,
3367
3443
  extra: null
3368
3444
  };
3445
+ const SubForeignAssetsTokenConfigSchema = TokenConfigBaseSchema.extend({
3446
+ onChainId: chaindataProvider.SubForeignAssetsTokenSchema.shape.onChainId,
3447
+ existentialDeposit: chaindataProvider.SubForeignAssetsTokenSchema.shape.existentialDeposit
3448
+ });
3449
+
3450
+ // {
3451
+ // tokens?: Array<
3452
+ // {
3453
+ // onChainId: string
3454
+ // } & BalancesConfigTokenParams
3455
+ // >
3456
+ // }
3457
+
3369
3458
  const SubForeignAssetsModule = hydrate => {
3370
3459
  const {
3371
3460
  chainConnectors,
@@ -3390,8 +3479,8 @@ const SubForeignAssetsModule = hydrate => {
3390
3479
  extra: null
3391
3480
  };
3392
3481
  },
3393
- async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
3394
- if ((moduleConfig?.tokens ?? []).length < 1) return {};
3482
+ async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
3483
+ if (!tokens?.length) return {};
3395
3484
  const {
3396
3485
  miniMetadata
3397
3486
  } = chainMeta;
@@ -3401,8 +3490,8 @@ const SubForeignAssetsModule = hydrate => {
3401
3490
  const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(unifiedMetadata));
3402
3491
  const assetCoder = scaleBuilder.buildStorage("ForeignAssets", "Asset");
3403
3492
  const metadataCoder = scaleBuilder.buildStorage("ForeignAssets", "Metadata");
3404
- const tokens = {};
3405
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
3493
+ const tokenList = {};
3494
+ for (const tokenConfig of tokens ?? []) {
3406
3495
  try {
3407
3496
  const onChainId = (() => {
3408
3497
  try {
@@ -3437,13 +3526,13 @@ const SubForeignAssetsModule = hydrate => {
3437
3526
  };
3438
3527
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
3439
3528
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
3440
- tokens[token.id] = token;
3529
+ tokenList[token.id] = token;
3441
3530
  } catch (error) {
3442
3531
  log.error(`Failed to build substrate-foreignassets token ${tokenConfig.onChainId} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
3443
3532
  continue;
3444
3533
  }
3445
3534
  }
3446
- return tokens;
3535
+ return tokenList;
3447
3536
  },
3448
3537
  // TODO: Don't create empty subscriptions
3449
3538
  async subscribeBalances({
@@ -3472,10 +3561,10 @@ const SubForeignAssetsModule = hydrate => {
3472
3561
  }
3473
3562
  }));
3474
3563
  return () => {
3475
- controller.abort();
3476
3564
  pUnsubs.then(unsubs => {
3477
3565
  unsubs.forEach(unsubscribe => unsubscribe());
3478
3566
  });
3567
+ controller.abort();
3479
3568
  };
3480
3569
  },
3481
3570
  async fetchBalances(addressesByToken) {
@@ -3662,263 +3751,6 @@ const asObservable = handler => (...args) => new rxjs.Observable(subscriber => {
3662
3751
  return unsubscribe;
3663
3752
  });
3664
3753
 
3665
- /**
3666
- * Crowdloan contributions are stored in the `childstate` key returned by this function.
3667
- */
3668
- const crowdloanFundContributionsChildKey = fundIndex => util$1.u8aToHex(util$1.u8aConcat(":child_storage:default:", utilCrypto.blake2AsU8a(util$1.u8aConcat("crowdloan", scaleTs.u32.enc(fundIndex)))));
3669
-
3670
- // TODO make this method chain-specific
3671
- async function subscribeCrowdloans(chaindataProvider$1, chainConnector, addressesByToken, callback, signal) {
3672
- const allChains = await chaindataProvider$1.chainsById();
3673
- const tokens = await chaindataProvider$1.tokensById();
3674
-
3675
- // there should be only one network here when subscribing to balances, we've split it up by network at the top level
3676
- const networkIds = lodash.keys(addressesByToken).map(tokenId => chaindataProvider.parseTokenId(tokenId).networkId);
3677
- const miniMetadatas = new Map();
3678
- for (const networkId of networkIds) miniMetadatas.set(networkId, await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native"));
3679
- const crowdloanTokenIds = Object.entries(tokens).filter(([, token]) => {
3680
- // ignore non-native tokens
3681
- if (token.type !== "substrate-native") return;
3682
- // ignore tokens on chains with no crowdloans pallet
3683
- const miniMetadata = miniMetadatas.get(token.networkId);
3684
- return typeof miniMetadata?.extra?.crowdloanPalletId === "string";
3685
- }).map(([tokenId]) => tokenId);
3686
-
3687
- // crowdloan contributions can only be done by the native token on chains with the crowdloan pallet
3688
- const addressesByCrowdloanToken = Object.fromEntries(Object.entries(addressesByToken)
3689
- // remove ethereum addresses
3690
- .map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !util.isEthereumAddress(address))])
3691
- // remove tokens which aren't crowdloan tokens
3692
- .filter(([tokenId]) => crowdloanTokenIds.includes(tokenId)));
3693
- const uniqueChainIds = getUniqueChainIds(addressesByCrowdloanToken, tokens);
3694
- const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
3695
- const chainStorageCoders = buildStorageCoders({
3696
- chainIds: uniqueChainIds,
3697
- chains,
3698
- miniMetadatas,
3699
- coders: {
3700
- parachains: ["Paras", "Parachains"],
3701
- funds: ["Crowdloan", "Funds"]
3702
- }
3703
- });
3704
- const tokenSubscriptions = [];
3705
- for (const [tokenId, addresses] of Object.entries(addressesByCrowdloanToken)) {
3706
- const token = tokens[tokenId];
3707
- if (!token) {
3708
- log.warn(`Token ${tokenId} not found`);
3709
- continue;
3710
- }
3711
- if (token.type !== "substrate-native") {
3712
- log.debug(`This module doesn't handle tokens of type ${token.type}`);
3713
- continue;
3714
- }
3715
- const chainId = token.networkId;
3716
- if (!chainId) {
3717
- log.warn(`Token ${tokenId} has no chain`);
3718
- continue;
3719
- }
3720
- const chain = chains[chainId];
3721
- if (!chain) {
3722
- log.warn(`Chain ${chainId} for token ${tokenId} not found`);
3723
- continue;
3724
- }
3725
- const subscribeParaIds = callback => {
3726
- const scaleCoder = chainStorageCoders.get(chainId)?.parachains;
3727
- const queries = [0].flatMap(() => {
3728
- const stateKey = scale.encodeStateKey(scaleCoder);
3729
- if (!stateKey) return [];
3730
- const decodeResult = change => {
3731
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3732
-
3733
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode parachains on chain ${chainId}`);
3734
- const paraIds = decoded ?? [];
3735
- return paraIds;
3736
- };
3737
- return {
3738
- chainId,
3739
- stateKey,
3740
- decodeResult
3741
- };
3742
- });
3743
- const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
3744
- return () => subscription.then(unsubscribe => unsubscribe());
3745
- };
3746
- const subscribeParaFundIndexes = (paraIds, callback) => {
3747
- const scaleCoder = chainStorageCoders.get(chainId)?.funds;
3748
- const queries = paraIds.flatMap(paraId => {
3749
- const stateKey = scale.encodeStateKey(scaleCoder, `Invalid paraId in ${chainId} funds query ${paraId}`, paraId);
3750
- if (!stateKey) return [];
3751
- const decodeResult = change => {
3752
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3753
-
3754
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode paras on chain ${chainId}`);
3755
- const firstPeriod = decoded?.first_period?.toString?.() ?? "";
3756
- const lastPeriod = decoded?.last_period?.toString?.() ?? "";
3757
- const fundPeriod = `${firstPeriod}-${lastPeriod}`;
3758
- const fundIndex = decoded?.fund_index ?? decoded?.trie_index;
3759
- return {
3760
- paraId,
3761
- fundPeriod,
3762
- fundIndex
3763
- };
3764
- };
3765
- return {
3766
- chainId,
3767
- stateKey,
3768
- decodeResult
3769
- };
3770
- });
3771
- const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
3772
- return () => subscription.then(unsubscribe => unsubscribe());
3773
- };
3774
- const subscribeFundContributions = (funds, addresses, callback) => {
3775
- // TODO: Watch system_events in order to subscribe to changes, then redo the contributions query when changes are detected:
3776
- // https://github.com/polkadot-js/api/blob/8fe02a14345b57e6abb8f7f2c2b624cf70c51b23/packages/api-derive/src/crowdloan/ownContributions.ts#L32-L47
3777
- //
3778
- // For now we just re-fetch all contributions on a timer and then only send them to the subscription callback when they have changed
3779
-
3780
- const queries = funds.map(({
3781
- paraId,
3782
- fundIndex
3783
- }) => ({
3784
- paraId,
3785
- fundIndex,
3786
- addresses,
3787
- childKey: crowdloanFundContributionsChildKey(fundIndex),
3788
- storageKeys: addresses.map(address => util$1.u8aToHex(util.decodeAnyAddress(address)))
3789
- }));
3790
-
3791
- // track whether our caller is still subscribed
3792
- let subscriptionActive = true;
3793
- let previousContributions = null;
3794
- const fetchContributions = async () => {
3795
- try {
3796
- const results = await Promise.all(queries.map(async ({
3797
- paraId,
3798
- fundIndex,
3799
- addresses,
3800
- childKey,
3801
- storageKeys
3802
- }) => ({
3803
- paraId,
3804
- fundIndex,
3805
- addresses,
3806
- result: await chainConnector.send(chainId, "childstate_getStorageEntries", [childKey, storageKeys])
3807
- })));
3808
- const contributions = results.flatMap(queryResult => {
3809
- const {
3810
- paraId,
3811
- fundIndex,
3812
- addresses,
3813
- result
3814
- } = queryResult;
3815
- return (Array.isArray(result) ? result : []).flatMap((encoded, index) => {
3816
- const amount = (() => {
3817
- try {
3818
- return typeof encoded === "string" ? scaleTs.u128.dec(encoded) ?? 0n : 0n;
3819
- } catch {
3820
- return 0n;
3821
- }
3822
- })().toString();
3823
- return {
3824
- paraId,
3825
- fundIndex,
3826
- address: addresses[index],
3827
- amount
3828
- };
3829
- });
3830
- });
3831
-
3832
- // ignore these results if our caller has tried to close this subscription
3833
- if (!subscriptionActive) return;
3834
-
3835
- // ignore these results if they're the same as what we previously fetched
3836
- if (isEqual__default.default(previousContributions, contributions)) return;
3837
- previousContributions = contributions;
3838
- callback(null, contributions);
3839
- } catch (error) {
3840
- callback(error);
3841
- }
3842
- };
3843
-
3844
- // set up polling for contributions
3845
- const crowdloanContributionsPollInterval = 60_000; // 60_000ms === 1 minute
3846
- const pollContributions = async () => {
3847
- if (!subscriptionActive) return;
3848
- try {
3849
- await fetchContributions();
3850
- } catch (error) {
3851
- // log any errors, but don't cancel the poll for contributions when one fetch fails
3852
- log.error(error);
3853
- }
3854
- if (!subscriptionActive) return;
3855
- setTimeout(pollContributions, crowdloanContributionsPollInterval);
3856
- };
3857
-
3858
- // start polling
3859
- pollContributions();
3860
- return () => {
3861
- // stop polling
3862
- subscriptionActive = false;
3863
- };
3864
- };
3865
- const paraIds$ = asObservable(subscribeParaIds)().pipe(rxjs.scan((_, next) => Array.from(new Set(next.flatMap(paraIds => paraIds))), []), rxjs.share());
3866
- const fundIndexesByParaId$ = paraIds$.pipe(rxjs.map(paraIds => asObservable(subscribeParaFundIndexes)(paraIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
3867
- for (const fund of next) {
3868
- const {
3869
- paraId,
3870
- fundIndex
3871
- } = fund;
3872
- if (typeof fundIndex === "number") {
3873
- state.set(paraId, (state.get(paraId) ?? new Set()).add(fundIndex));
3874
- }
3875
- }
3876
- return state;
3877
- }, new Map()));
3878
- const contributionsByAddress$ = fundIndexesByParaId$.pipe(rxjs.map(fundIndexesByParaId => Array.from(fundIndexesByParaId).flatMap(([paraId, fundIndexes]) => Array.from(fundIndexes).map(fundIndex => ({
3879
- paraId,
3880
- fundIndex
3881
- })))), rxjs.map(funds => asObservable(subscribeFundContributions)(funds, addresses)), rxjs.switchAll(), rxjs.scan((state, next) => {
3882
- for (const contribution of next) {
3883
- const {
3884
- address
3885
- } = contribution;
3886
- state.set(address, (state.get(address) ?? new Set()).add(contribution));
3887
- }
3888
- return state;
3889
- }, new Map()));
3890
- const subscription = contributionsByAddress$.subscribe({
3891
- next: contributionsByAddress => {
3892
- const balances = Array.from(contributionsByAddress).map(([address, contributions]) => {
3893
- return {
3894
- source: "substrate-native",
3895
- status: "live",
3896
- address,
3897
- networkId: chainId,
3898
- tokenId,
3899
- values: Array.from(contributions).map(({
3900
- amount,
3901
- paraId
3902
- }) => ({
3903
- type: "crowdloan",
3904
- label: "crowdloan",
3905
- source: "crowdloan",
3906
- amount: amount,
3907
- meta: {
3908
- paraId
3909
- }
3910
- }))
3911
- };
3912
- });
3913
- if (balances.length > 0) callback(null, balances);
3914
- },
3915
- error: error => callback(error)
3916
- });
3917
- tokenSubscriptions.push(() => subscription.unsubscribe());
3918
- }
3919
- return () => tokenSubscriptions.forEach(unsub => unsub());
3920
- }
3921
-
3922
3754
  /**
3923
3755
  * Each nominationPool in the nominationPools pallet has access to some accountIds which have no
3924
3756
  * associated private key. Instead, they are derived from this function.
@@ -3950,6 +3782,7 @@ async function subscribeNompoolStaking(chaindataProvider$1, chainConnector, addr
3950
3782
  const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native");
3951
3783
  miniMetadatas.set(networkId, miniMetadata);
3952
3784
  }
3785
+ signal?.throwIfAborted();
3953
3786
  const nomPoolTokenIds = Object.entries(tokens).filter(([, token]) => {
3954
3787
  // ignore non-native tokens
3955
3788
  if (token.type !== "substrate-native") return false;
@@ -4267,9 +4100,10 @@ async function subscribeSubtensorStaking(chaindataProvider$1, chainConnector, ad
4267
4100
  const networkIds = lodash.keys(addressesByToken).map(tokenId => chaindataProvider.parseTokenId(tokenId).networkId);
4268
4101
  const miniMetadatas = new Map();
4269
4102
  for (const networkId of networkIds) {
4270
- const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native");
4103
+ const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native", signal);
4271
4104
  miniMetadatas.set(networkId, miniMetadata);
4272
4105
  }
4106
+ signal?.throwIfAborted();
4273
4107
  const subtensorTokenIds = Object.entries(tokens).filter(([, token]) => {
4274
4108
  // ignore non-native tokens
4275
4109
  if (token.type !== "substrate-native") return false;
@@ -4475,7 +4309,9 @@ async function subscribeSubtensorStaking(chaindataProvider$1, chainConnector, ad
4475
4309
  });
4476
4310
 
4477
4311
  // use the abortController to tear the subscription down when we don't need it anymore
4478
- abortController.signal.onabort = () => subscription.unsubscribe();
4312
+ abortController.signal.addEventListener("abort", () => {
4313
+ subscription.unsubscribe();
4314
+ });
4479
4315
  }
4480
4316
  return () => abortController.abort();
4481
4317
  }
@@ -4561,6 +4397,10 @@ const getLockTitle = (lock, {
4561
4397
 
4562
4398
  const moduleType$2 = "substrate-native";
4563
4399
 
4400
+ // {
4401
+ // disable?: boolean
4402
+ // } & BalancesConfigTokenParams
4403
+
4564
4404
  /**
4565
4405
  * Function to merge two 'sub sources' of the same balance together, or
4566
4406
  * two instances of the same balance with different values.
@@ -5038,6 +4878,7 @@ const UNSUPPORTED_CHAIN_META$1 = {
5038
4878
  miniMetadata: null,
5039
4879
  extra: null
5040
4880
  };
4881
+ const SubNativeTokenConfigSchema = TokenConfigBaseSchema;
5041
4882
  const SubNativeModule = hydrate => {
5042
4883
  const {
5043
4884
  chainConnectors,
@@ -5053,7 +4894,7 @@ const SubNativeModule = hydrate => {
5053
4894
  // subscribeBalances was split by network to prevent all subs to wait for all minimetadatas to be ready.
5054
4895
  // however the multichain logic in there is so deep in the function below that i had to keep it as-is, and call it by per-network chunks
5055
4896
  // TODO refactor this be actually network specific
5056
- const subscribeChainBalances = async (chainId, opts, callback) => {
4897
+ const subscribeChainBalances = (chainId, opts, callback, signal) => {
5057
4898
  const {
5058
4899
  addressesByToken,
5059
4900
  initialBalances
@@ -5152,13 +4993,19 @@ const SubNativeModule = hydrate => {
5152
4993
  return rxjs.from(queryCache.getQueries(newAddressesByToken)).pipe(rxjs.switchMap(baseQueries => {
5153
4994
  return new rxjs.Observable(subscriber => {
5154
4995
  if (!chainConnectors.substrate) return;
5155
- const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"));
5156
- const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"));
5157
- const unsubCrowdloans = subscribeCrowdloans(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("crowdloan"));
4996
+ const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"), signal);
4997
+ const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"), signal);
4998
+ // const unsubCrowdloans = subscribeCrowdloans(
4999
+ // chaindataProvider,
5000
+ // chainConnectors.substrate,
5001
+ // newAddressesByToken,
5002
+ // handleUpdateForSource("crowdloan"),
5003
+ // signal,
5004
+ // )
5158
5005
  const unsubBase = subscribeBase(baseQueries, chainConnectors.substrate, handleUpdateForSource("base"));
5159
5006
  subscriber.add(async () => (await unsubSubtensorStaking)());
5160
5007
  subscriber.add(async () => (await unsubNompoolStaking)());
5161
- subscriber.add(async () => (await unsubCrowdloans)());
5008
+ // subscriber.add(async () => (await unsubCrowdloans)())
5162
5009
  subscriber.add(async () => (await unsubBase)());
5163
5010
  });
5164
5011
  }));
@@ -5190,8 +5037,13 @@ const SubNativeModule = hydrate => {
5190
5037
  const nonCurrentTokens = Object.keys(addressesByToken).filter(tokenId => !currentTokens.has(tokenId)).sort(sortChains);
5191
5038
 
5192
5039
  // break nonCurrentTokens into chunks of POLLING_WINDOW_SIZE
5193
- await PromisePool__default.default.withConcurrency(POLLING_WINDOW_SIZE).for(nonCurrentTokens).process(async nonCurrentTokenId => await poll({
5040
+ const pool = new PQueue__default.default({
5041
+ concurrency: POLLING_WINDOW_SIZE
5042
+ });
5043
+ nonCurrentTokens.forEach(nonCurrentTokenId => pool.add(() => poll({
5194
5044
  [nonCurrentTokenId]: addressesByToken[nonCurrentTokenId]
5045
+ }), {
5046
+ signal
5195
5047
  }));
5196
5048
 
5197
5049
  // now poll every 30s on chains which are not subscriptionTokens
@@ -5204,7 +5056,9 @@ const SubNativeModule = hydrate => {
5204
5056
  Object.keys(addressesByToken).filter(tokenId => !subscribedTokenIds.includes(tokenId))), rxjs.exhaustMap(tokenIds => rxjs.from(util$1.arrayChunk(tokenIds, POLLING_WINDOW_SIZE)).pipe(rxjs.concatMap(async tokenChunk => {
5205
5057
  // tokenChunk is a chunk of tokenIds with size POLLING_WINDOW_SIZE
5206
5058
  const pollingTokenAddresses = Object.fromEntries(tokenChunk.map(tokenId => [tokenId, addressesByToken[tokenId]]));
5207
- await poll(pollingTokenAddresses);
5059
+ await pool.add(() => poll(pollingTokenAddresses), {
5060
+ signal
5061
+ });
5208
5062
  return true;
5209
5063
  })))).subscribe();
5210
5064
  return () => {
@@ -5249,7 +5103,7 @@ const SubNativeModule = hydrate => {
5249
5103
  };
5250
5104
  const existentialDeposit = getConstantValue("Balances", "ExistentialDeposit")?.toString();
5251
5105
  const nominationPoolsPalletId = getConstantValue("NominationPools", "PalletId")?.asText();
5252
- const crowdloanPalletId = getConstantValue("Crowdloan", "PalletId")?.asText();
5106
+ const crowdloanPalletId = getConstantValue("Crowdloan", "PalletId")?.asText(); // TODO yeet
5253
5107
  const hasSubtensorPallet = getConstantValue("SubtensorModule", "KeySwapCost") !== undefined;
5254
5108
 
5255
5109
  //
@@ -5272,10 +5126,13 @@ const SubNativeModule = hydrate => {
5272
5126
  }, {
5273
5127
  pallet: "Crowdloan",
5274
5128
  items: ["Funds"]
5275
- }, {
5129
+ },
5130
+ // TODO yeet
5131
+ {
5276
5132
  pallet: "Paras",
5277
5133
  items: ["Parachains"]
5278
5134
  },
5135
+ // TODO yeet
5279
5136
  // TotalColdkeyStake is used until v.2.2.1, then it is replaced by StakingHotkeys+Stake
5280
5137
  // Need to keep TotalColdkeyStake for a while so chaindata keeps including it in miniMetadatas, so it doesnt break old versions of the wallet
5281
5138
  {
@@ -5318,22 +5175,24 @@ const SubNativeModule = hydrate => {
5318
5175
  const {
5319
5176
  existentialDeposit
5320
5177
  } = chainMeta.extra ?? {};
5178
+ if (existentialDeposit === undefined) log.warn("Substrate native module: existentialDeposit is undefined for %s, using 0", chainId);
5321
5179
  const id = chaindataProvider.subNativeTokenId(chainId);
5322
5180
  const nativeToken = {
5323
5181
  id,
5324
5182
  type: "substrate-native",
5325
5183
  platform: "polkadot",
5326
- isDefault: moduleConfig?.isDefault ?? true,
5184
+ isDefault: true,
5327
5185
  symbol: symbol,
5328
- name: moduleConfig?.name ?? symbol,
5186
+ name: symbol,
5329
5187
  decimals: decimals,
5330
- logo: moduleConfig?.logo,
5331
5188
  existentialDeposit: existentialDeposit ?? "0",
5332
5189
  networkId: chainId
5333
5190
  };
5334
- if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol;
5335
- if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId;
5336
- if (moduleConfig?.mirrorOf) nativeToken.mirrorOf = moduleConfig?.mirrorOf;
5191
+
5192
+ // if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol
5193
+ // if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId
5194
+ // if (moduleConfig?.mirrorOf) nativeToken.mirrorOf = moduleConfig?.mirrorOf
5195
+
5337
5196
  return {
5338
5197
  [nativeToken.id]: nativeToken
5339
5198
  };
@@ -5365,11 +5224,11 @@ const SubNativeModule = hydrate => {
5365
5224
  return subscribeChainBalances(networkId, {
5366
5225
  addressesByToken: addressesByTokenByNetwork[networkId] ?? {},
5367
5226
  initialBalances: initialBalancesByNetwork[networkId] ?? []
5368
- }, safeCallback);
5227
+ }, safeCallback, controller.signal);
5369
5228
  }));
5370
5229
  return () => {
5371
- controller.abort();
5372
5230
  unsubsribeFns.then(fns => fns.forEach(unsubscribe => unsubscribe()));
5231
+ controller.abort();
5373
5232
  };
5374
5233
  },
5375
5234
  fetchBalances,
@@ -6587,6 +6446,22 @@ var psp22Abi = {
6587
6446
  };
6588
6447
 
6589
6448
  const moduleType$1 = "substrate-psp22";
6449
+ const SubPsp22TokenConfigSchema = TokenConfigBaseSchema.extend({
6450
+ contractAddress: chaindataProvider.SubPsp22TokenSchema.shape.contractAddress,
6451
+ existentialDeposit: chaindataProvider.SubPsp22TokenSchema.shape.existentialDeposit.optional()
6452
+ });
6453
+
6454
+ // {
6455
+ // tokens?: Array<
6456
+ // {
6457
+ // symbol?: string
6458
+ // decimals?: number
6459
+ // ed?: string
6460
+ // contractAddress: string
6461
+ // } & BalancesConfigTokenParams
6462
+ // >
6463
+ // }
6464
+
6590
6465
  const SubPsp22Module = hydrate => {
6591
6466
  const {
6592
6467
  chainConnectors,
@@ -6603,7 +6478,8 @@ const SubPsp22Module = hydrate => {
6603
6478
  extra: null
6604
6479
  };
6605
6480
  },
6606
- async fetchSubstrateChainTokens(chainId, _chainMeta, moduleConfig) {
6481
+ async fetchSubstrateChainTokens(chainId, _chainMeta, moduleConfig, tokens) {
6482
+ if (!tokens?.length) return {};
6607
6483
  // const { isTestnet } = chainMeta
6608
6484
 
6609
6485
  const registry = new types.TypeRegistry();
@@ -6615,12 +6491,12 @@ const SubPsp22Module = hydrate => {
6615
6491
  chainId,
6616
6492
  registry
6617
6493
  });
6618
- const tokens = {};
6619
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
6494
+ const tokenList = {};
6495
+ for (const tokenConfig of tokens ?? []) {
6620
6496
  try {
6621
6497
  let symbol = tokenConfig?.symbol ?? "Unit";
6622
6498
  let decimals = tokenConfig?.decimals ?? 0;
6623
- const existentialDeposit = tokenConfig?.ed ?? "0";
6499
+ const existentialDeposit = tokenConfig?.existentialDeposit ?? "0";
6624
6500
  const contractAddress = tokenConfig?.contractAddress ?? undefined;
6625
6501
  if (contractAddress === undefined) continue;
6626
6502
  await (async () => {
@@ -6653,13 +6529,13 @@ const SubPsp22Module = hydrate => {
6653
6529
  };
6654
6530
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
6655
6531
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
6656
- tokens[token.id] = token;
6532
+ tokenList[token.id] = token;
6657
6533
  } catch (error) {
6658
6534
  log.error(`Failed to build substrate-psp22 token ${tokenConfig.contractAddress} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
6659
6535
  continue;
6660
6536
  }
6661
6537
  }
6662
- return tokens;
6538
+ return tokenList;
6663
6539
  },
6664
6540
  // TODO: Don't create empty subscriptions
6665
6541
  async subscribeBalances({
@@ -6834,11 +6710,28 @@ const fetchBalances = async (chainConnector, tokens, addressesByToken) => {
6834
6710
  };
6835
6711
 
6836
6712
  const moduleType = "substrate-tokens";
6713
+ const SubTokensTokenConfigSchema = TokenConfigBaseSchema.extend({
6714
+ onChainId: chaindataProvider.SubTokensTokenSchema.shape.onChainId,
6715
+ existentialDeposit: chaindataProvider.SubTokensTokenSchema.shape.existentialDeposit
6716
+ });
6837
6717
  const defaultPalletId = "Tokens";
6838
6718
  const UNSUPPORTED_CHAIN_META = {
6839
6719
  miniMetadata: null,
6840
6720
  extra: {}
6841
6721
  };
6722
+
6723
+ // {
6724
+ // palletId?: string // TODO unlikely it will ever be used - remove this ?
6725
+ // tokens?: Array<
6726
+ // {
6727
+ // symbol?: string
6728
+ // decimals?: number
6729
+ // ed?: string
6730
+ // onChainId?: string | number
6731
+ // } & BalancesConfigTokenParams
6732
+ // >
6733
+ // }
6734
+
6842
6735
  const SubTokensModule = hydrate => {
6843
6736
  const {
6844
6737
  chainConnectors,
@@ -6864,13 +6757,14 @@ const SubTokensModule = hydrate => {
6864
6757
  }
6865
6758
  };
6866
6759
  },
6867
- async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
6868
- const tokens = {};
6869
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
6760
+ async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
6761
+ const tokenList = {};
6762
+ for (const tokenConfig of tokens ?? []) {
6870
6763
  try {
6764
+ // TODO fetch metadata from chain, like we do for assets
6871
6765
  const symbol = tokenConfig?.symbol ?? "Unit";
6872
6766
  const decimals = tokenConfig?.decimals ?? 0;
6873
- const existentialDeposit = tokenConfig?.ed ?? "0";
6767
+ const existentialDeposit = tokenConfig?.existentialDeposit ?? "0";
6874
6768
  const onChainId = tokenConfig?.onChainId ?? undefined;
6875
6769
  if (onChainId === undefined) continue;
6876
6770
  const id = chaindataProvider.subTokensTokenId(chainId, onChainId);
@@ -6889,13 +6783,13 @@ const SubTokensModule = hydrate => {
6889
6783
  };
6890
6784
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
6891
6785
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
6892
- tokens[token.id] = token;
6786
+ tokenList[token.id] = token;
6893
6787
  } catch (error) {
6894
6788
  log.error(`Failed to build substrate-tokens token ${tokenConfig.onChainId} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
6895
6789
  continue;
6896
6790
  }
6897
6791
  }
6898
- return tokens;
6792
+ return tokenList;
6899
6793
  },
6900
6794
  // TODO: Don't create empty subscriptions
6901
6795
  async subscribeBalances({
@@ -6925,10 +6819,10 @@ const SubTokensModule = hydrate => {
6925
6819
  }
6926
6820
  }));
6927
6821
  return () => {
6928
- controller.abort();
6929
6822
  pUnsubs.then(unsubs => {
6930
6823
  unsubs.forEach(unsubscribe => unsubscribe());
6931
6824
  });
6825
+ controller.abort();
6932
6826
  };
6933
6827
  },
6934
6828
  async fetchBalances(addressesByToken) {
@@ -7144,9 +7038,12 @@ exports.Balances = Balances;
7144
7038
  exports.Change24hCurrencyFormatter = Change24hCurrencyFormatter;
7145
7039
  exports.DefaultBalanceModule = DefaultBalanceModule;
7146
7040
  exports.EvmErc20Module = EvmErc20Module;
7041
+ exports.EvmErc20TokenConfigSchema = EvmErc20TokenConfigSchema;
7147
7042
  exports.EvmNativeModule = EvmNativeModule;
7043
+ exports.EvmNativeTokenConfigSchema = EvmNativeTokenConfigSchema;
7148
7044
  exports.EvmTokenFetcher = EvmTokenFetcher;
7149
7045
  exports.EvmUniswapV2Module = EvmUniswapV2Module;
7046
+ exports.EvmUniswapV2TokenConfigSchema = EvmUniswapV2TokenConfigSchema;
7150
7047
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
7151
7048
  exports.ONE_ALPHA_TOKEN = ONE_ALPHA_TOKEN;
7152
7049
  exports.PlanckSumBalancesFormatter = PlanckSumBalancesFormatter;
@@ -7155,10 +7052,15 @@ exports.SCALE_FACTOR = SCALE_FACTOR;
7155
7052
  exports.SUBTENSOR_MIN_STAKE_AMOUNT_PLANK = SUBTENSOR_MIN_STAKE_AMOUNT_PLANK;
7156
7053
  exports.SUBTENSOR_ROOT_NETUID = SUBTENSOR_ROOT_NETUID;
7157
7054
  exports.SubAssetsModule = SubAssetsModule;
7055
+ exports.SubAssetsTokenConfigSchema = SubAssetsTokenConfigSchema;
7158
7056
  exports.SubForeignAssetsModule = SubForeignAssetsModule;
7057
+ exports.SubForeignAssetsTokenConfigSchema = SubForeignAssetsTokenConfigSchema;
7159
7058
  exports.SubNativeModule = SubNativeModule;
7059
+ exports.SubNativeTokenConfigSchema = SubNativeTokenConfigSchema;
7160
7060
  exports.SubPsp22Module = SubPsp22Module;
7061
+ exports.SubPsp22TokenConfigSchema = SubPsp22TokenConfigSchema;
7161
7062
  exports.SubTokensModule = SubTokensModule;
7063
+ exports.SubTokensTokenConfigSchema = SubTokensTokenConfigSchema;
7162
7064
  exports.SumBalancesFormatter = SumBalancesFormatter;
7163
7065
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
7164
7066
  exports.abiMulticall = abiMulticall;