@talismn/balances 0.0.0-pr2043-20250619170346 → 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-20250619170346"};
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,7 +2746,9 @@ 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 {
@@ -2718,6 +2772,8 @@ const POOL = new PQueue__default.default({
2718
2772
  });
2719
2773
  const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, specVersion, signal) => {
2720
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
+ );
2721
2777
  const pResult = POOL.add(() => fetchMiniMetadatas(chainConnector, chaindataProvider, networkId, specVersion), {
2722
2778
  signal
2723
2779
  });
@@ -2733,9 +2789,10 @@ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, sp
2733
2789
  CACHE.delete(networkId);
2734
2790
  }
2735
2791
  };
2792
+ const DotBalanceModuleTypeSchema = z__default.default.keyof(chaindataProvider.DotNetworkBalancesConfigSchema);
2736
2793
  const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, specVersion, signal) => {
2737
2794
  const start = performance.now();
2738
- log.debug("[miniMetadata] fetching minimetadatas for %s", chainId);
2795
+ log.info("[miniMetadata] fetching minimetadatas for %s", chainId);
2739
2796
  try {
2740
2797
  const metadataRpc = await getMetadataRpc(chainConnector, chainId);
2741
2798
  signal?.throwIfAborted();
@@ -2746,10 +2803,14 @@ const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, sp
2746
2803
  const modules = defaultBalanceModules.map(mod => mod({
2747
2804
  chainConnectors,
2748
2805
  chaindataProvider
2749
- })).filter(mod => mod.type.startsWith("substrate-"));
2806
+ })).filter(mod => DotBalanceModuleTypeSchema.safeParse(mod.type).success);
2750
2807
  return Promise.all(modules.map(async mod => {
2751
2808
  const source = mod.type;
2752
- 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);
2753
2814
  return {
2754
2815
  id: deriveMiniMetadataId({
2755
2816
  source,
@@ -3075,10 +3136,23 @@ const decompress = data => {
3075
3136
  };
3076
3137
 
3077
3138
  const moduleType$4 = "substrate-assets";
3139
+ const SubAssetsTokenConfigSchema = TokenConfigBaseSchema.extend({
3140
+ assetId: chaindataProvider.SubAssetsTokenSchema.shape.assetId,
3141
+ existentialDeposit: chaindataProvider.SubAssetsTokenSchema.shape.existentialDeposit
3142
+ });
3078
3143
  const UNSUPPORTED_CHAIN_META$3 = {
3079
3144
  miniMetadata: null,
3080
3145
  extra: null
3081
3146
  };
3147
+
3148
+ // {
3149
+ // tokens?: Array<
3150
+ // {
3151
+ // assetId: number | string
3152
+ // } & BalancesConfigTokenParams
3153
+ // >
3154
+ // }
3155
+
3082
3156
  const SubAssetsModule = hydrate => {
3083
3157
  const {
3084
3158
  chainConnectors,
@@ -3102,8 +3176,8 @@ const SubAssetsModule = hydrate => {
3102
3176
  extra: null
3103
3177
  };
3104
3178
  },
3105
- async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
3106
- if ((moduleConfig?.tokens ?? []).length < 1) return {};
3179
+ async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
3180
+ if (!tokens?.length) return {};
3107
3181
  const {
3108
3182
  miniMetadata
3109
3183
  } = chainMeta;
@@ -3112,8 +3186,8 @@ const SubAssetsModule = hydrate => {
3112
3186
  const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
3113
3187
  const assetCoder = scaleBuilder.buildStorage("Assets", "Asset");
3114
3188
  const metadataCoder = scaleBuilder.buildStorage("Assets", "Metadata");
3115
- const tokens = {};
3116
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
3189
+ const tokenList = {};
3190
+ for (const tokenConfig of tokens ?? []) {
3117
3191
  try {
3118
3192
  const assetId = String(tokenConfig.assetId);
3119
3193
  const assetStateKey = tryEncode(assetCoder, BigInt(assetId)) ?? tryEncode(assetCoder, assetId);
@@ -3145,13 +3219,13 @@ const SubAssetsModule = hydrate => {
3145
3219
  }
3146
3220
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
3147
3221
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
3148
- tokens[token.id] = token;
3222
+ tokenList[token.id] = token;
3149
3223
  } catch (error) {
3150
3224
  log.error(`Failed to build substrate-assets token ${tokenConfig.assetId} (${tokenConfig.symbol}) on ${chainId}`, error);
3151
3225
  continue;
3152
3226
  }
3153
3227
  }
3154
- return tokens;
3228
+ return tokenList;
3155
3229
  },
3156
3230
  // TODO: Don't create empty subscriptions
3157
3231
  async subscribeBalances({
@@ -3181,10 +3255,10 @@ const SubAssetsModule = hydrate => {
3181
3255
  }
3182
3256
  }));
3183
3257
  return () => {
3184
- controller.abort();
3185
3258
  pUnsubs.then(unsubs => {
3186
3259
  unsubs.forEach(unsubscribe => unsubscribe());
3187
3260
  });
3261
+ controller.abort();
3188
3262
  };
3189
3263
  },
3190
3264
  async fetchBalances(addressesByToken) {
@@ -3368,6 +3442,19 @@ const UNSUPPORTED_CHAIN_META$2 = {
3368
3442
  miniMetadata: null,
3369
3443
  extra: null
3370
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
+
3371
3458
  const SubForeignAssetsModule = hydrate => {
3372
3459
  const {
3373
3460
  chainConnectors,
@@ -3392,8 +3479,8 @@ const SubForeignAssetsModule = hydrate => {
3392
3479
  extra: null
3393
3480
  };
3394
3481
  },
3395
- async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
3396
- if ((moduleConfig?.tokens ?? []).length < 1) return {};
3482
+ async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
3483
+ if (!tokens?.length) return {};
3397
3484
  const {
3398
3485
  miniMetadata
3399
3486
  } = chainMeta;
@@ -3403,8 +3490,8 @@ const SubForeignAssetsModule = hydrate => {
3403
3490
  const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(unifiedMetadata));
3404
3491
  const assetCoder = scaleBuilder.buildStorage("ForeignAssets", "Asset");
3405
3492
  const metadataCoder = scaleBuilder.buildStorage("ForeignAssets", "Metadata");
3406
- const tokens = {};
3407
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
3493
+ const tokenList = {};
3494
+ for (const tokenConfig of tokens ?? []) {
3408
3495
  try {
3409
3496
  const onChainId = (() => {
3410
3497
  try {
@@ -3439,13 +3526,13 @@ const SubForeignAssetsModule = hydrate => {
3439
3526
  };
3440
3527
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
3441
3528
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
3442
- tokens[token.id] = token;
3529
+ tokenList[token.id] = token;
3443
3530
  } catch (error) {
3444
3531
  log.error(`Failed to build substrate-foreignassets token ${tokenConfig.onChainId} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
3445
3532
  continue;
3446
3533
  }
3447
3534
  }
3448
- return tokens;
3535
+ return tokenList;
3449
3536
  },
3450
3537
  // TODO: Don't create empty subscriptions
3451
3538
  async subscribeBalances({
@@ -3474,10 +3561,10 @@ const SubForeignAssetsModule = hydrate => {
3474
3561
  }
3475
3562
  }));
3476
3563
  return () => {
3477
- controller.abort();
3478
3564
  pUnsubs.then(unsubs => {
3479
3565
  unsubs.forEach(unsubscribe => unsubscribe());
3480
3566
  });
3567
+ controller.abort();
3481
3568
  };
3482
3569
  },
3483
3570
  async fetchBalances(addressesByToken) {
@@ -3664,263 +3751,6 @@ const asObservable = handler => (...args) => new rxjs.Observable(subscriber => {
3664
3751
  return unsubscribe;
3665
3752
  });
3666
3753
 
3667
- /**
3668
- * Crowdloan contributions are stored in the `childstate` key returned by this function.
3669
- */
3670
- const crowdloanFundContributionsChildKey = fundIndex => util$1.u8aToHex(util$1.u8aConcat(":child_storage:default:", utilCrypto.blake2AsU8a(util$1.u8aConcat("crowdloan", scaleTs.u32.enc(fundIndex)))));
3671
-
3672
- // TODO make this method chain-specific
3673
- async function subscribeCrowdloans(chaindataProvider$1, chainConnector, addressesByToken, callback, signal) {
3674
- const allChains = await chaindataProvider$1.chainsById();
3675
- const tokens = await chaindataProvider$1.tokensById();
3676
-
3677
- // there should be only one network here when subscribing to balances, we've split it up by network at the top level
3678
- const networkIds = lodash.keys(addressesByToken).map(tokenId => chaindataProvider.parseTokenId(tokenId).networkId);
3679
- const miniMetadatas = new Map();
3680
- for (const networkId of networkIds) miniMetadatas.set(networkId, await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native"));
3681
- const crowdloanTokenIds = Object.entries(tokens).filter(([, token]) => {
3682
- // ignore non-native tokens
3683
- if (token.type !== "substrate-native") return;
3684
- // ignore tokens on chains with no crowdloans pallet
3685
- const miniMetadata = miniMetadatas.get(token.networkId);
3686
- return typeof miniMetadata?.extra?.crowdloanPalletId === "string";
3687
- }).map(([tokenId]) => tokenId);
3688
-
3689
- // crowdloan contributions can only be done by the native token on chains with the crowdloan pallet
3690
- const addressesByCrowdloanToken = Object.fromEntries(Object.entries(addressesByToken)
3691
- // remove ethereum addresses
3692
- .map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !util.isEthereumAddress(address))])
3693
- // remove tokens which aren't crowdloan tokens
3694
- .filter(([tokenId]) => crowdloanTokenIds.includes(tokenId)));
3695
- const uniqueChainIds = getUniqueChainIds(addressesByCrowdloanToken, tokens);
3696
- const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
3697
- const chainStorageCoders = buildStorageCoders({
3698
- chainIds: uniqueChainIds,
3699
- chains,
3700
- miniMetadatas,
3701
- coders: {
3702
- parachains: ["Paras", "Parachains"],
3703
- funds: ["Crowdloan", "Funds"]
3704
- }
3705
- });
3706
- const tokenSubscriptions = [];
3707
- for (const [tokenId, addresses] of Object.entries(addressesByCrowdloanToken)) {
3708
- const token = tokens[tokenId];
3709
- if (!token) {
3710
- log.warn(`Token ${tokenId} not found`);
3711
- continue;
3712
- }
3713
- if (token.type !== "substrate-native") {
3714
- log.debug(`This module doesn't handle tokens of type ${token.type}`);
3715
- continue;
3716
- }
3717
- const chainId = token.networkId;
3718
- if (!chainId) {
3719
- log.warn(`Token ${tokenId} has no chain`);
3720
- continue;
3721
- }
3722
- const chain = chains[chainId];
3723
- if (!chain) {
3724
- log.warn(`Chain ${chainId} for token ${tokenId} not found`);
3725
- continue;
3726
- }
3727
- const subscribeParaIds = callback => {
3728
- const scaleCoder = chainStorageCoders.get(chainId)?.parachains;
3729
- const queries = [0].flatMap(() => {
3730
- const stateKey = scale.encodeStateKey(scaleCoder);
3731
- if (!stateKey) return [];
3732
- const decodeResult = change => {
3733
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3734
-
3735
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode parachains on chain ${chainId}`);
3736
- const paraIds = decoded ?? [];
3737
- return paraIds;
3738
- };
3739
- return {
3740
- chainId,
3741
- stateKey,
3742
- decodeResult
3743
- };
3744
- });
3745
- const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
3746
- return () => subscription.then(unsubscribe => unsubscribe());
3747
- };
3748
- const subscribeParaFundIndexes = (paraIds, callback) => {
3749
- const scaleCoder = chainStorageCoders.get(chainId)?.funds;
3750
- const queries = paraIds.flatMap(paraId => {
3751
- const stateKey = scale.encodeStateKey(scaleCoder, `Invalid paraId in ${chainId} funds query ${paraId}`, paraId);
3752
- if (!stateKey) return [];
3753
- const decodeResult = change => {
3754
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3755
-
3756
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode paras on chain ${chainId}`);
3757
- const firstPeriod = decoded?.first_period?.toString?.() ?? "";
3758
- const lastPeriod = decoded?.last_period?.toString?.() ?? "";
3759
- const fundPeriod = `${firstPeriod}-${lastPeriod}`;
3760
- const fundIndex = decoded?.fund_index ?? decoded?.trie_index;
3761
- return {
3762
- paraId,
3763
- fundPeriod,
3764
- fundIndex
3765
- };
3766
- };
3767
- return {
3768
- chainId,
3769
- stateKey,
3770
- decodeResult
3771
- };
3772
- });
3773
- const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
3774
- return () => subscription.then(unsubscribe => unsubscribe());
3775
- };
3776
- const subscribeFundContributions = (funds, addresses, callback) => {
3777
- // TODO: Watch system_events in order to subscribe to changes, then redo the contributions query when changes are detected:
3778
- // https://github.com/polkadot-js/api/blob/8fe02a14345b57e6abb8f7f2c2b624cf70c51b23/packages/api-derive/src/crowdloan/ownContributions.ts#L32-L47
3779
- //
3780
- // For now we just re-fetch all contributions on a timer and then only send them to the subscription callback when they have changed
3781
-
3782
- const queries = funds.map(({
3783
- paraId,
3784
- fundIndex
3785
- }) => ({
3786
- paraId,
3787
- fundIndex,
3788
- addresses,
3789
- childKey: crowdloanFundContributionsChildKey(fundIndex),
3790
- storageKeys: addresses.map(address => util$1.u8aToHex(util.decodeAnyAddress(address)))
3791
- }));
3792
-
3793
- // track whether our caller is still subscribed
3794
- let subscriptionActive = true;
3795
- let previousContributions = null;
3796
- const fetchContributions = async () => {
3797
- try {
3798
- const results = await Promise.all(queries.map(async ({
3799
- paraId,
3800
- fundIndex,
3801
- addresses,
3802
- childKey,
3803
- storageKeys
3804
- }) => ({
3805
- paraId,
3806
- fundIndex,
3807
- addresses,
3808
- result: await chainConnector.send(chainId, "childstate_getStorageEntries", [childKey, storageKeys])
3809
- })));
3810
- const contributions = results.flatMap(queryResult => {
3811
- const {
3812
- paraId,
3813
- fundIndex,
3814
- addresses,
3815
- result
3816
- } = queryResult;
3817
- return (Array.isArray(result) ? result : []).flatMap((encoded, index) => {
3818
- const amount = (() => {
3819
- try {
3820
- return typeof encoded === "string" ? scaleTs.u128.dec(encoded) ?? 0n : 0n;
3821
- } catch {
3822
- return 0n;
3823
- }
3824
- })().toString();
3825
- return {
3826
- paraId,
3827
- fundIndex,
3828
- address: addresses[index],
3829
- amount
3830
- };
3831
- });
3832
- });
3833
-
3834
- // ignore these results if our caller has tried to close this subscription
3835
- if (!subscriptionActive) return;
3836
-
3837
- // ignore these results if they're the same as what we previously fetched
3838
- if (isEqual__default.default(previousContributions, contributions)) return;
3839
- previousContributions = contributions;
3840
- callback(null, contributions);
3841
- } catch (error) {
3842
- callback(error);
3843
- }
3844
- };
3845
-
3846
- // set up polling for contributions
3847
- const crowdloanContributionsPollInterval = 60_000; // 60_000ms === 1 minute
3848
- const pollContributions = async () => {
3849
- if (!subscriptionActive) return;
3850
- try {
3851
- await fetchContributions();
3852
- } catch (error) {
3853
- // log any errors, but don't cancel the poll for contributions when one fetch fails
3854
- log.error(error);
3855
- }
3856
- if (!subscriptionActive) return;
3857
- setTimeout(pollContributions, crowdloanContributionsPollInterval);
3858
- };
3859
-
3860
- // start polling
3861
- pollContributions();
3862
- return () => {
3863
- // stop polling
3864
- subscriptionActive = false;
3865
- };
3866
- };
3867
- const paraIds$ = asObservable(subscribeParaIds)().pipe(rxjs.scan((_, next) => Array.from(new Set(next.flatMap(paraIds => paraIds))), []), rxjs.share());
3868
- const fundIndexesByParaId$ = paraIds$.pipe(rxjs.map(paraIds => asObservable(subscribeParaFundIndexes)(paraIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
3869
- for (const fund of next) {
3870
- const {
3871
- paraId,
3872
- fundIndex
3873
- } = fund;
3874
- if (typeof fundIndex === "number") {
3875
- state.set(paraId, (state.get(paraId) ?? new Set()).add(fundIndex));
3876
- }
3877
- }
3878
- return state;
3879
- }, new Map()));
3880
- const contributionsByAddress$ = fundIndexesByParaId$.pipe(rxjs.map(fundIndexesByParaId => Array.from(fundIndexesByParaId).flatMap(([paraId, fundIndexes]) => Array.from(fundIndexes).map(fundIndex => ({
3881
- paraId,
3882
- fundIndex
3883
- })))), rxjs.map(funds => asObservable(subscribeFundContributions)(funds, addresses)), rxjs.switchAll(), rxjs.scan((state, next) => {
3884
- for (const contribution of next) {
3885
- const {
3886
- address
3887
- } = contribution;
3888
- state.set(address, (state.get(address) ?? new Set()).add(contribution));
3889
- }
3890
- return state;
3891
- }, new Map()));
3892
- const subscription = contributionsByAddress$.subscribe({
3893
- next: contributionsByAddress => {
3894
- const balances = Array.from(contributionsByAddress).map(([address, contributions]) => {
3895
- return {
3896
- source: "substrate-native",
3897
- status: "live",
3898
- address,
3899
- networkId: chainId,
3900
- tokenId,
3901
- values: Array.from(contributions).map(({
3902
- amount,
3903
- paraId
3904
- }) => ({
3905
- type: "crowdloan",
3906
- label: "crowdloan",
3907
- source: "crowdloan",
3908
- amount: amount,
3909
- meta: {
3910
- paraId
3911
- }
3912
- }))
3913
- };
3914
- });
3915
- if (balances.length > 0) callback(null, balances);
3916
- },
3917
- error: error => callback(error)
3918
- });
3919
- tokenSubscriptions.push(() => subscription.unsubscribe());
3920
- }
3921
- return () => tokenSubscriptions.forEach(unsub => unsub());
3922
- }
3923
-
3924
3754
  /**
3925
3755
  * Each nominationPool in the nominationPools pallet has access to some accountIds which have no
3926
3756
  * associated private key. Instead, they are derived from this function.
@@ -3952,6 +3782,7 @@ async function subscribeNompoolStaking(chaindataProvider$1, chainConnector, addr
3952
3782
  const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native");
3953
3783
  miniMetadatas.set(networkId, miniMetadata);
3954
3784
  }
3785
+ signal?.throwIfAborted();
3955
3786
  const nomPoolTokenIds = Object.entries(tokens).filter(([, token]) => {
3956
3787
  // ignore non-native tokens
3957
3788
  if (token.type !== "substrate-native") return false;
@@ -4269,9 +4100,10 @@ async function subscribeSubtensorStaking(chaindataProvider$1, chainConnector, ad
4269
4100
  const networkIds = lodash.keys(addressesByToken).map(tokenId => chaindataProvider.parseTokenId(tokenId).networkId);
4270
4101
  const miniMetadatas = new Map();
4271
4102
  for (const networkId of networkIds) {
4272
- const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native");
4103
+ const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native", signal);
4273
4104
  miniMetadatas.set(networkId, miniMetadata);
4274
4105
  }
4106
+ signal?.throwIfAborted();
4275
4107
  const subtensorTokenIds = Object.entries(tokens).filter(([, token]) => {
4276
4108
  // ignore non-native tokens
4277
4109
  if (token.type !== "substrate-native") return false;
@@ -4477,7 +4309,9 @@ async function subscribeSubtensorStaking(chaindataProvider$1, chainConnector, ad
4477
4309
  });
4478
4310
 
4479
4311
  // use the abortController to tear the subscription down when we don't need it anymore
4480
- abortController.signal.onabort = () => subscription.unsubscribe();
4312
+ abortController.signal.addEventListener("abort", () => {
4313
+ subscription.unsubscribe();
4314
+ });
4481
4315
  }
4482
4316
  return () => abortController.abort();
4483
4317
  }
@@ -4563,6 +4397,10 @@ const getLockTitle = (lock, {
4563
4397
 
4564
4398
  const moduleType$2 = "substrate-native";
4565
4399
 
4400
+ // {
4401
+ // disable?: boolean
4402
+ // } & BalancesConfigTokenParams
4403
+
4566
4404
  /**
4567
4405
  * Function to merge two 'sub sources' of the same balance together, or
4568
4406
  * two instances of the same balance with different values.
@@ -5040,6 +4878,7 @@ const UNSUPPORTED_CHAIN_META$1 = {
5040
4878
  miniMetadata: null,
5041
4879
  extra: null
5042
4880
  };
4881
+ const SubNativeTokenConfigSchema = TokenConfigBaseSchema;
5043
4882
  const SubNativeModule = hydrate => {
5044
4883
  const {
5045
4884
  chainConnectors,
@@ -5055,7 +4894,7 @@ const SubNativeModule = hydrate => {
5055
4894
  // subscribeBalances was split by network to prevent all subs to wait for all minimetadatas to be ready.
5056
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
5057
4896
  // TODO refactor this be actually network specific
5058
- const subscribeChainBalances = async (chainId, opts, callback) => {
4897
+ const subscribeChainBalances = (chainId, opts, callback, signal) => {
5059
4898
  const {
5060
4899
  addressesByToken,
5061
4900
  initialBalances
@@ -5154,13 +4993,19 @@ const SubNativeModule = hydrate => {
5154
4993
  return rxjs.from(queryCache.getQueries(newAddressesByToken)).pipe(rxjs.switchMap(baseQueries => {
5155
4994
  return new rxjs.Observable(subscriber => {
5156
4995
  if (!chainConnectors.substrate) return;
5157
- const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"));
5158
- const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"));
5159
- 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
+ // )
5160
5005
  const unsubBase = subscribeBase(baseQueries, chainConnectors.substrate, handleUpdateForSource("base"));
5161
5006
  subscriber.add(async () => (await unsubSubtensorStaking)());
5162
5007
  subscriber.add(async () => (await unsubNompoolStaking)());
5163
- subscriber.add(async () => (await unsubCrowdloans)());
5008
+ // subscriber.add(async () => (await unsubCrowdloans)())
5164
5009
  subscriber.add(async () => (await unsubBase)());
5165
5010
  });
5166
5011
  }));
@@ -5192,8 +5037,13 @@ const SubNativeModule = hydrate => {
5192
5037
  const nonCurrentTokens = Object.keys(addressesByToken).filter(tokenId => !currentTokens.has(tokenId)).sort(sortChains);
5193
5038
 
5194
5039
  // break nonCurrentTokens into chunks of POLLING_WINDOW_SIZE
5195
- 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({
5196
5044
  [nonCurrentTokenId]: addressesByToken[nonCurrentTokenId]
5045
+ }), {
5046
+ signal
5197
5047
  }));
5198
5048
 
5199
5049
  // now poll every 30s on chains which are not subscriptionTokens
@@ -5206,7 +5056,9 @@ const SubNativeModule = hydrate => {
5206
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 => {
5207
5057
  // tokenChunk is a chunk of tokenIds with size POLLING_WINDOW_SIZE
5208
5058
  const pollingTokenAddresses = Object.fromEntries(tokenChunk.map(tokenId => [tokenId, addressesByToken[tokenId]]));
5209
- await poll(pollingTokenAddresses);
5059
+ await pool.add(() => poll(pollingTokenAddresses), {
5060
+ signal
5061
+ });
5210
5062
  return true;
5211
5063
  })))).subscribe();
5212
5064
  return () => {
@@ -5251,7 +5103,7 @@ const SubNativeModule = hydrate => {
5251
5103
  };
5252
5104
  const existentialDeposit = getConstantValue("Balances", "ExistentialDeposit")?.toString();
5253
5105
  const nominationPoolsPalletId = getConstantValue("NominationPools", "PalletId")?.asText();
5254
- const crowdloanPalletId = getConstantValue("Crowdloan", "PalletId")?.asText();
5106
+ const crowdloanPalletId = getConstantValue("Crowdloan", "PalletId")?.asText(); // TODO yeet
5255
5107
  const hasSubtensorPallet = getConstantValue("SubtensorModule", "KeySwapCost") !== undefined;
5256
5108
 
5257
5109
  //
@@ -5274,10 +5126,13 @@ const SubNativeModule = hydrate => {
5274
5126
  }, {
5275
5127
  pallet: "Crowdloan",
5276
5128
  items: ["Funds"]
5277
- }, {
5129
+ },
5130
+ // TODO yeet
5131
+ {
5278
5132
  pallet: "Paras",
5279
5133
  items: ["Parachains"]
5280
5134
  },
5135
+ // TODO yeet
5281
5136
  // TotalColdkeyStake is used until v.2.2.1, then it is replaced by StakingHotkeys+Stake
5282
5137
  // Need to keep TotalColdkeyStake for a while so chaindata keeps including it in miniMetadatas, so it doesnt break old versions of the wallet
5283
5138
  {
@@ -5320,22 +5175,24 @@ const SubNativeModule = hydrate => {
5320
5175
  const {
5321
5176
  existentialDeposit
5322
5177
  } = chainMeta.extra ?? {};
5178
+ if (existentialDeposit === undefined) log.warn("Substrate native module: existentialDeposit is undefined for %s, using 0", chainId);
5323
5179
  const id = chaindataProvider.subNativeTokenId(chainId);
5324
5180
  const nativeToken = {
5325
5181
  id,
5326
5182
  type: "substrate-native",
5327
5183
  platform: "polkadot",
5328
- isDefault: moduleConfig?.isDefault ?? true,
5184
+ isDefault: true,
5329
5185
  symbol: symbol,
5330
- name: moduleConfig?.name ?? symbol,
5186
+ name: symbol,
5331
5187
  decimals: decimals,
5332
- logo: moduleConfig?.logo,
5333
5188
  existentialDeposit: existentialDeposit ?? "0",
5334
5189
  networkId: chainId
5335
5190
  };
5336
- if (moduleConfig?.symbol) nativeToken.symbol = moduleConfig?.symbol;
5337
- if (moduleConfig?.coingeckoId) nativeToken.coingeckoId = moduleConfig?.coingeckoId;
5338
- 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
+
5339
5196
  return {
5340
5197
  [nativeToken.id]: nativeToken
5341
5198
  };
@@ -5367,11 +5224,11 @@ const SubNativeModule = hydrate => {
5367
5224
  return subscribeChainBalances(networkId, {
5368
5225
  addressesByToken: addressesByTokenByNetwork[networkId] ?? {},
5369
5226
  initialBalances: initialBalancesByNetwork[networkId] ?? []
5370
- }, safeCallback);
5227
+ }, safeCallback, controller.signal);
5371
5228
  }));
5372
5229
  return () => {
5373
- controller.abort();
5374
5230
  unsubsribeFns.then(fns => fns.forEach(unsubscribe => unsubscribe()));
5231
+ controller.abort();
5375
5232
  };
5376
5233
  },
5377
5234
  fetchBalances,
@@ -6589,6 +6446,22 @@ var psp22Abi = {
6589
6446
  };
6590
6447
 
6591
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
+
6592
6465
  const SubPsp22Module = hydrate => {
6593
6466
  const {
6594
6467
  chainConnectors,
@@ -6605,7 +6478,8 @@ const SubPsp22Module = hydrate => {
6605
6478
  extra: null
6606
6479
  };
6607
6480
  },
6608
- async fetchSubstrateChainTokens(chainId, _chainMeta, moduleConfig) {
6481
+ async fetchSubstrateChainTokens(chainId, _chainMeta, moduleConfig, tokens) {
6482
+ if (!tokens?.length) return {};
6609
6483
  // const { isTestnet } = chainMeta
6610
6484
 
6611
6485
  const registry = new types.TypeRegistry();
@@ -6617,12 +6491,12 @@ const SubPsp22Module = hydrate => {
6617
6491
  chainId,
6618
6492
  registry
6619
6493
  });
6620
- const tokens = {};
6621
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
6494
+ const tokenList = {};
6495
+ for (const tokenConfig of tokens ?? []) {
6622
6496
  try {
6623
6497
  let symbol = tokenConfig?.symbol ?? "Unit";
6624
6498
  let decimals = tokenConfig?.decimals ?? 0;
6625
- const existentialDeposit = tokenConfig?.ed ?? "0";
6499
+ const existentialDeposit = tokenConfig?.existentialDeposit ?? "0";
6626
6500
  const contractAddress = tokenConfig?.contractAddress ?? undefined;
6627
6501
  if (contractAddress === undefined) continue;
6628
6502
  await (async () => {
@@ -6655,13 +6529,13 @@ const SubPsp22Module = hydrate => {
6655
6529
  };
6656
6530
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
6657
6531
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
6658
- tokens[token.id] = token;
6532
+ tokenList[token.id] = token;
6659
6533
  } catch (error) {
6660
6534
  log.error(`Failed to build substrate-psp22 token ${tokenConfig.contractAddress} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
6661
6535
  continue;
6662
6536
  }
6663
6537
  }
6664
- return tokens;
6538
+ return tokenList;
6665
6539
  },
6666
6540
  // TODO: Don't create empty subscriptions
6667
6541
  async subscribeBalances({
@@ -6836,11 +6710,28 @@ const fetchBalances = async (chainConnector, tokens, addressesByToken) => {
6836
6710
  };
6837
6711
 
6838
6712
  const moduleType = "substrate-tokens";
6713
+ const SubTokensTokenConfigSchema = TokenConfigBaseSchema.extend({
6714
+ onChainId: chaindataProvider.SubTokensTokenSchema.shape.onChainId,
6715
+ existentialDeposit: chaindataProvider.SubTokensTokenSchema.shape.existentialDeposit
6716
+ });
6839
6717
  const defaultPalletId = "Tokens";
6840
6718
  const UNSUPPORTED_CHAIN_META = {
6841
6719
  miniMetadata: null,
6842
6720
  extra: {}
6843
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
+
6844
6735
  const SubTokensModule = hydrate => {
6845
6736
  const {
6846
6737
  chainConnectors,
@@ -6866,13 +6757,14 @@ const SubTokensModule = hydrate => {
6866
6757
  }
6867
6758
  };
6868
6759
  },
6869
- async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
6870
- const tokens = {};
6871
- for (const tokenConfig of moduleConfig?.tokens ?? []) {
6760
+ async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig, tokens) {
6761
+ const tokenList = {};
6762
+ for (const tokenConfig of tokens ?? []) {
6872
6763
  try {
6764
+ // TODO fetch metadata from chain, like we do for assets
6873
6765
  const symbol = tokenConfig?.symbol ?? "Unit";
6874
6766
  const decimals = tokenConfig?.decimals ?? 0;
6875
- const existentialDeposit = tokenConfig?.ed ?? "0";
6767
+ const existentialDeposit = tokenConfig?.existentialDeposit ?? "0";
6876
6768
  const onChainId = tokenConfig?.onChainId ?? undefined;
6877
6769
  if (onChainId === undefined) continue;
6878
6770
  const id = chaindataProvider.subTokensTokenId(chainId, onChainId);
@@ -6891,13 +6783,13 @@ const SubTokensModule = hydrate => {
6891
6783
  };
6892
6784
  if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
6893
6785
  if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
6894
- tokens[token.id] = token;
6786
+ tokenList[token.id] = token;
6895
6787
  } catch (error) {
6896
6788
  log.error(`Failed to build substrate-tokens token ${tokenConfig.onChainId} (${tokenConfig.symbol}) on ${chainId}`, error?.message ?? error);
6897
6789
  continue;
6898
6790
  }
6899
6791
  }
6900
- return tokens;
6792
+ return tokenList;
6901
6793
  },
6902
6794
  // TODO: Don't create empty subscriptions
6903
6795
  async subscribeBalances({
@@ -6927,10 +6819,10 @@ const SubTokensModule = hydrate => {
6927
6819
  }
6928
6820
  }));
6929
6821
  return () => {
6930
- controller.abort();
6931
6822
  pUnsubs.then(unsubs => {
6932
6823
  unsubs.forEach(unsubscribe => unsubscribe());
6933
6824
  });
6825
+ controller.abort();
6934
6826
  };
6935
6827
  },
6936
6828
  async fetchBalances(addressesByToken) {
@@ -7146,9 +7038,12 @@ exports.Balances = Balances;
7146
7038
  exports.Change24hCurrencyFormatter = Change24hCurrencyFormatter;
7147
7039
  exports.DefaultBalanceModule = DefaultBalanceModule;
7148
7040
  exports.EvmErc20Module = EvmErc20Module;
7041
+ exports.EvmErc20TokenConfigSchema = EvmErc20TokenConfigSchema;
7149
7042
  exports.EvmNativeModule = EvmNativeModule;
7043
+ exports.EvmNativeTokenConfigSchema = EvmNativeTokenConfigSchema;
7150
7044
  exports.EvmTokenFetcher = EvmTokenFetcher;
7151
7045
  exports.EvmUniswapV2Module = EvmUniswapV2Module;
7046
+ exports.EvmUniswapV2TokenConfigSchema = EvmUniswapV2TokenConfigSchema;
7152
7047
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
7153
7048
  exports.ONE_ALPHA_TOKEN = ONE_ALPHA_TOKEN;
7154
7049
  exports.PlanckSumBalancesFormatter = PlanckSumBalancesFormatter;
@@ -7157,10 +7052,15 @@ exports.SCALE_FACTOR = SCALE_FACTOR;
7157
7052
  exports.SUBTENSOR_MIN_STAKE_AMOUNT_PLANK = SUBTENSOR_MIN_STAKE_AMOUNT_PLANK;
7158
7053
  exports.SUBTENSOR_ROOT_NETUID = SUBTENSOR_ROOT_NETUID;
7159
7054
  exports.SubAssetsModule = SubAssetsModule;
7055
+ exports.SubAssetsTokenConfigSchema = SubAssetsTokenConfigSchema;
7160
7056
  exports.SubForeignAssetsModule = SubForeignAssetsModule;
7057
+ exports.SubForeignAssetsTokenConfigSchema = SubForeignAssetsTokenConfigSchema;
7161
7058
  exports.SubNativeModule = SubNativeModule;
7059
+ exports.SubNativeTokenConfigSchema = SubNativeTokenConfigSchema;
7162
7060
  exports.SubPsp22Module = SubPsp22Module;
7061
+ exports.SubPsp22TokenConfigSchema = SubPsp22TokenConfigSchema;
7163
7062
  exports.SubTokensModule = SubTokensModule;
7063
+ exports.SubTokensTokenConfigSchema = SubTokensTokenConfigSchema;
7164
7064
  exports.SumBalancesFormatter = SumBalancesFormatter;
7165
7065
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
7166
7066
  exports.abiMulticall = abiMulticall;