@talismn/balances 0.0.0-pr2043-20250618091117 → 0.0.0-pr2043-20250619003406

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.
@@ -1,8 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chaindataProvider = require('@talismn/chaindata-provider');
4
3
  var dexie = require('dexie');
5
- var rxjs = require('rxjs');
6
4
  var anylogger = require('anylogger');
7
5
  var tokenRates = require('@talismn/token-rates');
8
6
  var util = require('@talismn/util');
@@ -10,12 +8,14 @@ var BigNumber = require('bignumber.js');
10
8
  var util$1 = require('@polkadot/util');
11
9
  var utilCrypto = require('@polkadot/util-crypto');
12
10
  var pako = require('pako');
11
+ var chaindataProvider = require('@talismn/chaindata-provider');
13
12
  var viem = require('viem');
14
13
  var isEqual = require('lodash/isEqual');
15
14
  var txwrapperCore = require('@substrate/txwrapper-core');
16
15
  var scale = require('@talismn/scale');
17
16
  var lodash = require('lodash');
18
17
  var camelCase = require('lodash/camelCase');
18
+ var PQueue = require('p-queue');
19
19
  var sapi = require('@talismn/sapi');
20
20
  var types = require('@polkadot/types');
21
21
  var groupBy = require('lodash/groupBy');
@@ -23,8 +23,10 @@ var utils = require('@polkadot-api/utils');
23
23
  var polkadotApi = require('polkadot-api');
24
24
  var PromisePool = require('@supercharge/promise-pool');
25
25
  var chainConnector = require('@talismn/chain-connector');
26
+ var rxjs = require('rxjs');
26
27
  var scaleTs = require('scale-ts');
27
28
  var upperFirst = require('lodash/upperFirst');
29
+ var z = require('zod/v4');
28
30
  var apiContract = require('@polkadot/api-contract');
29
31
 
30
32
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -34,9 +36,11 @@ var BigNumber__default = /*#__PURE__*/_interopDefault(BigNumber);
34
36
  var pako__default = /*#__PURE__*/_interopDefault(pako);
35
37
  var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
36
38
  var camelCase__default = /*#__PURE__*/_interopDefault(camelCase);
39
+ var PQueue__default = /*#__PURE__*/_interopDefault(PQueue);
37
40
  var groupBy__default = /*#__PURE__*/_interopDefault(groupBy);
38
41
  var PromisePool__default = /*#__PURE__*/_interopDefault(PromisePool);
39
42
  var upperFirst__default = /*#__PURE__*/_interopDefault(upperFirst);
43
+ var z__default = /*#__PURE__*/_interopDefault(z);
40
44
 
41
45
  // TODO: Document default balances module purpose/usage
42
46
  const DefaultBalanceModule = type => ({
@@ -120,13 +124,11 @@ class EvmTokenFetcher {
120
124
  // }
121
125
  }
122
126
 
123
- var packageJson = {
127
+ var pkg = {
124
128
  name: "@talismn/balances",
125
- version: "0.0.0-pr2043-20250618091117"};
126
-
127
- const libVersion = packageJson.version;
129
+ version: "0.0.0-pr2043-20250619003406"};
128
130
 
129
- var log = anylogger__default.default(packageJson.name);
131
+ var log = anylogger__default.default(pkg.name);
130
132
 
131
133
  function excludeFromTransferableAmount(locks) {
132
134
  if (typeof locks === "string") return BigInt(locks);
@@ -336,20 +338,13 @@ class Balances {
336
338
  return new SumBalancesFormatter(this);
337
339
  }
338
340
  }
339
-
340
- // type BalanceJsonEvm = BalanceJson & { evmNetworkId: string }
341
-
342
- // const isBalanceEvm = (balance: BalanceJson): balance is BalanceJsonEvm => "evmNetworkId" in balance
343
-
344
341
  const getBalanceId = balance => {
345
342
  const {
346
343
  source,
347
344
  address,
348
- tokenId,
349
- networkId
345
+ tokenId
350
346
  } = balance;
351
- //const locationId = isBalanceEvm(balance) ? balance.evmNetworkId : balance.chainId
352
- return [source, address, networkId, tokenId].filter(util.isTruthy).join("::");
347
+ return [source, address, tokenId].join("::");
353
348
  };
354
349
 
355
350
  /**
@@ -1061,257 +1056,6 @@ class TalismanBalancesDatabase extends dexie.Dexie {
1061
1056
  }
1062
1057
  const db = new TalismanBalancesDatabase();
1063
1058
 
1064
- const minimumHydrationInterval = 300_000; // 300_000ms = 300s = 5 minutes
1065
-
1066
- /**
1067
- * A substrate dapp needs access to a set of types when it wants to communicate with a blockchain node.
1068
- *
1069
- * These types are used to encode requests & decode responses via the SCALE codec.
1070
- * Each chain generally has its own set of types.
1071
- *
1072
- * Substrate provides a construct to retrieve these types from a blockchain node.
1073
- * The chain metadata.
1074
- *
1075
- * The metadata includes the types required for any communication with the chain,
1076
- * including lots of methods which are not relevant to balance fetching.
1077
- *
1078
- * As such, the metadata can clock in at around 1-2MB per chain, which is a lot of storage
1079
- * for browser-based dapps which want to connect to lots of chains.
1080
- *
1081
- * By utilizing the wonderful [scale-ts](https://github.com/unstoppablejs/unstoppablejs/tree/main/packages/scale-ts#readme) library,
1082
- * we can trim the chain metadata down so that it only includes the types we need for balance fetching.
1083
- *
1084
- * Each balance module has a function to do just that, `BalanceModule::fetchSubstrateChainMeta`.
1085
- *
1086
- * But, we only want to run this operation when necessary.
1087
- *
1088
- * The purpose of this class, `MiniMetadataUpdater`, is to maintain a local cache of
1089
- * trimmed-down metadatas, which we'll refer to as `MiniMetadatas`.
1090
- */
1091
- class MiniMetadataUpdater {
1092
- #lastHydratedMiniMetadatasAt = 0;
1093
- #lastHydratedCustomChainsAt = 0;
1094
- #chainConnectors;
1095
- #chaindataProvider;
1096
- #balanceModules;
1097
- constructor(chainConnectors, chaindataProvider, balanceModules) {
1098
- this.#chainConnectors = chainConnectors;
1099
- this.#chaindataProvider = chaindataProvider;
1100
- this.#balanceModules = balanceModules;
1101
- }
1102
-
1103
- /** Subscribe to the metadata for a chain */
1104
- subscribe(chainId) {
1105
- return rxjs.from(dexie.liveQuery(() => db.miniMetadatas.filter(m => m.chainId === chainId).toArray().then(array => array[0])));
1106
- }
1107
- async update(chainIds) {
1108
- await this.updateSubstrateChains(chainIds);
1109
- }
1110
- async statuses(chains) {
1111
- const ids = await db.miniMetadatas.orderBy("id").primaryKeys();
1112
- const wantedIdsByChain = new Map(chains.flatMap(({
1113
- id: chainId,
1114
- specName,
1115
- specVersion
1116
- }) => {
1117
- if (specName === null) return [];
1118
- if (specVersion === null) return [];
1119
- return [[chainId, this.#balanceModules.filter(m => m.type.startsWith("substrate-")).map(({
1120
- type: source
1121
- }) => deriveMiniMetadataId({
1122
- source,
1123
- chainId,
1124
- specVersion,
1125
- libVersion
1126
- }))]];
1127
- }));
1128
- const statusesByChain = new Map(Array.from(wantedIdsByChain.entries()).map(([chainId, wantedIds]) => [chainId, wantedIds.every(wantedId => ids.includes(wantedId)) ? "good" : "none"]));
1129
- return {
1130
- wantedIdsByChain,
1131
- statusesByChain
1132
- };
1133
- }
1134
- async hydrateFromChaindata() {
1135
- // TODO review this. feels unnecessary to fetch them all
1136
-
1137
- const now = Date.now();
1138
- if (now - this.#lastHydratedMiniMetadatasAt < minimumHydrationInterval) return false;
1139
- const dbHasMiniMetadatas = (await db.miniMetadatas.count()) > 0;
1140
- try {
1141
- try {
1142
- var miniMetadatas = await this.#chaindataProvider.miniMetadatas(); // eslint-disable-line no-var
1143
- if (miniMetadatas.length <= 0) throw new Error("Ignoring empty chaindata miniMetadatas response");
1144
- } catch (error) {
1145
- if (dbHasMiniMetadatas) throw error;
1146
- log.warn("Failed to fetch miniMetadatas from chaindata", error);
1147
- // On first start-up (db is empty), if we fail to fetch miniMetadatas then we should
1148
- // initialize the DB with the list of miniMetadatas inside our init/mini-metadatas.json file.
1149
- // This data will represent a relatively recent copy of what's in chaindata,
1150
- // which will be better for our users than to have nothing at all.
1151
- var miniMetadatas = await chaindataProvider.fetchInitMiniMetadatas(); // eslint-disable-line no-var
1152
- }
1153
- await db.miniMetadatas.bulkPut(miniMetadatas);
1154
- this.#lastHydratedMiniMetadatasAt = now;
1155
- return true;
1156
- } catch (error) {
1157
- log.warn(`Failed to hydrate miniMetadatas from chaindata`, error);
1158
- return false;
1159
- }
1160
- }
1161
- async hydrateCustomChains() {
1162
- // TODO
1163
- // const now = Date.now()
1164
- // if (now - this.#lastHydratedCustomChainsAt < minimumHydrationInterval) return false
1165
- // const chains = await this.#chaindataProvider.chains()
1166
- // const customChains = chains.filter(
1167
- // (chain): chain is CustomChain => "isCustom" in chain && chain.isCustom,
1168
- // )
1169
- // const updatedCustomChains: Array<CustomChain> = []
1170
- // const concurrency = 4
1171
- // ;(
1172
- // await PromisePool.withConcurrency(concurrency)
1173
- // .for(customChains)
1174
- // .process(async (customChain) => {
1175
- // const send = (method: string, params: unknown[]) =>
1176
- // this.#chainConnectors.substrate?.send(customChain.id, method, params)
1177
- // const [genesisHash, runtimeVersion, chainName, chainType] = await Promise.all([
1178
- // send("chain_getBlockHash", [0]),
1179
- // send("state_getRuntimeVersion", []),
1180
- // send("system_chain", []),
1181
- // send("system_chainType", []),
1182
- // ])
1183
- // // deconstruct rpc data
1184
- // const { specName, implName } = runtimeVersion
1185
- // const specVersion = String(runtimeVersion.specVersion)
1186
- // const changed =
1187
- // customChain.genesisHash !== genesisHash ||
1188
- // customChain.chainName !== chainName ||
1189
- // !isEqual(customChain.chainType, chainType) ||
1190
- // customChain.implName !== implName ||
1191
- // customChain.specName !== specName ||
1192
- // customChain.specVersion !== specVersion
1193
- // if (!changed) return
1194
- // customChain.genesisHash = genesisHash
1195
- // customChain.chainName = chainName
1196
- // customChain.chainType = chainType
1197
- // customChain.implName = implName
1198
- // customChain.specName = specName
1199
- // customChain.specVersion = specVersion
1200
- // updatedCustomChains.push(customChain)
1201
- // })
1202
- // ).errors.forEach((error) => log.error("Error hydrating custom chains", error))
1203
- // if (updatedCustomChains.length > 0) {
1204
- // await this.#chaindataProvider.transaction("rw", ["chains"], async () => {
1205
- // for (const updatedCustomChain of updatedCustomChains) {
1206
- // await this.#chaindataProvider.removeCustomChain(updatedCustomChain.id)
1207
- // await this.#chaindataProvider.addCustomChain(updatedCustomChain)
1208
- // }
1209
- // })
1210
- // }
1211
- // if (updatedCustomChains.length > 0) this.#lastHydratedCustomChainsAt = now
1212
- // return true
1213
- }
1214
- async updateSubstrateChains(_chainIds) {
1215
- // const chains = new Map(
1216
- // (await this.#chaindataProvider.chains()).map((chain) => [chain.id, chain]),
1217
- // )
1218
- // const filteredChains = chainIds.flatMap((chainId) => chains.get(chainId) ?? [])
1219
- // const ids = await balancesDb.miniMetadatas.orderBy("id").primaryKeys()
1220
- // const { wantedIdsByChain, statusesByChain } = await this.statuses(filteredChains)
1221
- // // clean up store
1222
- // const wantedIds = Array.from(wantedIdsByChain.values()).flatMap((ids) => ids)
1223
- // const unwantedIds = ids.filter((id) => !wantedIds.includes(id))
1224
- // if (unwantedIds.length > 0) {
1225
- // const chainIds = Array.from(
1226
- // new Set((await balancesDb.miniMetadatas.bulkGet(unwantedIds)).map((m) => m?.chainId)),
1227
- // )
1228
- // log.info(`Pruning ${unwantedIds.length} miniMetadatas on chains ${chainIds.join(", ")}`)
1229
- // await balancesDb.miniMetadatas.bulkDelete(unwantedIds)
1230
- // }
1231
- // const needUpdates = Array.from(statusesByChain.entries())
1232
- // .filter(([, status]) => status !== "good")
1233
- // .map(([chainId]) => chainId)
1234
- // if (needUpdates.length > 0)
1235
- // log.info(`${needUpdates.length} miniMetadatas need updates (${needUpdates.join(", ")})`)
1236
- // const availableTokenLogos = await availableTokenLogoFilenames().catch((error) => {
1237
- // log.error("Failed to fetch available token logos", error)
1238
- // return []
1239
- // })
1240
- // const concurrency = 12
1241
- // ;(
1242
- // await PromisePool.withConcurrency(concurrency)
1243
- // .for(needUpdates)
1244
- // .process(async (chainId) => {
1245
- // log.info(`Updating metadata for chain ${chainId}`)
1246
- // const chain = chains.get(chainId)
1247
- // if (!chain) return
1248
- // const { specName, specVersion } = chain
1249
- // if (specName === null) return
1250
- // if (specVersion === null) return
1251
- // const fetchMetadata = async () => {
1252
- // try {
1253
- // return await fetchBestMetadata(
1254
- // (method, params, isCacheable) => {
1255
- // if (!this.#chainConnectors.substrate)
1256
- // throw new Error("Substrate connector is not available")
1257
- // return this.#chainConnectors.substrate.send(chainId, method, params, isCacheable)
1258
- // },
1259
- // true, // allow v14 fallback
1260
- // )
1261
- // } catch (err) {
1262
- // log.warn(`Failed to fetch metadata for chain ${chainId}`)
1263
- // return undefined
1264
- // }
1265
- // }
1266
- // const [metadataRpc, systemProperties] = await Promise.all([
1267
- // fetchMetadata(),
1268
- // this.#chainConnectors.substrate?.send(chainId, "system_properties", []),
1269
- // ])
1270
- // for (const mod of this.#balanceModules.filter((m) => m.type.startsWith("substrate-"))) {
1271
- // const balancesConfig = (chain.balancesConfig ?? []).find(
1272
- // ({ moduleType }) => moduleType === mod.type,
1273
- // )
1274
- // const moduleConfig = balancesConfig?.moduleConfig ?? {}
1275
- // const chainMeta = await mod.fetchSubstrateChainMeta(
1276
- // chainId,
1277
- // moduleConfig,
1278
- // metadataRpc,
1279
- // systemProperties,
1280
- // )
1281
- // const tokens = await mod.fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig)
1282
- // // update tokens in chaindata
1283
- // await this.#chaindataProvider.updateChainTokens(
1284
- // chainId,
1285
- // mod.type,
1286
- // Object.values(tokens),
1287
- // availableTokenLogos,
1288
- // )
1289
- // // update miniMetadatas
1290
- // const { miniMetadata: data, metadataVersion: version, ...extra } = chainMeta ?? {}
1291
- // await balancesDb.miniMetadatas.put({
1292
- // id: deriveMiniMetadataId({
1293
- // source: mod.type,
1294
- // chainId,
1295
- // specName,
1296
- // specVersion,
1297
- // balancesConfig: JSON.stringify(moduleConfig),
1298
- // }),
1299
- // source: mod.type,
1300
- // chainId,
1301
- // specName,
1302
- // specVersion,
1303
- // balancesConfig: JSON.stringify(moduleConfig),
1304
- // // TODO: Standardise return value from `fetchSubstrateChainMeta`
1305
- // version,
1306
- // data,
1307
- // extra: JSON.stringify(extra),
1308
- // })
1309
- // }
1310
- // })
1311
- // ).errors.forEach((error) => log.error("Error updating chain metadata", error))
1312
- }
1313
- }
1314
-
1315
1059
  const erc20Abi = [{
1316
1060
  constant: true,
1317
1061
  inputs: [],
@@ -2953,12 +2697,14 @@ async function getPoolBalance(publicClient, contractAddress, accountAddress) {
2953
2697
  }
2954
2698
  }
2955
2699
 
2700
+ const libVersion = pkg.version;
2701
+
2956
2702
  // cache the promise so it can be shared across multiple calls
2957
2703
  const CACHE_GET_SPEC_VERSION = new Map();
2958
2704
  const fetchSpecVersion = async (chainConnector, networkId) => {
2959
2705
  const {
2960
2706
  specVersion
2961
- } = await chainConnector.send(networkId, "state_getRuntimeVersion", [true]);
2707
+ } = await chainConnector.send(networkId, "state_getRuntimeVersion", [], true);
2962
2708
  return specVersion;
2963
2709
  };
2964
2710
 
@@ -3000,9 +2746,16 @@ const getMetadataRpc = async (chainConnector, networkId) => {
3000
2746
 
3001
2747
  // share requests as all modules will call this at once
3002
2748
  const CACHE = new Map();
3003
- const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, specVersion) => {
2749
+
2750
+ // ensures we dont fetch miniMetadatas on more than 4 chains at once
2751
+ const POOL = new PQueue__default.default({
2752
+ concurrency: 4
2753
+ });
2754
+ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, specVersion, signal) => {
3004
2755
  if (CACHE.has(networkId)) return CACHE.get(networkId);
3005
- const pResult = fetchMiniMetadatas(chainConnector, chaindataProvider, networkId, specVersion);
2756
+ const pResult = POOL.add(() => fetchMiniMetadatas(chainConnector, chaindataProvider, networkId, specVersion), {
2757
+ signal
2758
+ });
3006
2759
  CACHE.set(networkId, pResult);
3007
2760
  try {
3008
2761
  return await pResult;
@@ -3014,49 +2767,57 @@ const getMiniMetadatas = async (chainConnector, chaindataProvider, networkId, sp
3014
2767
  CACHE.delete(networkId);
3015
2768
  }
3016
2769
  };
3017
- const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, specVersion) => {
3018
- const metadataRpc = await getMetadataRpc(chainConnector, chainId);
3019
- const chainConnectors = {
3020
- substrate: chainConnector
3021
- };
3022
- const modules = defaultBalanceModules.map(mod => mod({
3023
- chainConnectors,
3024
- chaindataProvider
3025
- })).filter(mod => mod.type.startsWith("substrate-"));
3026
- return Promise.all(modules.map(async mod => {
3027
- const source = mod.type;
3028
- const chainMeta = await mod.fetchSubstrateChainMeta(chainId, {}, metadataRpc, {});
3029
- return {
3030
- id: deriveMiniMetadataId({
2770
+ const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, specVersion, signal) => {
2771
+ const start = performance.now();
2772
+ log.debug("[miniMetadata] fetching minimetadatas for %s", chainId);
2773
+ try {
2774
+ const metadataRpc = await getMetadataRpc(chainConnector, chainId);
2775
+ signal?.throwIfAborted();
2776
+ const chainConnectors = {
2777
+ substrate: chainConnector,
2778
+ evm: {} // wont be used but workarounds error for module creation
2779
+ };
2780
+ const modules = defaultBalanceModules.map(mod => mod({
2781
+ chainConnectors,
2782
+ chaindataProvider
2783
+ })).filter(mod => mod.type.startsWith("substrate-"));
2784
+ return Promise.all(modules.map(async mod => {
2785
+ const source = mod.type;
2786
+ const chainMeta = await mod.fetchSubstrateChainMeta(chainId, {}, metadataRpc);
2787
+ return {
2788
+ id: deriveMiniMetadataId({
2789
+ source,
2790
+ chainId,
2791
+ specVersion,
2792
+ libVersion
2793
+ }),
3031
2794
  source,
3032
2795
  chainId,
3033
2796
  specVersion,
3034
- libVersion
3035
- }),
3036
- source,
3037
- chainId,
3038
- specVersion,
3039
- libVersion,
3040
- data: chainMeta?.miniMetadata ?? null
3041
- };
3042
- }));
2797
+ libVersion,
2798
+ data: chainMeta?.miniMetadata ?? null
2799
+ };
2800
+ }));
2801
+ } finally {
2802
+ log.debug("[miniMetadata] updated miniMetadatas for %s in %sms", chainId, performance.now() - start);
2803
+ }
3043
2804
  };
3044
2805
 
3045
- const getUpdatedMiniMetadatas = async (chainConnector, chaindataProvider, networkId, specVersion) => {
3046
- const miniMetadatas = await getMiniMetadatas(chainConnector, chaindataProvider, networkId, specVersion);
2806
+ const getUpdatedMiniMetadatas = async (chainConnector, chaindataProvider, chainId, specVersion, signal) => {
2807
+ const miniMetadatas = await getMiniMetadatas(chainConnector, chaindataProvider, chainId, specVersion, signal);
2808
+ signal?.throwIfAborted();
3047
2809
  await db.transaction("readwrite", "miniMetadatas", async tx => {
3048
2810
  await tx.miniMetadatas.where({
3049
- networkId
2811
+ chainId
3050
2812
  }).delete();
3051
2813
  await tx.miniMetadatas.bulkPut(miniMetadatas);
3052
2814
  });
3053
2815
  return miniMetadatas;
3054
2816
  };
3055
2817
 
3056
- const getMiniMetadata = async (chaindataProvider, chainConnector, chainId, source) => {
2818
+ const getMiniMetadata = async (chaindataProvider, chainConnector, chainId, source, signal) => {
3057
2819
  const specVersion = await getSpecVersion(chainConnector, chainId);
3058
-
3059
- // TODO when working a chaindata branch, need a way to pass the libVersion used to derive the miniMetadataId got github
2820
+ signal?.throwIfAborted();
3060
2821
  const miniMetadataId = deriveMiniMetadataId({
3061
2822
  source,
3062
2823
  chainId,
@@ -3066,11 +2827,13 @@ const getMiniMetadata = async (chaindataProvider, chainConnector, chainId, sourc
3066
2827
 
3067
2828
  // lookup local ones
3068
2829
  const [dbMiniMetadata, ghMiniMetadata] = await Promise.all([db.miniMetadatas.get(miniMetadataId), chaindataProvider.miniMetadataById(miniMetadataId)]);
2830
+ signal?.throwIfAborted();
3069
2831
  const miniMetadata = dbMiniMetadata ?? ghMiniMetadata;
3070
2832
  if (miniMetadata) return miniMetadata;
3071
2833
 
3072
2834
  // update from live chain metadata and persist locally
3073
- const miniMetadatas = await getUpdatedMiniMetadatas(chainConnector, chaindataProvider, chainId, specVersion);
2835
+ const miniMetadatas = await getUpdatedMiniMetadatas(chainConnector, chaindataProvider, chainId, specVersion, signal);
2836
+ signal?.throwIfAborted();
3074
2837
  const found = miniMetadatas.find(m => m.id === miniMetadataId);
3075
2838
  if (!found) {
3076
2839
  log.warn("MiniMetadata not found in updated miniMetadatas", {
@@ -3157,6 +2920,28 @@ const buildStorageCoders = ({
3157
2920
  return [];
3158
2921
  }
3159
2922
  }));
2923
+ const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
2924
+ if (!miniMetadata.data) return null;
2925
+ const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
2926
+ try {
2927
+ const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
2928
+ const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
2929
+ const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
2930
+ chainId
2931
+ }) : moduleMethodOrFn;
2932
+ try {
2933
+ return [[key, scaleBuilder.buildStorage(module, method)]];
2934
+ } catch (cause) {
2935
+ log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
2936
+ return [];
2937
+ }
2938
+ }));
2939
+ return builtCoders;
2940
+ } catch (cause) {
2941
+ log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
2942
+ }
2943
+ return null;
2944
+ };
3160
2945
 
3161
2946
  /**
3162
2947
  * Decodes & unwraps outputs and errors of a given result, contract, and method.
@@ -3357,15 +3142,9 @@ const SubAssetsModule = hydrate => {
3357
3142
  util$1.assert(chainConnector, "This module requires a substrate chain connector");
3358
3143
  return {
3359
3144
  ...DefaultBalanceModule(moduleType$4),
3145
+ // TODO make synchronous at the module definition level ?
3360
3146
  async fetchSubstrateChainMeta(chainId, moduleConfig, metadataRpc) {
3361
- const isTestnet = (await chaindataProvider$1.chainById(chainId))?.isTestnet || false;
3362
- if (metadataRpc === undefined) return {
3363
- isTestnet
3364
- };
3365
- if ((moduleConfig?.tokens ?? []).length < 1) return {
3366
- isTestnet
3367
- };
3368
- const metadataVersion = scale.getMetadataVersion(metadataRpc);
3147
+ if (!metadataRpc) return {};
3369
3148
  const metadata = scale.decAnyMetadata(metadataRpc);
3370
3149
  scale.compactMetadata(metadata, [{
3371
3150
  pallet: "Assets",
@@ -3373,9 +3152,7 @@ const SubAssetsModule = hydrate => {
3373
3152
  }]);
3374
3153
  const miniMetadata = scale.encodeMetadata(metadata);
3375
3154
  return {
3376
- isTestnet,
3377
- miniMetadata,
3378
- metadataVersion
3155
+ miniMetadata
3379
3156
  };
3380
3157
  },
3381
3158
  async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
@@ -3440,44 +3217,32 @@ const SubAssetsModule = hydrate => {
3440
3217
  return acc;
3441
3218
  }, {});
3442
3219
  const controller = new AbortController();
3443
- await Promise.all(lodash.toPairs(byNetwork).map(async ([networkId, addressesByToken]) => {
3444
- const queries = await buildNetworkQueries(networkId, chainConnector, chaindataProvider$1, addressesByToken);
3445
- if (controller.signal.aborted) return;
3446
- const stateHelper = new RpcStateQueryHelper(chainConnector, queries);
3447
- const unsubscribe = await stateHelper.subscribe((error, result) => {
3448
- // console.log("SubstrateAssetsModule.callback", { error, result })
3449
- if (error) return callback(error);
3450
- const balances = result?.filter(b => b !== null) ?? [];
3451
- if (balances.length > 0) callback(null, new Balances(balances));
3452
- });
3453
- controller.signal.addEventListener("abort", () => {
3454
- log.debug("TMP subscribeBalances aborted, unsubscribing from network", networkId);
3455
- unsubscribe();
3456
- });
3220
+ const pUnsubs = Promise.all(lodash.toPairs(byNetwork).map(async ([networkId, addressesByToken]) => {
3221
+ try {
3222
+ const queries = await buildNetworkQueries$1(networkId, chainConnector, chaindataProvider$1, addressesByToken, controller.signal);
3223
+ if (controller.signal.aborted) return () => {};
3224
+ const stateHelper = new RpcStateQueryHelper(chainConnector, queries);
3225
+ return await stateHelper.subscribe((error, result) => {
3226
+ // console.log("SubstrateAssetsModule.callback", { error, result })
3227
+ if (error) return callback(error);
3228
+ const balances = result?.filter(b => b !== null) ?? [];
3229
+ if (balances.length > 0) callback(null, new Balances(balances));
3230
+ });
3231
+ } catch (err) {
3232
+ if (!controller.signal.aborted) log.error(`Failed to subscribe balances for network ${networkId}`, err);
3233
+ return () => {};
3234
+ }
3457
3235
  }));
3458
-
3459
- // const networkIds = uniq(uniq(keys(addressesByToken)).map((tokenId) => parseSubAssetTokenId(tokenId).networkId))
3460
- // const
3461
-
3462
- //console.log("SubstrateAssetsModule.subscribeBalances 1", { addressesByToken })
3463
- // const queries = await buildQueries(chaindataProvider, addressesByToken)
3464
- // //console.log("SubstrateAssetsModule.subscribeBalances 2", { queries, addressesByToken })
3465
- // const unsubscribe = await new RpcStateQueryHelper(chainConnector, queries).subscribe(
3466
- // (error, result) => {
3467
- // // console.log("SubstrateAssetsModule.callback", { error, result })
3468
- // if (error) return callback(error)
3469
- // const balances = result?.filter((b): b is SubAssetsBalance => b !== null) ?? []
3470
- // if (balances.length > 0) callback(null, new Balances(balances))
3471
- // },
3472
- // )
3473
-
3474
3236
  return () => {
3475
3237
  controller.abort();
3238
+ pUnsubs.then(unsubs => {
3239
+ unsubs.forEach(unsubscribe => unsubscribe());
3240
+ });
3476
3241
  };
3477
3242
  },
3478
3243
  async fetchBalances(addressesByToken) {
3479
3244
  util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
3480
- const queries = await buildQueries$3(chaindataProvider$1, addressesByToken);
3245
+ const queries = await buildQueries$3(chainConnector, chaindataProvider$1, addressesByToken);
3481
3246
  const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
3482
3247
  const balances = result?.filter(b => b !== null) ?? [];
3483
3248
  return new Balances(balances);
@@ -3547,23 +3312,14 @@ const SubAssetsModule = hydrate => {
3547
3312
  }
3548
3313
  };
3549
3314
  };
3550
- async function buildNetworkQueries(networkId, chainConnector, chaindataProvider, addressesByToken) {
3551
- const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType$4);
3552
- const network = await chaindataProvider.chainById(networkId);
3315
+ async function buildNetworkQueries$1(networkId, chainConnector, chaindataProvider, addressesByToken, signal) {
3316
+ const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType$4, signal);
3317
+ // console.log("Fetched miniMetadata for network", networkId, { miniMetadata })
3318
+ const chain = await chaindataProvider.chainById(networkId);
3553
3319
  const tokensById = await chaindataProvider.tokensById();
3554
- const chainIds = [networkId];
3555
- const chains = network ? {
3556
- [networkId]: network
3557
- } : {};
3558
- const miniMetadatas = new Map([[miniMetadata.id, miniMetadata]]);
3559
- const chainStorageCoders = buildStorageCoders({
3560
- chainIds,
3561
- chains,
3562
- miniMetadatas,
3563
- moduleType: moduleType$4,
3564
- coders: {
3565
- storage: ["Assets", "Account"]
3566
- }
3320
+ signal?.throwIfAborted();
3321
+ const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
3322
+ storage: ["Assets", "Account"]
3567
3323
  });
3568
3324
  return Object.entries(addressesByToken).flatMap(([tokenId, addresses]) => {
3569
3325
  const token = tokensById[tokenId];
@@ -3575,19 +3331,14 @@ async function buildNetworkQueries(networkId, chainConnector, chaindataProvider,
3575
3331
  log.debug(`This module doesn't handle tokens of type ${token.type}`);
3576
3332
  return [];
3577
3333
  }
3578
- const networkId = token.networkId;
3579
- if (!networkId) {
3580
- log.warn(`Token ${tokenId} has no chain`);
3581
- return [];
3582
- }
3583
- const chain = chains[networkId];
3334
+ //
3584
3335
  if (!chain) {
3585
3336
  log.warn(`Chain ${networkId} for token ${tokenId} not found`);
3586
3337
  return [];
3587
3338
  }
3588
3339
  return addresses.flatMap(address => {
3589
- const scaleCoder = chainStorageCoders.get(networkId)?.storage;
3590
- const stateKey = tryEncode(scaleCoder, BigInt(token.assetId), address) ?? tryEncode(scaleCoder, token.assetId, address);
3340
+ const scaleCoder = networkStorageCoders?.storage;
3341
+ const stateKey = tryEncode(scaleCoder, BigInt(token.assetId), address) ?? tryEncode(scaleCoder, Number(token.assetId), address);
3591
3342
  if (!stateKey) {
3592
3343
  log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3593
3344
  return [];
@@ -3642,102 +3393,16 @@ async function buildNetworkQueries(networkId, chainConnector, chaindataProvider,
3642
3393
  });
3643
3394
  });
3644
3395
  }
3645
- async function buildQueries$3(chaindataProvider, addressesByToken) {
3646
- const allChains = await chaindataProvider.chainsById();
3647
- const tokens = await chaindataProvider.tokensById();
3648
-
3649
- // const networkIds = Object.keys(addressesByToken)
3650
-
3651
- // const
3652
- // const miniMetadatas = await getMiniMetadatas(chainConnector, chaindataProvider, network)
3653
- const miniMetadatas = new Map((await db.miniMetadatas.toArray()).map(miniMetadata => [miniMetadata.id, miniMetadata]));
3654
- const uniqueChainIds = getUniqueChainIds(addressesByToken, tokens);
3655
- const chains = Object.fromEntries(uniqueChainIds.map(chainId => [chainId, allChains[chainId]]));
3656
- const chainStorageCoders = buildStorageCoders({
3657
- chainIds: uniqueChainIds,
3658
- chains,
3659
- miniMetadatas,
3660
- moduleType: "substrate-assets",
3661
- coders: {
3662
- storage: ["Assets", "Account"]
3663
- }
3664
- });
3665
- return Object.entries(addressesByToken).flatMap(([tokenId, addresses]) => {
3666
- const token = tokens[tokenId];
3667
- if (!token) {
3668
- log.warn(`Token ${tokenId} not found`);
3669
- return [];
3670
- }
3671
- if (token.type !== "substrate-assets") {
3672
- log.debug(`This module doesn't handle tokens of type ${token.type}`);
3673
- return [];
3674
- }
3675
- const networkId = token.networkId;
3676
- if (!networkId) {
3677
- log.warn(`Token ${tokenId} has no chain`);
3678
- return [];
3679
- }
3680
- const chain = chains[networkId];
3681
- if (!chain) {
3682
- log.warn(`Chain ${networkId} for token ${tokenId} not found`);
3683
- return [];
3684
- }
3685
- return addresses.flatMap(address => {
3686
- const scaleCoder = chainStorageCoders.get(networkId)?.storage;
3687
- const stateKey = tryEncode(scaleCoder, BigInt(token.assetId), address) ?? tryEncode(scaleCoder, token.assetId, address);
3688
- if (!stateKey) {
3689
- log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3690
- return [];
3691
- }
3692
- const decodeResult = change => {
3693
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3694
-
3695
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
3696
- balance: 0n,
3697
- status: {
3698
- type: "Liquid"
3699
- }};
3700
- const isFrozen = decoded?.status?.type === "Frozen";
3701
- const amount = (decoded?.balance ?? 0n).toString();
3702
-
3703
- // due to the following balance calculations, which are made in the `Balance` type:
3704
- //
3705
- // total balance = (free balance) + (reserved balance)
3706
- // transferable balance = (free balance) - (frozen balance)
3707
- //
3708
- // when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
3709
- // of this balance to the value we received from the RPC.
3710
- //
3711
- // if we only set the `frozen` amount, then the `total` calculation will be incorrect!
3712
- const free = amount;
3713
- const frozen = token.isFrozen || isFrozen ? amount : "0";
3714
-
3715
- // include balance values even if zero, so that newly-zero values overwrite old values
3716
- const balanceValues = [{
3717
- type: "free",
3718
- label: "free",
3719
- amount: free.toString()
3720
- }, {
3721
- type: "locked",
3722
- label: "frozen",
3723
- amount: frozen.toString()
3724
- }];
3725
- return {
3726
- source: "substrate-assets",
3727
- status: "live",
3728
- address,
3729
- networkId,
3730
- tokenId: token.id,
3731
- values: balanceValues
3732
- };
3733
- };
3734
- return {
3735
- chainId: networkId,
3736
- stateKey,
3737
- decodeResult
3738
- };
3739
- });
3740
- });
3396
+ async function buildQueries$3(chainConnector, chaindataProvider$1, addressesByToken, signal) {
3397
+ const byNetwork = lodash.keys(addressesByToken).reduce((acc, tokenId) => {
3398
+ const networkId = chaindataProvider.parseSubAssetTokenId(tokenId).networkId;
3399
+ if (!acc[networkId]) acc[networkId] = {};
3400
+ acc[networkId][tokenId] = addressesByToken[tokenId];
3401
+ return acc;
3402
+ }, {});
3403
+ return (await Promise.all(lodash.toPairs(byNetwork).map(([networkId, addressesByToken]) => {
3404
+ return buildNetworkQueries$1(networkId, chainConnector, chaindataProvider$1, addressesByToken, signal);
3405
+ }))).flat();
3741
3406
  }
3742
3407
  // NOTE: Different chains need different formats for assetId when encoding the stateKey
3743
3408
  // E.g. Polkadot Asset Hub needs it to be a string, Astar needs it to be a bigint
@@ -5413,8 +5078,23 @@ class SubNativeBalanceError extends Error {
5413
5078
  }
5414
5079
  }
5415
5080
 
5416
- const DEFAULT_SYMBOL = "Unit";
5417
- const DEFAULT_DECIMALS = 0;
5081
+ const DotNetworkPropertiesSimple = z__default.default.object({
5082
+ tokenDecimals: z__default.default.number().optional().default(0),
5083
+ tokenSymbol: z__default.default.string().optional().default("Unit")
5084
+ });
5085
+ const DotNetworkPropertiesArray = z__default.default.object({
5086
+ tokenDecimals: z__default.default.array(z__default.default.number()).nonempty(),
5087
+ tokenSymbol: z__default.default.array(z__default.default.string()).nonempty()
5088
+ });
5089
+ const DotNetworkProperties = z__default.default.union([DotNetworkPropertiesSimple, DotNetworkPropertiesArray]).transform(val => ({
5090
+ tokenDecimals: Array.isArray(val.tokenDecimals) ? val.tokenDecimals[0] : val.tokenDecimals,
5091
+ tokenSymbol: Array.isArray(val.tokenSymbol) ? val.tokenSymbol[0] : val.tokenSymbol
5092
+ }));
5093
+ const getChainProperties = async (chainConnector, networkId) => {
5094
+ const properties = await chainConnector.send(networkId, "system_properties", [], true);
5095
+ return DotNetworkProperties.parse(properties);
5096
+ };
5097
+
5418
5098
  const POLLING_WINDOW_SIZE = 20;
5419
5099
  const MAX_SUBSCRIPTION_SIZE = 40;
5420
5100
  const SubNativeModule = hydrate => {
@@ -5428,29 +5108,187 @@ const SubNativeModule = hydrate => {
5428
5108
  const getModuleTokens = async () => {
5429
5109
  return await chaindataProvider$1.tokensByIdForType(moduleType$2);
5430
5110
  };
5431
- return {
5432
- ...DefaultBalanceModule(moduleType$2),
5433
- async fetchSubstrateChainMeta(chainId, moduleConfig, metadataRpc, systemProperties) {
5434
- const isTestnet = (await chaindataProvider$1.chainById(chainId))?.isTestnet || false;
5435
- if (moduleConfig?.disable === true || metadataRpc === undefined) return {
5436
- isTestnet
5437
- };
5438
5111
 
5439
- //
5440
- // extract system_properties
5441
- //
5112
+ // subscribeBalances was split by network to prevent all subs to wait for all minimetadatas to be ready.
5113
+ // 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
5114
+ // TODO refactor this be actually network specific
5115
+ const subscribeChainBalances = async (chainId, opts, callback) => {
5116
+ const {
5117
+ addressesByToken,
5118
+ initialBalances
5119
+ } = opts;
5120
+ // full record of balances for this module
5121
+ const subNativeBalances = new rxjs.BehaviorSubject(Object.fromEntries(initialBalances?.map(b => [getBalanceId(b), b]) ?? []));
5122
+ // tokens which have a known positive balance
5123
+ const positiveBalanceTokens = subNativeBalances.pipe(rxjs.map(balances => Array.from(new Set(Object.values(balances).map(b => b.tokenId)))), rxjs.share());
5124
+
5125
+ // tokens that will be subscribed to, simply a slice of the positive balance tokens of size MAX_SUBSCRIPTION_SIZE
5126
+ const subscriptionTokens = positiveBalanceTokens.pipe(rxjs.map(tokens => tokens.sort(sortChains).slice(0, MAX_SUBSCRIPTION_SIZE)));
5127
+
5128
+ // an initialised balance is one where we have received a response for any type of 'subsource',
5129
+ // until then they are initialising. We only need to maintain one map of tokens to addresses for this
5130
+ const initialisingBalances = Object.entries(addressesByToken).reduce((acc, [tokenId, addresses]) => {
5131
+ acc.set(tokenId, new Set(addresses));
5132
+ return acc;
5133
+ }, new Map());
5134
+
5135
+ // after thirty seconds, we need to kill the initialising balances
5136
+ const initBalancesTimeout = setTimeout(() => {
5137
+ initialisingBalances.clear();
5138
+ // manually call the callback to ensure the caller gets the correct status
5139
+ callback(null, {
5140
+ status: "live",
5141
+ data: Object.values(subNativeBalances.getValue())
5142
+ });
5143
+ }, 30_000);
5144
+ const _callbackSub = subNativeBalances.pipe(rxjs.debounceTime(100)).subscribe({
5145
+ next: balances => {
5146
+ callback(null, {
5147
+ status: initialisingBalances.size > 0 ? "initialising" : "live",
5148
+ data: Object.values(balances)
5149
+ });
5150
+ },
5151
+ error: error => callback(error),
5152
+ complete: () => {
5153
+ initialisingBalances.clear();
5154
+ clearTimeout(initBalancesTimeout);
5155
+ }
5156
+ });
5157
+ const unsubDeferred = util.Deferred();
5158
+ // we return this to the caller so that they can let us know when they're no longer interested in this subscription
5159
+ const callerUnsubscribe = () => {
5160
+ subNativeBalances.complete();
5161
+ _callbackSub.unsubscribe();
5162
+ return unsubDeferred.reject(new Error(`Caller unsubscribed`));
5163
+ };
5164
+ // we queue up our work to clean up our subscription when this promise rejects
5165
+ const callerUnsubscribed = unsubDeferred.promise;
5166
+
5167
+ // The update handler is to allow us to merge balances with the same id, and manage initialising and positive balances state for each
5168
+ // balance type and network
5169
+ const handleUpdateForSource = source => (error, result) => {
5170
+ if (result) {
5171
+ const currentBalances = subNativeBalances.getValue();
5172
+
5173
+ // first merge any balances with the same id within the result
5174
+ const accumulatedUpdates = result.filter(b => b.values.length > 0).reduce((acc, b) => {
5175
+ const bId = getBalanceId(b);
5176
+ acc[bId] = mergeBalances(acc[bId], b, source, false);
5177
+ return acc;
5178
+ }, {});
5179
+
5180
+ // then merge these with the current balances
5181
+ const mergedBalances = {};
5182
+ Object.entries(accumulatedUpdates).forEach(([bId, b]) => {
5183
+ // merge the values from the new balance into the existing balance, if there is one
5184
+ mergedBalances[bId] = mergeBalances(currentBalances[bId], b, source, true);
5185
+
5186
+ // update initialisingBalances to remove balances which have been updated
5187
+ const intialisingForToken = initialisingBalances.get(b.tokenId);
5188
+ if (intialisingForToken) {
5189
+ intialisingForToken.delete(b.address);
5190
+ if (intialisingForToken.size === 0) initialisingBalances.delete(b.tokenId);else initialisingBalances.set(b.tokenId, intialisingForToken);
5191
+ }
5192
+ });
5193
+ subNativeBalances.next({
5194
+ ...currentBalances,
5195
+ ...mergedBalances
5196
+ });
5197
+ }
5198
+ if (error) {
5199
+ if (error instanceof SubNativeBalanceError) {
5200
+ // this type of error doesn't need to be handled by the caller
5201
+ initialisingBalances.delete(error.tokenId);
5202
+ } else return callback(error);
5203
+ }
5204
+ };
5442
5205
 
5443
- const {
5444
- tokenSymbol,
5445
- tokenDecimals
5446
- } = systemProperties ?? {};
5447
- const symbol = (Array.isArray(tokenSymbol) ? tokenSymbol[0] : tokenSymbol) ?? DEFAULT_SYMBOL;
5448
- const decimals = (Array.isArray(tokenDecimals) ? tokenDecimals[0] : tokenDecimals) ?? DEFAULT_DECIMALS;
5206
+ // subscribe to addresses and tokens for which we have a known positive balance
5207
+ const positiveSub = subscriptionTokens.pipe(rxjs.debounceTime(1000), rxjs.takeUntil(callerUnsubscribed), rxjs.map(tokenIds => tokenIds.reduce((acc, tokenId) => {
5208
+ acc[tokenId] = addressesByToken[tokenId];
5209
+ return acc;
5210
+ }, {})), rxjs.distinctUntilChanged(isEqual__default.default), rxjs.switchMap(newAddressesByToken => {
5211
+ return rxjs.from(queryCache.getQueries(newAddressesByToken)).pipe(rxjs.switchMap(baseQueries => {
5212
+ return new rxjs.Observable(subscriber => {
5213
+ if (!chainConnectors.substrate) return;
5214
+ const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"));
5215
+ const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"));
5216
+ const unsubCrowdloans = subscribeCrowdloans(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("crowdloan"));
5217
+ const unsubBase = subscribeBase(baseQueries, chainConnectors.substrate, handleUpdateForSource("base"));
5218
+ subscriber.add(async () => (await unsubSubtensorStaking)());
5219
+ subscriber.add(async () => (await unsubNompoolStaking)());
5220
+ subscriber.add(async () => (await unsubCrowdloans)());
5221
+ subscriber.add(async () => (await unsubBase)());
5222
+ });
5223
+ }));
5224
+ })).subscribe();
5225
+
5226
+ // for chains where we don't have a known positive balance, poll rather than subscribe
5227
+ const poll = async (addressesByToken = {}) => {
5228
+ const handleUpdate = handleUpdateForSource("base");
5229
+ try {
5230
+ const balances = await fetchBalances(addressesByToken);
5231
+ handleUpdate(null, Object.values(balances.toJSON()));
5232
+ } catch (error) {
5233
+ if (error instanceof chainConnector.ChainConnectionError) {
5234
+ // coerce ChainConnection errors into SubNativeBalance errors
5235
+ const errorChainId = error.chainId;
5236
+ Object.entries(await getModuleTokens()).filter(([, token]) => token.networkId === errorChainId).forEach(([tokenId]) => {
5237
+ const wrappedError = new SubNativeBalanceError(tokenId, error.message);
5238
+ handleUpdate(wrappedError);
5239
+ });
5240
+ } else {
5241
+ log.error("unknown substrate native balance error", error);
5242
+ handleUpdate(error);
5243
+ }
5244
+ }
5245
+ };
5246
+ // do one poll to get things started
5247
+ const currentBalances = subNativeBalances.getValue();
5248
+ const currentTokens = new Set(Object.values(currentBalances).map(b => b.tokenId));
5249
+ const nonCurrentTokens = Object.keys(addressesByToken).filter(tokenId => !currentTokens.has(tokenId)).sort(sortChains);
5250
+
5251
+ // break nonCurrentTokens into chunks of POLLING_WINDOW_SIZE
5252
+ await PromisePool__default.default.withConcurrency(POLLING_WINDOW_SIZE).for(nonCurrentTokens).process(async nonCurrentTokenId => await poll({
5253
+ [nonCurrentTokenId]: addressesByToken[nonCurrentTokenId]
5254
+ }));
5255
+
5256
+ // now poll every 30s on chains which are not subscriptionTokens
5257
+ // we chunk this observable into batches of positive token ids, to prevent eating all the websocket connections
5258
+ const pollingSub = rxjs.interval(30_000) // emit values every 30 seconds
5259
+ .pipe(rxjs.takeUntil(callerUnsubscribed), rxjs.withLatestFrom(subscriptionTokens),
5260
+ // Combine latest value from subscriptionTokens with each interval tick
5261
+ rxjs.map(([, subscribedTokenIds]) =>
5262
+ // Filter out tokens that are not subscribed
5263
+ 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 => {
5264
+ // tokenChunk is a chunk of tokenIds with size POLLING_WINDOW_SIZE
5265
+ const pollingTokenAddresses = Object.fromEntries(tokenChunk.map(tokenId => [tokenId, addressesByToken[tokenId]]));
5266
+ await poll(pollingTokenAddresses);
5267
+ return true;
5268
+ })))).subscribe();
5269
+ return () => {
5270
+ callerUnsubscribe();
5271
+ positiveSub.unsubscribe();
5272
+ pollingSub.unsubscribe();
5273
+ };
5274
+ };
5275
+ const fetchBalances = async addressesByToken => {
5276
+ util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
5277
+ const queries = await queryCache.getQueries(addressesByToken);
5278
+ util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
5279
+ const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
5280
+ return new Balances(result ?? []);
5281
+ };
5282
+ return {
5283
+ ...DefaultBalanceModule(moduleType$2),
5284
+ async fetchSubstrateChainMeta(chainId, moduleConfig, metadataRpc) {
5285
+ if (!metadataRpc) return {};
5449
5286
 
5450
5287
  //
5451
5288
  // process metadata into SCALE encoders/decoders
5452
5289
  //
5453
5290
  const metadataVersion = scale.getMetadataVersion(metadataRpc);
5291
+ if (metadataVersion < 14) return {};
5454
5292
  const metadata = scale.decAnyMetadata(metadataRpc);
5455
5293
  const unifiedMetadata = scale.unifyMetadata(metadata);
5456
5294
 
@@ -5517,16 +5355,15 @@ const SubNativeModule = hydrate => {
5517
5355
  }) => name === "Freezes"));
5518
5356
  const useLegacyTransferableCalculation = !hasFreezesItem;
5519
5357
  const chainMeta = {
5520
- isTestnet,
5358
+ // isTestnet,
5521
5359
  useLegacyTransferableCalculation,
5522
- symbol,
5523
- decimals,
5360
+ // symbol,
5361
+ // decimals,
5524
5362
  existentialDeposit,
5525
5363
  nominationPoolsPalletId,
5526
5364
  crowdloanPalletId,
5527
5365
  hasSubtensorPallet,
5528
- miniMetadata,
5529
- metadataVersion
5366
+ miniMetadata
5530
5367
  };
5531
5368
  if (!useLegacyTransferableCalculation) delete chainMeta.useLegacyTransferableCalculation;
5532
5369
  if (!hasSubtensorPallet) delete chainMeta.hasSubtensorPallet;
@@ -5535,9 +5372,10 @@ const SubNativeModule = hydrate => {
5535
5372
  async fetchSubstrateChainTokens(chainId, chainMeta, moduleConfig) {
5536
5373
  if (moduleConfig?.disable === true) return {};
5537
5374
  const {
5538
- isTestnet,
5539
- symbol,
5540
- decimals,
5375
+ tokenSymbol: symbol,
5376
+ tokenDecimals: decimals
5377
+ } = await getChainProperties(chainConnector$1, chainId);
5378
+ const {
5541
5379
  existentialDeposit
5542
5380
  } = chainMeta;
5543
5381
  const id = chaindataProvider.subNativeTokenId(chainId);
@@ -5545,11 +5383,10 @@ const SubNativeModule = hydrate => {
5545
5383
  id,
5546
5384
  type: "substrate-native",
5547
5385
  platform: "polkadot",
5548
- isTestnet,
5549
5386
  isDefault: moduleConfig?.isDefault ?? true,
5550
- symbol: symbol ?? DEFAULT_SYMBOL,
5551
- name: moduleConfig?.name ?? symbol ?? DEFAULT_SYMBOL,
5552
- decimals: decimals ?? DEFAULT_DECIMALS,
5387
+ symbol: symbol,
5388
+ name: moduleConfig?.name ?? symbol,
5389
+ decimals: decimals,
5553
5390
  logo: moduleConfig?.logo,
5554
5391
  existentialDeposit: existentialDeposit ?? "0",
5555
5392
  networkId: chainId
@@ -5566,169 +5403,43 @@ const SubNativeModule = hydrate => {
5566
5403
  initialBalances
5567
5404
  }, callback) {
5568
5405
  util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
5569
-
5570
- // full record of balances for this module
5571
- const subNativeBalances = new rxjs.BehaviorSubject(Object.fromEntries(initialBalances?.map(b => [getBalanceId(b), b]) ?? []));
5572
- // tokens which have a known positive balance
5573
- const positiveBalanceTokens = subNativeBalances.pipe(rxjs.map(balances => Array.from(new Set(Object.values(balances).map(b => b.tokenId)))), rxjs.share());
5574
-
5575
- // tokens that will be subscribed to, simply a slice of the positive balance tokens of size MAX_SUBSCRIPTION_SIZE
5576
- const subscriptionTokens = positiveBalanceTokens.pipe(rxjs.map(tokens => tokens.sort(sortChains).slice(0, MAX_SUBSCRIPTION_SIZE)));
5577
-
5578
- // an initialised balance is one where we have received a response for any type of 'subsource',
5579
- // until then they are initialising. We only need to maintain one map of tokens to addresses for this
5580
- const initialisingBalances = Object.entries(addressesByToken).reduce((acc, [tokenId, addresses]) => {
5581
- acc.set(tokenId, new Set(addresses));
5406
+ const addressesByTokenByNetwork = lodash.keys(addressesByToken).reduce((acc, tokenId) => {
5407
+ const networkId = chaindataProvider.parseSubNativeTokenId(tokenId).networkId;
5408
+ if (!acc[networkId]) acc[networkId] = {};
5409
+ acc[networkId][tokenId] = addressesByToken[tokenId];
5582
5410
  return acc;
5583
- }, new Map());
5584
-
5585
- // after thirty seconds, we need to kill the initialising balances
5586
- const initBalancesTimeout = setTimeout(() => {
5587
- initialisingBalances.clear();
5588
- // manually call the callback to ensure the caller gets the correct status
5589
- callback(null, {
5590
- status: "live",
5591
- data: Object.values(subNativeBalances.getValue())
5592
- });
5593
- }, 30_000);
5594
- const _callbackSub = subNativeBalances.pipe(rxjs.debounceTime(100)).subscribe({
5595
- next: balances => {
5596
- callback(null, {
5597
- status: initialisingBalances.size > 0 ? "initialising" : "live",
5598
- data: Object.values(balances)
5599
- });
5600
- },
5601
- error: error => callback(error),
5602
- complete: () => {
5603
- initialisingBalances.clear();
5604
- clearTimeout(initBalancesTimeout);
5605
- }
5606
- });
5607
- const unsubDeferred = util.Deferred();
5608
- // we return this to the caller so that they can let us know when they're no longer interested in this subscription
5609
- const callerUnsubscribe = () => {
5610
- subNativeBalances.complete();
5611
- _callbackSub.unsubscribe();
5612
- return unsubDeferred.reject(new Error(`Caller unsubscribed`));
5613
- };
5614
- // we queue up our work to clean up our subscription when this promise rejects
5615
- const callerUnsubscribed = unsubDeferred.promise;
5616
-
5617
- // The update handler is to allow us to merge balances with the same id, and manage initialising and positive balances state for each
5618
- // balance type and network
5619
- const handleUpdateForSource = source => (error, result) => {
5620
- if (result) {
5621
- const currentBalances = subNativeBalances.getValue();
5622
-
5623
- // first merge any balances with the same id within the result
5624
- const accumulatedUpdates = result.filter(b => b.values.length > 0).reduce((acc, b) => {
5625
- const bId = getBalanceId(b);
5626
- acc[bId] = mergeBalances(acc[bId], b, source, false);
5627
- return acc;
5628
- }, {});
5629
-
5630
- // then merge these with the current balances
5631
- const mergedBalances = {};
5632
- Object.entries(accumulatedUpdates).forEach(([bId, b]) => {
5633
- // merge the values from the new balance into the existing balance, if there is one
5634
- mergedBalances[bId] = mergeBalances(currentBalances[bId], b, source, true);
5635
-
5636
- // update initialisingBalances to remove balances which have been updated
5637
- const intialisingForToken = initialisingBalances.get(b.tokenId);
5638
- if (intialisingForToken) {
5639
- intialisingForToken.delete(b.address);
5640
- if (intialisingForToken.size === 0) initialisingBalances.delete(b.tokenId);else initialisingBalances.set(b.tokenId, intialisingForToken);
5641
- }
5642
- });
5643
- subNativeBalances.next({
5644
- ...currentBalances,
5645
- ...mergedBalances
5646
- });
5647
- }
5648
- if (error) {
5649
- if (error instanceof SubNativeBalanceError) {
5650
- // this type of error doesn't need to be handled by the caller
5651
- initialisingBalances.delete(error.tokenId);
5652
- } else return callback(error);
5653
- }
5411
+ }, {});
5412
+ const initialBalancesByNetwork = lodash.groupBy(initialBalances ?? [], "networkId");
5413
+ const {
5414
+ abort,
5415
+ signal
5416
+ } = new AbortController();
5417
+ const safeCallback = (error, result) => {
5418
+ if (signal.aborted) return;
5419
+ // typescript isnt happy with fowarding parameters as is
5420
+ return error ? callback(error, undefined) : callback(error, result);
5654
5421
  };
5655
-
5656
- // subscribe to addresses and tokens for which we have a known positive balance
5657
- const positiveSub = subscriptionTokens.pipe(rxjs.debounceTime(1000), rxjs.takeUntil(callerUnsubscribed), rxjs.map(tokenIds => tokenIds.reduce((acc, tokenId) => {
5658
- acc[tokenId] = addressesByToken[tokenId];
5659
- return acc;
5660
- }, {})), rxjs.distinctUntilChanged(isEqual__default.default), rxjs.switchMap(newAddressesByToken => {
5661
- return rxjs.from(queryCache.getQueries(newAddressesByToken)).pipe(rxjs.switchMap(baseQueries => {
5662
- return new rxjs.Observable(subscriber => {
5663
- if (!chainConnectors.substrate) return;
5664
- const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"));
5665
- const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"));
5666
- const unsubCrowdloans = subscribeCrowdloans(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("crowdloan"));
5667
- const unsubBase = subscribeBase(baseQueries, chainConnectors.substrate, handleUpdateForSource("base"));
5668
- subscriber.add(async () => (await unsubSubtensorStaking)());
5669
- subscriber.add(async () => (await unsubNompoolStaking)());
5670
- subscriber.add(async () => (await unsubCrowdloans)());
5671
- subscriber.add(async () => (await unsubBase)());
5672
- });
5673
- }));
5674
- })).subscribe();
5675
-
5676
- // for chains where we don't have a known positive balance, poll rather than subscribe
5677
- const poll = async (addressesByToken = {}) => {
5678
- const handleUpdate = handleUpdateForSource("base");
5422
+ const unsubsribeFns = Promise.all(lodash.keys(addressesByTokenByNetwork).map(async networkId => {
5679
5423
  try {
5680
- const balances = await this.fetchBalances(addressesByToken);
5681
- handleUpdate(null, Object.values(balances.toJSON()));
5682
- } catch (error) {
5683
- if (error instanceof chainConnector.ChainConnectionError) {
5684
- // coerce ChainConnection errors into SubNativeBalance errors
5685
- const errorChainId = error.chainId;
5686
- Object.entries(await getModuleTokens()).filter(([, token]) => token.networkId === errorChainId).forEach(([tokenId]) => {
5687
- const wrappedError = new SubNativeBalanceError(tokenId, error.message);
5688
- handleUpdate(wrappedError);
5689
- });
5690
- } else {
5691
- log.error("unknown substrate native balance error", error);
5692
- handleUpdate(error);
5693
- }
5424
+ // this is what we want to be done separately for each network
5425
+ // this will update the DB so minimetadata will be available when it's used, veeeeery far down the tree of subscribeChainBalances
5426
+ await getMiniMetadata(chaindataProvider$1, chainConnector$1, networkId, moduleType$2, signal);
5427
+ } catch (err) {
5428
+ if (!signal.aborted) log.warn("Failed to get native token miniMetadata for network", networkId, err);
5429
+ return () => {};
5694
5430
  }
5695
- };
5696
- // do one poll to get things started
5697
- const currentBalances = subNativeBalances.getValue();
5698
- const currentTokens = new Set(Object.values(currentBalances).map(b => b.tokenId));
5699
- const nonCurrentTokens = Object.keys(addressesByToken).filter(tokenId => !currentTokens.has(tokenId)).sort(sortChains);
5700
-
5701
- // break nonCurrentTokens into chunks of POLLING_WINDOW_SIZE
5702
- await PromisePool__default.default.withConcurrency(POLLING_WINDOW_SIZE).for(nonCurrentTokens).process(async nonCurrentTokenId => await poll({
5703
- [nonCurrentTokenId]: addressesByToken[nonCurrentTokenId]
5431
+ if (signal.aborted) return () => {};
5432
+ return subscribeChainBalances(networkId, {
5433
+ addressesByToken: addressesByTokenByNetwork[networkId] ?? {},
5434
+ initialBalances: initialBalancesByNetwork[networkId] ?? []
5435
+ }, safeCallback);
5704
5436
  }));
5705
-
5706
- // now poll every 30s on chains which are not subscriptionTokens
5707
- // we chunk this observable into batches of positive token ids, to prevent eating all the websocket connections
5708
- const pollingSub = rxjs.interval(30_000) // emit values every 30 seconds
5709
- .pipe(rxjs.takeUntil(callerUnsubscribed), rxjs.withLatestFrom(subscriptionTokens),
5710
- // Combine latest value from subscriptionTokens with each interval tick
5711
- rxjs.map(([, subscribedTokenIds]) =>
5712
- // Filter out tokens that are not subscribed
5713
- 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 => {
5714
- // tokenChunk is a chunk of tokenIds with size POLLING_WINDOW_SIZE
5715
- const pollingTokenAddresses = Object.fromEntries(tokenChunk.map(tokenId => [tokenId, addressesByToken[tokenId]]));
5716
- await poll(pollingTokenAddresses);
5717
- return true;
5718
- })))).subscribe();
5719
5437
  return () => {
5720
- callerUnsubscribe();
5721
- positiveSub.unsubscribe();
5722
- pollingSub.unsubscribe();
5438
+ abort();
5439
+ unsubsribeFns.then(fns => fns.forEach(unsubscribe => unsubscribe()));
5723
5440
  };
5724
5441
  },
5725
- async fetchBalances(addressesByToken) {
5726
- util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
5727
- const queries = await queryCache.getQueries(addressesByToken);
5728
- util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
5729
- const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
5730
- return new Balances(result ?? []);
5731
- },
5442
+ fetchBalances,
5732
5443
  async transferToken({
5733
5444
  tokenId,
5734
5445
  from,
@@ -7200,7 +6911,6 @@ const SubTokensModule = hydrate => {
7200
6911
  ...DefaultBalanceModule(moduleType),
7201
6912
  async fetchSubstrateChainMeta(chainId, moduleConfig, metadataRpc) {
7202
6913
  if (metadataRpc === undefined) return {};
7203
- if ((moduleConfig?.tokens ?? []).length < 1) return {};
7204
6914
  const metadata = scale.decAnyMetadata(metadataRpc);
7205
6915
  const palletId = moduleConfig?.palletId ?? defaultPalletId;
7206
6916
  scale.compactMetadata(metadata, [{
@@ -7252,17 +6962,39 @@ const SubTokensModule = hydrate => {
7252
6962
  async subscribeBalances({
7253
6963
  addressesByToken
7254
6964
  }, callback) {
7255
- const queries = await buildQueries(chaindataProvider$1, addressesByToken);
7256
- const unsubscribe = await new RpcStateQueryHelper(chainConnector, queries).subscribe((error, result) => {
7257
- if (error) return callback(error);
7258
- const balances = result?.filter(b => b !== null) ?? [];
7259
- if (balances.length > 0) callback(null, new Balances(balances));
7260
- });
7261
- return unsubscribe;
6965
+ const byNetwork = lodash.keys(addressesByToken).reduce((acc, tokenId) => {
6966
+ const networkId = chaindataProvider.parseSubTokensTokenId(tokenId).networkId;
6967
+ if (!acc[networkId]) acc[networkId] = {};
6968
+ acc[networkId][tokenId] = addressesByToken[tokenId];
6969
+ return acc;
6970
+ }, {});
6971
+ const controller = new AbortController();
6972
+ const pUnsubs = Promise.all(lodash.toPairs(byNetwork).map(async ([networkId, addressesByToken]) => {
6973
+ try {
6974
+ const queries = await buildNetworkQueries(networkId, chainConnector, chaindataProvider$1, addressesByToken, controller.signal);
6975
+ if (controller.signal.aborted) return () => {};
6976
+ const stateHelper = new RpcStateQueryHelper(chainConnector, queries);
6977
+ return await stateHelper.subscribe((error, result) => {
6978
+ // console.log("SubstrateAssetsModule.callback", { error, result })
6979
+ if (error) return callback(error);
6980
+ const balances = result?.filter(b => b !== null) ?? [];
6981
+ if (balances.length > 0) callback(null, new Balances(balances));
6982
+ });
6983
+ } catch (err) {
6984
+ if (!controller.signal.aborted) log.error(`Failed to subscribe balances for network ${networkId}`, err);
6985
+ return () => {};
6986
+ }
6987
+ }));
6988
+ return () => {
6989
+ controller.abort();
6990
+ pUnsubs.then(unsubs => {
6991
+ unsubs.forEach(unsubscribe => unsubscribe());
6992
+ });
6993
+ };
7262
6994
  },
7263
6995
  async fetchBalances(addressesByToken) {
7264
6996
  util$1.assert(chainConnectors.substrate, "This module requires a substrate chain connector");
7265
- const queries = await buildQueries(chaindataProvider$1, addressesByToken);
6997
+ const queries = await buildQueries(chainConnector, chaindataProvider$1, addressesByToken);
7266
6998
  const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
7267
6999
  const balances = result?.filter(b => b !== null) ?? [];
7268
7000
  return new Balances(balances);
@@ -7381,23 +7113,16 @@ const SubTokensModule = hydrate => {
7381
7113
  }
7382
7114
  };
7383
7115
  };
7384
- async function buildQueries(chaindataProvider, addressesByToken) {
7385
- const allChains = await chaindataProvider.chainsById();
7116
+ async function buildNetworkQueries(networkId, chainConnector, chaindataProvider, addressesByToken, signal) {
7117
+ const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType, signal);
7118
+ const chain = await chaindataProvider.chainById(networkId);
7386
7119
  const tokens = await chaindataProvider.tokensById();
7387
- const miniMetadatas = new Map((await db.miniMetadatas.toArray()).map(miniMetadata => [miniMetadata.id, miniMetadata]));
7388
- const tokensPalletByChain = new Map(Object.values(allChains).map(chain => [chain.id, findChainMeta(miniMetadatas, moduleType, chain)[0]?.palletId]));
7389
- const uniqueChainIds = getUniqueChainIds(addressesByToken, tokens);
7390
- const chains = Object.fromEntries(uniqueChainIds.map(chainId => [chainId, allChains[chainId]]));
7391
- const chainStorageCoders = buildStorageCoders({
7392
- chainIds: uniqueChainIds,
7393
- chains,
7394
- miniMetadatas,
7395
- moduleType: "substrate-tokens",
7396
- coders: {
7397
- storage: ({
7398
- chainId
7399
- }) => [tokensPalletByChain.get(chainId) ?? defaultPalletId, "Accounts"]
7400
- }
7120
+ if (!chain) return [];
7121
+ signal?.throwIfAborted();
7122
+ const tokensMetadata = miniMetadata;
7123
+ const palletId = tokensMetadata.palletId ?? defaultPalletId;
7124
+ const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
7125
+ storage: [palletId, "Accounts"]
7401
7126
  });
7402
7127
  return Object.entries(addressesByToken).flatMap(([tokenId, addresses]) => {
7403
7128
  const token = tokens[tokenId];
@@ -7409,18 +7134,8 @@ async function buildQueries(chaindataProvider, addressesByToken) {
7409
7134
  log.debug(`This module doesn't handle tokens of type ${token.type}`);
7410
7135
  return [];
7411
7136
  }
7412
- const networkId = token.networkId;
7413
- if (!networkId) {
7414
- log.warn(`Token ${tokenId} has no chain`);
7415
- return [];
7416
- }
7417
- const chain = chains[networkId];
7418
- if (!chain) {
7419
- log.warn(`Chain ${networkId} for token ${tokenId} not found`);
7420
- return [];
7421
- }
7422
7137
  return addresses.flatMap(address => {
7423
- const scaleCoder = chainStorageCoders.get(networkId)?.storage;
7138
+ const scaleCoder = networkStorageCoders?.storage;
7424
7139
  const onChainId = (() => {
7425
7140
  try {
7426
7141
  return scale.papiParse(token.onChainId);
@@ -7471,35 +7186,20 @@ async function buildQueries(chaindataProvider, addressesByToken) {
7471
7186
  });
7472
7187
  });
7473
7188
  }
7189
+ async function buildQueries(chainConnector, chaindataProvider$1, addressesByToken, signal) {
7190
+ const byNetwork = lodash.keys(addressesByToken).reduce((acc, tokenId) => {
7191
+ const networkId = chaindataProvider.parseSubTokensTokenId(tokenId).networkId;
7192
+ if (!acc[networkId]) acc[networkId] = {};
7193
+ acc[networkId][tokenId] = addressesByToken[tokenId];
7194
+ return acc;
7195
+ }, {});
7196
+ return (await Promise.all(lodash.toPairs(byNetwork).map(([networkId, addressesByToken]) => {
7197
+ return buildNetworkQueries(networkId, chainConnector, chaindataProvider$1, addressesByToken, signal);
7198
+ }))).flat();
7199
+ }
7474
7200
 
7475
7201
  const defaultBalanceModules = [EvmErc20Module, EvmNativeModule, EvmUniswapV2Module, SubAssetsModule, SubForeignAssetsModule, SubNativeModule, SubPsp22Module, SubTokensModule];
7476
7202
 
7477
- /** Pulls the latest chaindata from https://github.com/TalismanSociety/chaindata */
7478
- const hydrateChaindataAndMiniMetadata = async (chaindataProvider, miniMetadataUpdater) => {
7479
- // need chains to be provisioned first, or substrate balances won't fetch on first subscription
7480
- await chaindataProvider.hydrate();
7481
- await Promise.all([miniMetadataUpdater.hydrateFromChaindata(), miniMetadataUpdater.hydrateCustomChains()]);
7482
- const chains = await chaindataProvider.chains();
7483
- const {
7484
- statusesByChain
7485
- } = await miniMetadataUpdater.statuses(chains);
7486
- const goodChains = [...statusesByChain.entries()].flatMap(([chainId, status]) => status === "good" ? chainId : []);
7487
- await chaindataProvider.hydrateSubstrateTokens(goodChains);
7488
- };
7489
-
7490
- /** Builds any missing miniMetadatas (e.g. for the user's custom substrate chains) */
7491
- const updateCustomMiniMetadata = async (chaindataProvider, miniMetadataUpdater) => {
7492
- const chainIds = await chaindataProvider.chainIds();
7493
- await miniMetadataUpdater.update(chainIds);
7494
- };
7495
-
7496
- /** Fetches any missing Evm Tokens */
7497
- const updateEvmTokens = async (chaindataProvider, evmTokenFetcher) => {
7498
- await chaindataProvider.hydrate();
7499
- const evmNetworkIds = await chaindataProvider.evmNetworkIds();
7500
- await evmTokenFetcher.update(evmNetworkIds);
7501
- };
7502
-
7503
7203
  exports.Balance = Balance;
7504
7204
  exports.BalanceFormatter = BalanceFormatter;
7505
7205
  exports.BalanceValueGetter = BalanceValueGetter;
@@ -7511,7 +7211,6 @@ exports.EvmNativeModule = EvmNativeModule;
7511
7211
  exports.EvmTokenFetcher = EvmTokenFetcher;
7512
7212
  exports.EvmUniswapV2Module = EvmUniswapV2Module;
7513
7213
  exports.FiatSumBalancesFormatter = FiatSumBalancesFormatter;
7514
- exports.MiniMetadataUpdater = MiniMetadataUpdater;
7515
7214
  exports.ONE_ALPHA_TOKEN = ONE_ALPHA_TOKEN;
7516
7215
  exports.PlanckSumBalancesFormatter = PlanckSumBalancesFormatter;
7517
7216
  exports.RpcStateQueryHelper = RpcStateQueryHelper;
@@ -7527,6 +7226,7 @@ exports.SumBalancesFormatter = SumBalancesFormatter;
7527
7226
  exports.TalismanBalancesDatabase = TalismanBalancesDatabase;
7528
7227
  exports.abiMulticall = abiMulticall;
7529
7228
  exports.balances = balances;
7229
+ exports.buildNetworkStorageCoders = buildNetworkStorageCoders;
7530
7230
  exports.buildStorageCoders = buildStorageCoders;
7531
7231
  exports.calculateAlphaPrice = calculateAlphaPrice;
7532
7232
  exports.calculateTaoAmountFromAlpha = calculateTaoAmountFromAlpha;
@@ -7550,9 +7250,6 @@ exports.getBalanceId = getBalanceId;
7550
7250
  exports.getLockTitle = getLockTitle;
7551
7251
  exports.getUniqueChainIds = getUniqueChainIds;
7552
7252
  exports.getValueId = getValueId;
7553
- exports.hydrateChaindataAndMiniMetadata = hydrateChaindataAndMiniMetadata;
7554
7253
  exports.includeInTotalExtraAmount = includeInTotalExtraAmount;
7555
7254
  exports.makeContractCaller = makeContractCaller;
7556
7255
  exports.uniswapV2PairAbi = uniswapV2PairAbi;
7557
- exports.updateCustomMiniMetadata = updateCustomMiniMetadata;
7558
- exports.updateEvmTokens = updateEvmTokens;