@talismn/balances 0.0.0-pr2075-20250708143919 → 0.0.0-pr2075-20250708160640

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.
@@ -15,10 +15,10 @@ var z = require('zod/v4');
15
15
  var rxjs = require('rxjs');
16
16
  var isEqual = require('lodash/isEqual');
17
17
  var scale = require('@talismn/scale');
18
- var types = require('@polkadot/types');
19
- var groupBy = require('lodash/groupBy');
20
18
  var utils = require('@polkadot-api/utils');
21
19
  var polkadotApi = require('polkadot-api');
20
+ var types = require('@polkadot/types');
21
+ var groupBy = require('lodash/groupBy');
22
22
  var upperFirst = require('lodash/upperFirst');
23
23
  var scaleTs = require('scale-ts');
24
24
  var apiContract = require('@polkadot/api-contract');
@@ -1553,6 +1553,10 @@ const fetchBalances$c = async ({
1553
1553
  tokensWithAddresses,
1554
1554
  connector
1555
1555
  }) => {
1556
+ if (!tokensWithAddresses.length) return {
1557
+ success: [],
1558
+ errors: []
1559
+ };
1556
1560
  const client = await connector.getPublicClientForEvmNetwork(networkId);
1557
1561
  if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
1558
1562
  for (const [token, addresses] of tokensWithAddresses) {
@@ -1571,7 +1575,6 @@ const fetchWithoutAggregator$1 = async (client, balanceDefs) => {
1571
1575
  success: [],
1572
1576
  errors: []
1573
1577
  };
1574
- log.debug("fetching %s balances without aggregator", MODULE_TYPE$8, balanceDefs.length);
1575
1578
  const results = await Promise.allSettled(balanceDefs.map(async ({
1576
1579
  token,
1577
1580
  address
@@ -1589,7 +1592,7 @@ const fetchWithoutAggregator$1 = async (client, balanceDefs) => {
1589
1592
  value: result.toString(),
1590
1593
  source: MODULE_TYPE$8,
1591
1594
  networkId: chaindataProvider.parseEvmErc20TokenId(token.id).networkId,
1592
- status: "cache"
1595
+ status: "live"
1593
1596
  };
1594
1597
  return balance;
1595
1598
  } catch (err) {
@@ -1616,7 +1619,6 @@ const fetchWithAggregator$1 = async (client, balanceDefs, erc20BalancesAggregato
1616
1619
  success: [],
1617
1620
  errors: []
1618
1621
  };
1619
- log.debug("fetching %s balances with aggregator", MODULE_TYPE$8, balanceDefs.length);
1620
1622
  try {
1621
1623
  const erc20Balances = await client.readContract({
1622
1624
  abi: erc20BalancesAggregatorAbi,
@@ -1633,7 +1635,7 @@ const fetchWithAggregator$1 = async (client, balanceDefs, erc20BalancesAggregato
1633
1635
  value: erc20Balances[index].toString(),
1634
1636
  source: MODULE_TYPE$8,
1635
1637
  networkId: chaindataProvider.parseTokenId(balanceDef.token.id).networkId,
1636
- status: "cache"
1638
+ status: "live"
1637
1639
  }));
1638
1640
  return {
1639
1641
  success,
@@ -1779,12 +1781,16 @@ const getTransferCallData$8 = ({
1779
1781
  };
1780
1782
  };
1781
1783
 
1782
- const SUBSCRIPTION_INTERVAL$6 = 6_000;
1784
+ const SUBSCRIPTION_INTERVAL$4 = 6_000;
1783
1785
  const subscribeBalances$8 = ({
1784
1786
  networkId,
1785
1787
  tokensWithAddresses,
1786
1788
  connector
1787
1789
  }) => {
1790
+ if (!tokensWithAddresses.length) return rxjs.of({
1791
+ success: [],
1792
+ errors: []
1793
+ });
1788
1794
  return new rxjs.Observable(subscriber => {
1789
1795
  const abortController = new AbortController();
1790
1796
  const poll = async () => {
@@ -1797,7 +1803,7 @@ const subscribeBalances$8 = ({
1797
1803
  });
1798
1804
  if (abortController.signal.aborted) return;
1799
1805
  subscriber.next(balances);
1800
- setTimeout(poll, SUBSCRIPTION_INTERVAL$6);
1806
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
1801
1807
  } catch (error) {
1802
1808
  log.error("Error", {
1803
1809
  module: MODULE_TYPE$8,
@@ -1843,6 +1849,10 @@ const fetchBalances$b = async ({
1843
1849
  tokensWithAddresses,
1844
1850
  connector
1845
1851
  }) => {
1852
+ if (!tokensWithAddresses.length) return {
1853
+ success: [],
1854
+ errors: []
1855
+ };
1846
1856
  const client = await connector.getPublicClientForEvmNetwork(networkId);
1847
1857
  if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
1848
1858
  for (const [token, addresses] of tokensWithAddresses) {
@@ -1861,7 +1871,6 @@ const fetchWithoutMulticall = async (client, balanceDefs) => {
1861
1871
  success: [],
1862
1872
  errors: []
1863
1873
  };
1864
- log.debug("fetching %s balances without multicall3", MODULE_TYPE$7, balanceDefs.length);
1865
1874
  const results = await Promise.allSettled(balanceDefs.map(async ({
1866
1875
  token,
1867
1876
  address
@@ -1903,7 +1912,6 @@ const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
1903
1912
  success: [],
1904
1913
  errors: []
1905
1914
  };
1906
- log.debug("fetching %s balances with multicall3", MODULE_TYPE$7, balanceDefs.length);
1907
1915
  try {
1908
1916
  const callResults = await client.multicall({
1909
1917
  contracts: balanceDefs.map(({
@@ -1993,12 +2001,16 @@ const getTransferCallData$7 = ({
1993
2001
  };
1994
2002
  };
1995
2003
 
1996
- const SUBSCRIPTION_INTERVAL$5 = 6_000;
2004
+ const SUBSCRIPTION_INTERVAL$3 = 6_000;
1997
2005
  const subscribeBalances$7 = ({
1998
2006
  networkId,
1999
2007
  tokensWithAddresses,
2000
2008
  connector
2001
2009
  }) => {
2010
+ if (!tokensWithAddresses.length) return rxjs.of({
2011
+ success: [],
2012
+ errors: []
2013
+ });
2002
2014
  return new rxjs.Observable(subscriber => {
2003
2015
  const abortController = new AbortController();
2004
2016
  const poll = async () => {
@@ -2011,7 +2023,7 @@ const subscribeBalances$7 = ({
2011
2023
  });
2012
2024
  if (abortController.signal.aborted) return;
2013
2025
  subscriber.next(balances);
2014
- setTimeout(poll, SUBSCRIPTION_INTERVAL$5);
2026
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
2015
2027
  } catch (error) {
2016
2028
  log.error("Error", {
2017
2029
  module: MODULE_TYPE$7,
@@ -2052,6 +2064,10 @@ const fetchBalances$a = async ({
2052
2064
  tokensWithAddresses,
2053
2065
  connector
2054
2066
  }) => {
2067
+ if (!tokensWithAddresses.length) return {
2068
+ success: [],
2069
+ errors: []
2070
+ };
2055
2071
  const client = await connector.getPublicClientForEvmNetwork(networkId);
2056
2072
  if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
2057
2073
  for (const [token, addresses] of tokensWithAddresses) {
@@ -2070,7 +2086,6 @@ const fetchWithoutAggregator = async (client, balanceDefs) => {
2070
2086
  success: [],
2071
2087
  errors: []
2072
2088
  };
2073
- log.debug("fetching %s balances without aggregator", MODULE_TYPE$6, balanceDefs.length);
2074
2089
  const results = await Promise.allSettled(balanceDefs.map(async ({
2075
2090
  token,
2076
2091
  address
@@ -2088,7 +2103,7 @@ const fetchWithoutAggregator = async (client, balanceDefs) => {
2088
2103
  value: result.toString(),
2089
2104
  source: MODULE_TYPE$6,
2090
2105
  networkId: chaindataProvider.parseTokenId(token.id).networkId,
2091
- status: "cache"
2106
+ status: "live"
2092
2107
  };
2093
2108
  return balance;
2094
2109
  } catch (err) {
@@ -2115,7 +2130,6 @@ const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorA
2115
2130
  success: [],
2116
2131
  errors: []
2117
2132
  };
2118
- log.debug("fetching %s balances with aggregator", MODULE_TYPE$6, balanceDefs.length);
2119
2133
  try {
2120
2134
  const erc20Balances = await client.readContract({
2121
2135
  abi: erc20BalancesAggregatorAbi,
@@ -2132,7 +2146,7 @@ const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorA
2132
2146
  value: erc20Balances[index].toString(),
2133
2147
  source: MODULE_TYPE$6,
2134
2148
  networkId: chaindataProvider.parseTokenId(balanceDef.token.id).networkId,
2135
- status: "cache"
2149
+ status: "live"
2136
2150
  }));
2137
2151
  return {
2138
2152
  success,
@@ -2326,12 +2340,16 @@ const getTransferCallData$6 = ({
2326
2340
  };
2327
2341
  };
2328
2342
 
2329
- const SUBSCRIPTION_INTERVAL$4 = 6_000;
2343
+ const SUBSCRIPTION_INTERVAL$2 = 6_000;
2330
2344
  const subscribeBalances$6 = ({
2331
2345
  networkId,
2332
2346
  tokensWithAddresses,
2333
2347
  connector
2334
2348
  }) => {
2349
+ if (!tokensWithAddresses.length) return rxjs.of({
2350
+ success: [],
2351
+ errors: []
2352
+ });
2335
2353
  return new rxjs.Observable(subscriber => {
2336
2354
  const abortController = new AbortController();
2337
2355
  const poll = async () => {
@@ -2344,7 +2362,7 @@ const subscribeBalances$6 = ({
2344
2362
  });
2345
2363
  if (abortController.signal.aborted) return;
2346
2364
  subscriber.next(balances);
2347
- setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
2365
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
2348
2366
  } catch (error) {
2349
2367
  log.error("Error", {
2350
2368
  module: MODULE_TYPE$6,
@@ -3208,273 +3226,138 @@ async function getPoolBalance(publicClient, contractAddress, accountAddress) {
3208
3226
  const MODULE_TYPE$5 = chaindataProvider.SubAssetsTokenSchema.shape.type.value;
3209
3227
  const PLATFORM$5 = chaindataProvider.SubAssetsTokenSchema.shape.platform.value;
3210
3228
 
3211
- /**
3212
- * Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
3213
- * This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
3214
- */
3229
+ const fetchRpcQueryPack = async (connector, networkId, queries) => {
3230
+ const allStateKeys = queries.flatMap(({
3231
+ stateKeys
3232
+ }) => stateKeys).filter(util.isNotNil);
3215
3233
 
3216
- async function balances(balanceModule, addressesByToken, callback) {
3217
- // subscription request
3218
- if (callback !== undefined) return await balanceModule.subscribeBalances({
3219
- addressesByToken
3220
- }, callback);
3234
+ // doing a query without keys would throw an error => return early
3235
+ if (!allStateKeys.length) return queries.map(({
3236
+ stateKeys,
3237
+ decodeResult
3238
+ }) => decodeResult(stateKeys.map(() => null)));
3239
+ const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
3240
+ return decodeRpcQueryPack(queries, result);
3241
+ };
3242
+ const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
3243
+ const allStateKeys = queries.flatMap(({
3244
+ stateKeys
3245
+ }) => stateKeys).filter(util.isNotNil);
3221
3246
 
3222
- // one-off request
3223
- return await balanceModule.fetchBalances(addressesByToken);
3224
- }
3247
+ // doing a query without keys would throw an error => return early
3248
+ if (!allStateKeys.length) return rxjs.of(queries.map(({
3249
+ stateKeys,
3250
+ decodeResult
3251
+ }) => decodeResult(stateKeys.map(() => null))));
3252
+ return new rxjs.Observable(subscriber => {
3253
+ const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
3254
+ if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
3255
+ }, timeout);
3256
+ return () => {
3257
+ promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
3258
+ };
3259
+ });
3260
+ };
3261
+ const decodeRpcQueryPack = (queries, result) => {
3262
+ return queries.reduce((acc, {
3263
+ stateKeys,
3264
+ decodeResult
3265
+ }) => {
3266
+ const changes = stateKeys.map(stateKey => {
3267
+ if (!stateKey) return null;
3268
+ const change = result.changes.find(([key]) => key === stateKey);
3269
+ if (!change) return null;
3270
+ return change[1];
3271
+ });
3272
+ acc.push(decodeResult(changes));
3273
+ return acc;
3274
+ }, []);
3275
+ };
3225
3276
 
3226
- // TODO remove this one in favor of the network specific one below
3227
- const buildStorageCoders = ({
3228
- chainIds,
3229
- chains,
3230
- miniMetadatas,
3231
- coders
3232
- }) => new Map([...chainIds].flatMap(chainId => {
3233
- const chain = chains[chainId];
3234
- if (!chain) return [];
3235
- const miniMetadata = miniMetadatas.get(chainId); // findMiniMetadata<TBalanceModule>(miniMetadatas, moduleType, chain)
3236
- if (!miniMetadata) return [];
3237
- if (!miniMetadata.data) return [];
3238
- const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
3239
- try {
3240
- const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
3241
- const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
3242
- const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
3243
- chainId
3244
- }) : moduleMethodOrFn;
3245
- try {
3246
- return [[key, scaleBuilder.buildStorage(module, method)]];
3247
- } catch (cause) {
3248
- log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
3249
- return [];
3250
- }
3251
- }));
3252
- return [[chainId, builtCoders]];
3253
- } catch (cause) {
3254
- log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
3255
- return [];
3256
- }
3257
- }));
3258
- // type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
3277
+ const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3278
+ const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
3279
+ storage: ["Assets", "Account"]
3280
+ });
3281
+ return balanceDefs.map(({
3282
+ token,
3283
+ address
3284
+ }) => {
3285
+ const scaleCoder = networkStorageCoders?.storage;
3286
+ const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
3287
+ // Asset Hub
3288
+ tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
3259
3289
 
3260
- const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
3261
- if (!miniMetadata.data) return null;
3262
- const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
3290
+ if (!stateKey) {
3291
+ log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3292
+ return null;
3293
+ }
3294
+ const decodeResult = changes => {
3295
+ /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3296
+
3297
+ const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
3298
+ balance: 0n,
3299
+ status: {
3300
+ type: "Liquid"
3301
+ }};
3302
+ const isFrozen = decoded?.status?.type === "Frozen";
3303
+ const amount = (decoded?.balance ?? 0n).toString();
3304
+
3305
+ // due to the following balance calculations, which are made in the `Balance` type:
3306
+ //
3307
+ // total balance = (free balance) + (reserved balance)
3308
+ // transferable balance = (free balance) - (frozen balance)
3309
+ //
3310
+ // when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
3311
+ // of this balance to the value we received from the RPC.
3312
+ //
3313
+ // if we only set the `frozen` amount, then the `total` calculation will be incorrect!
3314
+ const free = amount;
3315
+ const frozen = token.isFrozen || isFrozen ? amount : "0";
3316
+
3317
+ // include balance values even if zero, so that newly-zero values overwrite old values
3318
+ const balanceValues = [{
3319
+ type: "free",
3320
+ label: "free",
3321
+ amount: free.toString()
3322
+ }, {
3323
+ type: "locked",
3324
+ label: "frozen",
3325
+ amount: frozen.toString()
3326
+ }];
3327
+ const balance = {
3328
+ source: "substrate-assets",
3329
+ status: "live",
3330
+ address,
3331
+ networkId,
3332
+ tokenId: token.id,
3333
+ values: balanceValues
3334
+ };
3335
+ return balance;
3336
+ };
3337
+ return {
3338
+ stateKeys: [stateKey],
3339
+ decodeResult
3340
+ };
3341
+ }).filter(util.isNotNil);
3342
+ };
3343
+ const tryEncode$1 = (scaleCoder, ...args) => {
3263
3344
  try {
3264
- const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
3265
- const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
3266
- const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
3267
- chainId
3268
- }) : moduleMethodOrFn;
3269
- try {
3270
- return [[key, scaleBuilder.buildStorage(module, method)]];
3271
- } catch (cause) {
3272
- log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
3273
- return [];
3274
- }
3275
- }));
3276
- return builtCoders;
3277
- } catch (cause) {
3278
- log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
3345
+ return scaleCoder?.keys?.enc?.(...args);
3346
+ } catch {
3347
+ return null;
3279
3348
  }
3280
- return null;
3281
3349
  };
3282
3350
 
3283
- /**
3284
- * Decodes & unwraps outputs and errors of a given result, contract, and method.
3285
- * Parsed error message can be found in `decodedOutput` if `isError` is true.
3286
- * SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
3287
- */
3288
- function decodeOutput({
3289
- result
3290
- }, registry, abi, method) {
3291
- let output;
3292
- let decodedOutput = "";
3293
- let isError = true;
3294
- if (result.isOk) {
3295
- const flags = result.asOk.flags.toHuman();
3296
- isError = flags.includes("Revert");
3297
- const abiMessage = getAbiMessage(abi, method);
3298
- const returnType = abiMessage.returnType;
3299
- const returnTypeName = getReturnTypeName(returnType);
3300
- const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
3301
- output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
3302
- const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
3303
- const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
3304
- decodedOutput = isError ? errorText : okText;
3305
- } else if (result.isErr) {
3306
- output = result.toHuman();
3307
- let errorText;
3308
- if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
3309
- const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
3310
- errorText = `${errorKey}${errorValue}`;
3311
- }
3312
- decodedOutput = errorText || "Error";
3313
- }
3314
- return {
3315
- output,
3316
- decodedOutput,
3317
- isError
3351
+ const fetchBalances$6 = async ({
3352
+ networkId,
3353
+ tokensWithAddresses,
3354
+ connector,
3355
+ miniMetadata
3356
+ }) => {
3357
+ if (!tokensWithAddresses.length) return {
3358
+ success: [],
3359
+ errors: []
3318
3360
  };
3319
- }
3320
-
3321
- /**
3322
- * Helper types & functions
3323
- * SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
3324
- */
3325
-
3326
- function isErr(o) {
3327
- return typeof o === "object" && o !== null && "Err" in o;
3328
- }
3329
- function isOk(o) {
3330
- return typeof o === "object" && o !== null && "Ok" in o;
3331
- }
3332
- function getReturnTypeName(type) {
3333
- return type?.lookupName || type?.type || "";
3334
- }
3335
- function getAbiMessage(abi, method) {
3336
- const abiMessage = abi.messages.find(m => util$1.stringCamelCase(m.method) === util$1.stringCamelCase(method));
3337
- if (!abiMessage) {
3338
- throw new Error(`"${method}" not found in Contract`);
3339
- }
3340
- return abiMessage;
3341
- }
3342
-
3343
- /**
3344
- *
3345
- * Detect Balances::transfer -> Balances::transfer_allow_death migration
3346
- * https://github.com/paritytech/substrate/pull/12951
3347
- *
3348
- * `transfer_allow_death` is the preferred method,
3349
- * so if something goes wrong during detection, we should assume the chain has migrated
3350
- * @param metadataRpc string containing the hashed RPC metadata for the chain
3351
- * @returns
3352
- */
3353
- const detectTransferMethod = metadataRpc => {
3354
- const pjsMetadata = new types.Metadata(new types.TypeRegistry(), metadataRpc);
3355
- pjsMetadata.registry.setMetadata(pjsMetadata);
3356
- const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
3357
- const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
3358
- const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
3359
- const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
3360
- return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
3361
- };
3362
-
3363
- const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
3364
-
3365
- const makeContractCaller = ({
3366
- chainConnector,
3367
- chainId,
3368
- registry
3369
- }) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", util$1.u8aToHex(util$1.u8aConcatStrict([
3370
- // origin
3371
- registry.createType("AccountId", callFrom).toU8a(),
3372
- // dest
3373
- registry.createType("AccountId", contractAddress).toU8a(),
3374
- // value
3375
- registry.createType("Balance", 0).toU8a(),
3376
- // gasLimit
3377
- registry.createType("Option<WeightV2>").toU8a(),
3378
- // storageDepositLimit
3379
- registry.createType("Option<Balance>").toU8a(),
3380
- // inputData
3381
- inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
3382
-
3383
- /**
3384
- * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
3385
- */
3386
-
3387
- /**
3388
- * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
3389
- */
3390
- class RpcStateQueryHelper {
3391
- #chainConnector;
3392
- #queries;
3393
- constructor(chainConnector, queries) {
3394
- this.#chainConnector = chainConnector;
3395
- this.#queries = queries;
3396
- }
3397
- async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
3398
- const queriesByChain = groupBy__default.default(this.#queries, "chainId");
3399
- const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
3400
- const params = [queries.map(({
3401
- stateKey
3402
- }) => stateKey)];
3403
- const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
3404
- error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
3405
- }, timeout);
3406
- return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
3407
- });
3408
- return () => subscriptions.forEach(unsubscribe => unsubscribe());
3409
- }
3410
- async fetch(method = "state_queryStorageAt") {
3411
- const queriesByChain = groupBy__default.default(this.#queries, "chainId");
3412
- const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
3413
- const params = [queries.map(({
3414
- stateKey
3415
- }) => stateKey)];
3416
- const result = (await this.#chainConnector.send(chainId, method, params))[0];
3417
- return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
3418
- }));
3419
- return resultsByChain.flatMap(result => result);
3420
- }
3421
- #distributeChangesToQueryDecoders(chainId, result) {
3422
- if (typeof result !== "object" || result === null) return [];
3423
- if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
3424
- if (!Array.isArray(result.changes)) return [];
3425
- return result.changes.flatMap(([reference, change]) => {
3426
- if (typeof reference !== "string") {
3427
- log.warn(`Received non-string reference in RPC result: ${reference}`);
3428
- return [];
3429
- }
3430
- if (typeof change !== "string" && change !== null) {
3431
- log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
3432
- return [];
3433
- }
3434
- const query = this.#queries.find(({
3435
- chainId: cId,
3436
- stateKey
3437
- }) => cId === chainId && stateKey === reference);
3438
- if (!query) {
3439
- log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
3440
- stateKey
3441
- }) => stateKey)}`);
3442
- return [];
3443
- }
3444
- return [query.decodeResult(change)];
3445
- });
3446
- }
3447
- }
3448
-
3449
- const configureStore = (dbTable = db.balancesBlob) => ({
3450
- persistData: async balances => {
3451
- const output = compress(balances);
3452
- await dbTable.clear();
3453
- await dbTable.put({
3454
- data: output,
3455
- id: Date.now().toString()
3456
- });
3457
- },
3458
- retrieveData: async () => {
3459
- const compressedData = await dbTable.toCollection().first();
3460
- if (!compressedData) return [];
3461
- return decompress(compressedData.data);
3462
- }
3463
- });
3464
- const compress = balances => pako__default.default.deflate(JSON.stringify(balances));
3465
- const decompress = data => {
3466
- const decompressed = pako__default.default.inflate(data, {
3467
- to: "string"
3468
- });
3469
- return JSON.parse(decompressed);
3470
- };
3471
-
3472
- const fetchBalances$6 = async ({
3473
- networkId,
3474
- tokensWithAddresses,
3475
- connector,
3476
- miniMetadata
3477
- }) => {
3478
3361
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
3479
3362
  if (!miniMetadata?.data) {
3480
3363
  log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$5} balances on ${networkId}.`);
@@ -3509,8 +3392,8 @@ const fetchBalances$6 = async ({
3509
3392
  }))
3510
3393
  };
3511
3394
  }
3512
- const queries = buildQueries$7(networkId, balanceDefs, miniMetadata);
3513
- const balances = await new RpcStateQueryHelper(connector, queries).fetch();
3395
+ const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
3396
+ const balances = await fetchRpcQueryPack(connector, networkId, queries);
3514
3397
  return balanceDefs.reduce((acc, def) => {
3515
3398
  const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
3516
3399
  if (balance) acc.success.push(balance);
@@ -3537,80 +3420,6 @@ const fetchBalances$6 = async ({
3537
3420
  errors: []
3538
3421
  });
3539
3422
  };
3540
- const buildQueries$7 = (networkId, balanceDefs, miniMetadata) => {
3541
- const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
3542
- storage: ["Assets", "Account"]
3543
- });
3544
- return balanceDefs.map(({
3545
- token,
3546
- address
3547
- }) => {
3548
- const scaleCoder = networkStorageCoders?.storage;
3549
- const stateKey = tryEncode$2(scaleCoder, Number(token.assetId), address) ??
3550
- // Asset Hub
3551
- tryEncode$2(scaleCoder, BigInt(token.assetId), address); // Astar
3552
-
3553
- if (!stateKey) {
3554
- log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3555
- return null;
3556
- }
3557
- const decodeResult = change => {
3558
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3559
-
3560
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
3561
- balance: 0n,
3562
- status: {
3563
- type: "Liquid"
3564
- }};
3565
- const isFrozen = decoded?.status?.type === "Frozen";
3566
- const amount = (decoded?.balance ?? 0n).toString();
3567
-
3568
- // due to the following balance calculations, which are made in the `Balance` type:
3569
- //
3570
- // total balance = (free balance) + (reserved balance)
3571
- // transferable balance = (free balance) - (frozen balance)
3572
- //
3573
- // when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
3574
- // of this balance to the value we received from the RPC.
3575
- //
3576
- // if we only set the `frozen` amount, then the `total` calculation will be incorrect!
3577
- const free = amount;
3578
- const frozen = token.isFrozen || isFrozen ? amount : "0";
3579
-
3580
- // include balance values even if zero, so that newly-zero values overwrite old values
3581
- const balanceValues = [{
3582
- type: "free",
3583
- label: "free",
3584
- amount: free.toString()
3585
- }, {
3586
- type: "locked",
3587
- label: "frozen",
3588
- amount: frozen.toString()
3589
- }];
3590
- const balance = {
3591
- source: "substrate-assets",
3592
- status: "live",
3593
- address,
3594
- networkId,
3595
- tokenId: token.id,
3596
- values: balanceValues
3597
- };
3598
- return balance;
3599
- };
3600
- return {
3601
- chainId: networkId,
3602
- stateKey,
3603
- decodeResult
3604
- };
3605
- }).filter(util.isNotNil);
3606
- };
3607
- const tryEncode$2 = (scaleCoder, ...args) => {
3608
- try {
3609
- return scaleCoder?.keys?.enc?.(...args);
3610
- } catch {
3611
- return null;
3612
- }
3613
- };
3614
3423
 
3615
3424
  const fetchTokens$5 = async ({
3616
3425
  networkId,
@@ -3871,93 +3680,335 @@ const tryGetConstantValue = (metadataRpc, pallet, constant) => {
3871
3680
  return codec.dec(encodedValue);
3872
3681
  };
3873
3682
 
3874
- const fetchRpcQueryPack = async (connector, networkId, queries) => {
3875
- const allStateKeys = queries.flatMap(({
3876
- stateKeys
3877
- }) => stateKeys).filter(util.isNotNil);
3878
-
3879
- // doing a query without keys would throw an error => return early
3880
- if (!allStateKeys.length) return queries.map(({
3881
- stateKeys,
3882
- decodeResult
3883
- }) => decodeResult(stateKeys.map(() => null)));
3884
- const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
3885
- return decodeRpcQueryPack(queries, result);
3683
+ const subscribeBalances$5 = ({
3684
+ networkId,
3685
+ tokensWithAddresses,
3686
+ connector,
3687
+ miniMetadata
3688
+ }) => {
3689
+ if (!tokensWithAddresses.length) return rxjs.of({
3690
+ success: [],
3691
+ errors: []
3692
+ });
3693
+ const balanceDefs = getBalanceDefs(tokensWithAddresses);
3694
+ const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
3695
+ return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
3696
+ success: balances,
3697
+ errors: []
3698
+ })));
3886
3699
  };
3887
- const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
3888
- const allStateKeys = queries.flatMap(({
3889
- stateKeys
3890
- }) => stateKeys).filter(util.isNotNil);
3891
3700
 
3892
- // doing a query without keys would throw an error => return early
3893
- if (!allStateKeys.length) return rxjs.of(queries.map(({
3894
- stateKeys,
3895
- decodeResult
3896
- }) => decodeResult(stateKeys.map(() => null))));
3897
- return new rxjs.Observable(subscriber => {
3898
- const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
3899
- if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
3900
- }, timeout);
3901
- return () => {
3902
- promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
3903
- };
3904
- });
3701
+ const SubAssetsBalanceModule = {
3702
+ type: MODULE_TYPE$5,
3703
+ platform: PLATFORM$5,
3704
+ getMiniMetadata: getMiniMetadata$6,
3705
+ fetchTokens: fetchTokens$5,
3706
+ fetchBalances: fetchBalances$6,
3707
+ subscribeBalances: subscribeBalances$5,
3708
+ getTransferCallData: getTransferCallData$5
3905
3709
  };
3906
- const decodeRpcQueryPack = (queries, result) => {
3907
- return queries.reduce((acc, {
3908
- stateKeys,
3909
- decodeResult
3910
- }) => {
3911
- const changes = stateKeys.map(stateKey => {
3912
- if (!stateKey) return null;
3913
- const change = result.changes.find(([key]) => key === stateKey);
3914
- if (!change) return null;
3915
- return change[1];
3916
- });
3917
- acc.push(decodeResult(changes));
3918
- return acc;
3919
- }, []);
3710
+
3711
+ // to be used by chaindata too
3712
+ const SubAssetsTokenConfigSchema = z__default.default.strictObject({
3713
+ assetId: chaindataProvider.SubAssetsTokenSchema.shape.assetId,
3714
+ ...TokenConfigBaseSchema.shape
3715
+ });
3716
+
3717
+ const MODULE_TYPE$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.type.value;
3718
+ const PLATFORM$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.platform.value;
3719
+
3720
+ /**
3721
+ * Wraps a BalanceModule's fetch/subscribe methods with a single `balances` method.
3722
+ * This `balances` method will subscribe if a callback parameter is provided, or otherwise fetch.
3723
+ */
3724
+
3725
+ async function balances(balanceModule, addressesByToken, callback) {
3726
+ // subscription request
3727
+ if (callback !== undefined) return await balanceModule.subscribeBalances({
3728
+ addressesByToken
3729
+ }, callback);
3730
+
3731
+ // one-off request
3732
+ return await balanceModule.fetchBalances(addressesByToken);
3733
+ }
3734
+
3735
+ // TODO remove this one in favor of the network specific one below
3736
+ const buildStorageCoders = ({
3737
+ chainIds,
3738
+ chains,
3739
+ miniMetadatas,
3740
+ coders
3741
+ }) => new Map([...chainIds].flatMap(chainId => {
3742
+ const chain = chains[chainId];
3743
+ if (!chain) return [];
3744
+ const miniMetadata = miniMetadatas.get(chainId); // findMiniMetadata<TBalanceModule>(miniMetadatas, moduleType, chain)
3745
+ if (!miniMetadata) return [];
3746
+ if (!miniMetadata.data) return [];
3747
+ const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
3748
+ try {
3749
+ const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
3750
+ const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
3751
+ const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
3752
+ chainId
3753
+ }) : moduleMethodOrFn;
3754
+ try {
3755
+ return [[key, scaleBuilder.buildStorage(module, method)]];
3756
+ } catch (cause) {
3757
+ log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
3758
+ return [];
3759
+ }
3760
+ }));
3761
+ return [[chainId, builtCoders]];
3762
+ } catch (cause) {
3763
+ log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
3764
+ return [];
3765
+ }
3766
+ }));
3767
+ // type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
3768
+
3769
+ const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
3770
+ if (!miniMetadata.data) return null;
3771
+ const metadata = scale.unifyMetadata(scale.decAnyMetadata(miniMetadata.data));
3772
+ try {
3773
+ const scaleBuilder = scale.getDynamicBuilder(scale.getLookupFn(metadata));
3774
+ const builtCoders = Object.fromEntries(Object.entries(coders).flatMap(([key, moduleMethodOrFn]) => {
3775
+ const [module, method] = typeof moduleMethodOrFn === "function" ? moduleMethodOrFn({
3776
+ chainId
3777
+ }) : moduleMethodOrFn;
3778
+ try {
3779
+ return [[key, scaleBuilder.buildStorage(module, method)]];
3780
+ } catch (cause) {
3781
+ log.trace(`Failed to build SCALE coder for chain ${chainId} (${module}::${method})`, cause);
3782
+ return [];
3783
+ }
3784
+ }));
3785
+ return builtCoders;
3786
+ } catch (cause) {
3787
+ log.error(`Failed to build SCALE coders for chain ${chainId} (${JSON.stringify(coders)})`, cause);
3788
+ }
3789
+ return null;
3920
3790
  };
3921
3791
 
3922
- const subscribeBalances$5 = ({
3923
- networkId,
3924
- tokensWithAddresses,
3925
- connector,
3926
- miniMetadata
3927
- }) => {
3928
- const balanceDefs = getBalanceDefs(tokensWithAddresses);
3929
- const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
3930
- return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
3931
- success: balances,
3932
- errors: []
3933
- })));
3792
+ /**
3793
+ * Decodes & unwraps outputs and errors of a given result, contract, and method.
3794
+ * Parsed error message can be found in `decodedOutput` if `isError` is true.
3795
+ * SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
3796
+ */
3797
+ function decodeOutput({
3798
+ result
3799
+ }, registry, abi, method) {
3800
+ let output;
3801
+ let decodedOutput = "";
3802
+ let isError = true;
3803
+ if (result.isOk) {
3804
+ const flags = result.asOk.flags.toHuman();
3805
+ isError = flags.includes("Revert");
3806
+ const abiMessage = getAbiMessage(abi, method);
3807
+ const returnType = abiMessage.returnType;
3808
+ const returnTypeName = getReturnTypeName(returnType);
3809
+ const r = returnType ? registry.createTypeUnsafe(returnTypeName, [result.asOk.data]).toHuman() : "()";
3810
+ output = isOk(r) ? r.Ok : isErr(r) ? r.Err : r;
3811
+ const errorText = isErr(output) ? typeof output.Err === "object" ? JSON.stringify(output.Err, null, 2) : output.Err?.toString() ?? "Error" : output !== "Ok" ? output?.toString() || "Error" : "Error";
3812
+ const okText = isOk(r) ? typeof output === "object" ? JSON.stringify(output, null, "\t") : output?.toString() ?? "()" : JSON.stringify(output, null, "\t") ?? "()";
3813
+ decodedOutput = isError ? errorText : okText;
3814
+ } else if (result.isErr) {
3815
+ output = result.toHuman();
3816
+ let errorText;
3817
+ if (isErr(output) && typeof output.Err === "object" && Object.keys(output.Err || {}).length && typeof Object.values(output.Err || {})[0] === "string") {
3818
+ const [errorKey, errorValue] = Object.entries(output.Err || {})[0];
3819
+ errorText = `${errorKey}${errorValue}`;
3820
+ }
3821
+ decodedOutput = errorText || "Error";
3822
+ }
3823
+ return {
3824
+ output,
3825
+ decodedOutput,
3826
+ isError
3827
+ };
3828
+ }
3829
+
3830
+ /**
3831
+ * Helper types & functions
3832
+ * SOURCE: https://github.com/paritytech/contracts-ui (GPL-3.0-only)
3833
+ */
3834
+
3835
+ function isErr(o) {
3836
+ return typeof o === "object" && o !== null && "Err" in o;
3837
+ }
3838
+ function isOk(o) {
3839
+ return typeof o === "object" && o !== null && "Ok" in o;
3840
+ }
3841
+ function getReturnTypeName(type) {
3842
+ return type?.lookupName || type?.type || "";
3843
+ }
3844
+ function getAbiMessage(abi, method) {
3845
+ const abiMessage = abi.messages.find(m => util$1.stringCamelCase(m.method) === util$1.stringCamelCase(method));
3846
+ if (!abiMessage) {
3847
+ throw new Error(`"${method}" not found in Contract`);
3848
+ }
3849
+ return abiMessage;
3850
+ }
3851
+
3852
+ /**
3853
+ *
3854
+ * Detect Balances::transfer -> Balances::transfer_allow_death migration
3855
+ * https://github.com/paritytech/substrate/pull/12951
3856
+ *
3857
+ * `transfer_allow_death` is the preferred method,
3858
+ * so if something goes wrong during detection, we should assume the chain has migrated
3859
+ * @param metadataRpc string containing the hashed RPC metadata for the chain
3860
+ * @returns
3861
+ */
3862
+ const detectTransferMethod = metadataRpc => {
3863
+ const pjsMetadata = new types.Metadata(new types.TypeRegistry(), metadataRpc);
3864
+ pjsMetadata.registry.setMetadata(pjsMetadata);
3865
+ const balancesPallet = pjsMetadata.asLatest.pallets.find(pallet => pallet.name.eq("Balances"));
3866
+ const balancesCallsTypeIndex = balancesPallet?.calls.value.type.toNumber();
3867
+ const balancesCallsType = balancesCallsTypeIndex !== undefined ? pjsMetadata.asLatest.lookup.types[balancesCallsTypeIndex] : undefined;
3868
+ const hasDeprecatedTransferCall = balancesCallsType?.type.def.asVariant?.variants.find(variant => variant.name.eq("transfer")) !== undefined;
3869
+ return hasDeprecatedTransferCall ? "transfer" : "transfer_allow_death";
3934
3870
  };
3935
- const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3871
+
3872
+ const getUniqueChainIds = (addressesByToken, tokens) => [...new Set(Object.keys(addressesByToken).map(tokenId => tokens[tokenId]?.networkId).flatMap(chainId => chainId ? [chainId] : []))];
3873
+
3874
+ const makeContractCaller = ({
3875
+ chainConnector,
3876
+ chainId,
3877
+ registry
3878
+ }) => async (callFrom, contractAddress, inputData) => registry.createType("ContractExecResult", await chainConnector.send(chainId, "state_call", ["ContractsApi_call", util$1.u8aToHex(util$1.u8aConcatStrict([
3879
+ // origin
3880
+ registry.createType("AccountId", callFrom).toU8a(),
3881
+ // dest
3882
+ registry.createType("AccountId", contractAddress).toU8a(),
3883
+ // value
3884
+ registry.createType("Balance", 0).toU8a(),
3885
+ // gasLimit
3886
+ registry.createType("Option<WeightV2>").toU8a(),
3887
+ // storageDepositLimit
3888
+ registry.createType("Option<Balance>").toU8a(),
3889
+ // inputData
3890
+ inputData instanceof Uint8Array ? inputData : inputData.toU8a()]))]));
3891
+
3892
+ /**
3893
+ * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
3894
+ */
3895
+
3896
+ /**
3897
+ * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
3898
+ */
3899
+ class RpcStateQueryHelper {
3900
+ #chainConnector;
3901
+ #queries;
3902
+ constructor(chainConnector, queries) {
3903
+ this.#chainConnector = chainConnector;
3904
+ this.#queries = queries;
3905
+ }
3906
+ async subscribe(callback, timeout = false, subscribeMethod = "state_subscribeStorage", responseMethod = "state_storage", unsubscribeMethod = "state_unsubscribeStorage") {
3907
+ const queriesByChain = groupBy__default.default(this.#queries, "chainId");
3908
+ const subscriptions = Object.entries(queriesByChain).map(([chainId, queries]) => {
3909
+ const params = [queries.map(({
3910
+ stateKey
3911
+ }) => stateKey)];
3912
+ const unsub = this.#chainConnector.subscribe(chainId, subscribeMethod, responseMethod, params, (error, result) => {
3913
+ error ? callback(error) : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result));
3914
+ }, timeout);
3915
+ return () => unsub.then(unsubscribe => unsubscribe(unsubscribeMethod));
3916
+ });
3917
+ return () => subscriptions.forEach(unsubscribe => unsubscribe());
3918
+ }
3919
+ async fetch(method = "state_queryStorageAt") {
3920
+ const queriesByChain = groupBy__default.default(this.#queries, "chainId");
3921
+ const resultsByChain = await Promise.all(Object.entries(queriesByChain).map(async ([chainId, queries]) => {
3922
+ const params = [queries.map(({
3923
+ stateKey
3924
+ }) => stateKey)];
3925
+ const result = (await this.#chainConnector.send(chainId, method, params))[0];
3926
+ return this.#distributeChangesToQueryDecoders.call(this, chainId, result);
3927
+ }));
3928
+ return resultsByChain.flatMap(result => result);
3929
+ }
3930
+ #distributeChangesToQueryDecoders(chainId, result) {
3931
+ if (typeof result !== "object" || result === null) return [];
3932
+ if (!util.hasOwnProperty(result, "changes") || typeof result.changes !== "object") return [];
3933
+ if (!Array.isArray(result.changes)) return [];
3934
+ return result.changes.flatMap(([reference, change]) => {
3935
+ if (typeof reference !== "string") {
3936
+ log.warn(`Received non-string reference in RPC result: ${reference}`);
3937
+ return [];
3938
+ }
3939
+ if (typeof change !== "string" && change !== null) {
3940
+ log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`);
3941
+ return [];
3942
+ }
3943
+ const query = this.#queries.find(({
3944
+ chainId: cId,
3945
+ stateKey
3946
+ }) => cId === chainId && stateKey === reference);
3947
+ if (!query) {
3948
+ log.warn(`Failed to find query:\n${reference} in\n${this.#queries.map(({
3949
+ stateKey
3950
+ }) => stateKey)}`);
3951
+ return [];
3952
+ }
3953
+ return [query.decodeResult(change)];
3954
+ });
3955
+ }
3956
+ }
3957
+
3958
+ const configureStore = (dbTable = db.balancesBlob) => ({
3959
+ persistData: async balances => {
3960
+ const output = compress(balances);
3961
+ await dbTable.clear();
3962
+ await dbTable.put({
3963
+ data: output,
3964
+ id: Date.now().toString()
3965
+ });
3966
+ },
3967
+ retrieveData: async () => {
3968
+ const compressedData = await dbTable.toCollection().first();
3969
+ if (!compressedData) return [];
3970
+ return decompress(compressedData.data);
3971
+ }
3972
+ });
3973
+ const compress = balances => pako__default.default.deflate(JSON.stringify(balances));
3974
+ const decompress = data => {
3975
+ const decompressed = pako__default.default.inflate(data, {
3976
+ to: "string"
3977
+ });
3978
+ return JSON.parse(decompressed);
3979
+ };
3980
+
3981
+ const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
3936
3982
  const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
3937
- storage: ["Assets", "Account"]
3983
+ storage: ["ForeignAssets", "Account"]
3938
3984
  });
3939
3985
  return balanceDefs.map(({
3940
3986
  token,
3941
3987
  address
3942
3988
  }) => {
3943
3989
  const scaleCoder = networkStorageCoders?.storage;
3944
- const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
3945
- // Asset Hub
3946
- tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
3947
-
3990
+ const getStateKey = onChainId => {
3991
+ try {
3992
+ return scaleCoder?.keys?.enc?.(scale.papiParse(onChainId), address);
3993
+ } catch {
3994
+ return null;
3995
+ }
3996
+ };
3997
+ const stateKey = getStateKey(token.onChainId);
3948
3998
  if (!stateKey) {
3949
- log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3999
+ log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
3950
4000
  return null;
3951
- } else log.log(`VALID assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
4001
+ }
3952
4002
  const decodeResult = changes => {
3953
4003
  /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3954
4004
 
3955
4005
  const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
3956
4006
  balance: 0n,
4007
+ is_frozen: false,
3957
4008
  status: {
3958
4009
  type: "Liquid"
3959
4010
  }};
3960
- const isFrozen = decoded?.status?.type === "Frozen";
4011
+ const isFrozen = decoded.is_frozen ?? decoded?.status?.type === "Frozen";
3961
4012
  const amount = (decoded?.balance ?? 0n).toString();
3962
4013
 
3963
4014
  // due to the following balance calculations, which are made in the `Balance` type:
@@ -3998,32 +4049,6 @@ const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3998
4049
  };
3999
4050
  }).filter(util.isNotNil);
4000
4051
  };
4001
- const tryEncode$1 = (scaleCoder, ...args) => {
4002
- try {
4003
- return scaleCoder?.keys?.enc?.(...args);
4004
- } catch {
4005
- return null;
4006
- }
4007
- };
4008
-
4009
- const SubAssetsBalanceModule = {
4010
- type: MODULE_TYPE$5,
4011
- platform: PLATFORM$5,
4012
- getMiniMetadata: getMiniMetadata$6,
4013
- fetchTokens: fetchTokens$5,
4014
- fetchBalances: fetchBalances$6,
4015
- subscribeBalances: subscribeBalances$5,
4016
- getTransferCallData: getTransferCallData$5
4017
- };
4018
-
4019
- // to be used by chaindata too
4020
- const SubAssetsTokenConfigSchema = z__default.default.strictObject({
4021
- assetId: chaindataProvider.SubAssetsTokenSchema.shape.assetId,
4022
- ...TokenConfigBaseSchema.shape
4023
- });
4024
-
4025
- const MODULE_TYPE$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.type.value;
4026
- const PLATFORM$4 = chaindataProvider.SubForeignAssetsTokenSchema.shape.platform.value;
4027
4052
 
4028
4053
  const fetchBalances$5 = async ({
4029
4054
  networkId,
@@ -4031,6 +4056,10 @@ const fetchBalances$5 = async ({
4031
4056
  connector,
4032
4057
  miniMetadata
4033
4058
  }) => {
4059
+ if (!tokensWithAddresses.length) return {
4060
+ success: [],
4061
+ errors: []
4062
+ };
4034
4063
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
4035
4064
  if (!miniMetadata?.data) {
4036
4065
  log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$4} balances on ${networkId}.`);
@@ -4066,7 +4095,7 @@ const fetchBalances$5 = async ({
4066
4095
  };
4067
4096
  }
4068
4097
  const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
4069
- const balances = await new RpcStateQueryHelper(connector, queries).fetch();
4098
+ const balances = await fetchRpcQueryPack(connector, networkId, queries);
4070
4099
  return balanceDefs.reduce((acc, def) => {
4071
4100
  const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
4072
4101
  if (balance) acc.success.push(balance);
@@ -4075,95 +4104,23 @@ const fetchBalances$5 = async ({
4075
4104
  address: def.address,
4076
4105
  networkId,
4077
4106
  tokenId: def.token.id,
4078
- source: MODULE_TYPE$4,
4079
- status: "live",
4080
- values: [{
4081
- type: "free",
4082
- label: "free",
4083
- amount: "0"
4084
- }, {
4085
- type: "locked",
4086
- label: "frozen",
4087
- amount: "0"
4088
- }]
4089
- });
4090
- return acc;
4091
- }, {
4092
- success: [],
4093
- errors: []
4094
- });
4095
- };
4096
- const buildQueries$5 = (networkId, balanceDefs, miniMetadata) => {
4097
- const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
4098
- storage: ["ForeignAssets", "Account"]
4099
- });
4100
- return balanceDefs.map(({
4101
- token,
4102
- address
4103
- }) => {
4104
- const scaleCoder = networkStorageCoders?.storage;
4105
- const getStateKey = onChainId => {
4106
- try {
4107
- return scaleCoder?.keys?.enc?.(scale.papiParse(onChainId), address);
4108
- } catch {
4109
- return null;
4110
- }
4111
- };
4112
- const stateKey = getStateKey(token.onChainId);
4113
- if (!stateKey) {
4114
- log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
4115
- return null;
4116
- }
4117
- const decodeResult = change => {
4118
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
4119
-
4120
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
4121
- balance: 0n,
4122
- is_frozen: false,
4123
- status: {
4124
- type: "Liquid"
4125
- }};
4126
- const isFrozen = decoded.is_frozen ?? decoded?.status?.type === "Frozen";
4127
- const amount = (decoded?.balance ?? 0n).toString();
4128
-
4129
- // due to the following balance calculations, which are made in the `Balance` type:
4130
- //
4131
- // total balance = (free balance) + (reserved balance)
4132
- // transferable balance = (free balance) - (frozen balance)
4133
- //
4134
- // when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
4135
- // of this balance to the value we received from the RPC.
4136
- //
4137
- // if we only set the `frozen` amount, then the `total` calculation will be incorrect!
4138
- const free = amount;
4139
- const frozen = token.isFrozen || isFrozen ? amount : "0";
4140
-
4141
- // include balance values even if zero, so that newly-zero values overwrite old values
4142
- const balanceValues = [{
4143
- type: "free",
4144
- label: "free",
4145
- amount: free.toString()
4146
- }, {
4147
- type: "locked",
4148
- label: "frozen",
4149
- amount: frozen.toString()
4150
- }];
4151
- const balance = {
4152
- source: "substrate-assets",
4153
- status: "live",
4154
- address,
4155
- networkId,
4156
- tokenId: token.id,
4157
- values: balanceValues
4158
- };
4159
- return balance;
4160
- };
4161
- return {
4162
- chainId: networkId,
4163
- stateKey,
4164
- decodeResult
4165
- };
4166
- }).filter(util.isNotNil);
4107
+ source: MODULE_TYPE$4,
4108
+ status: "live",
4109
+ values: [{
4110
+ type: "free",
4111
+ label: "free",
4112
+ amount: "0"
4113
+ }, {
4114
+ type: "locked",
4115
+ label: "frozen",
4116
+ amount: "0"
4117
+ }]
4118
+ });
4119
+ return acc;
4120
+ }, {
4121
+ success: [],
4122
+ errors: []
4123
+ });
4167
4124
  };
4168
4125
 
4169
4126
  const fetchTokens$4 = async ({
@@ -4362,46 +4319,22 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
4362
4319
  })]);
4363
4320
  };
4364
4321
 
4365
- const SUBSCRIPTION_INTERVAL$3 = 6_000;
4366
4322
  const subscribeBalances$4 = ({
4367
4323
  networkId,
4368
4324
  tokensWithAddresses,
4369
4325
  connector,
4370
4326
  miniMetadata
4371
4327
  }) => {
4372
- return new rxjs.Observable(subscriber => {
4373
- const abortController = new AbortController();
4374
-
4375
- // on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
4376
- // => poll values for each block
4377
- const poll = async () => {
4378
- try {
4379
- if (abortController.signal.aborted) return;
4380
- const balances = await fetchBalances$5({
4381
- networkId,
4382
- tokensWithAddresses: tokensWithAddresses,
4383
- connector,
4384
- miniMetadata
4385
- });
4386
- if (abortController.signal.aborted) return;
4387
- subscriber.next(balances);
4388
- setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
4389
- } catch (error) {
4390
- log.error("Error", {
4391
- module: MODULE_TYPE$4,
4392
- networkId,
4393
- miniMetadata,
4394
- addressesByToken: tokensWithAddresses,
4395
- error
4396
- });
4397
- subscriber.error(error);
4398
- }
4399
- };
4400
- poll();
4401
- return () => {
4402
- abortController.abort();
4403
- };
4404
- }).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
4328
+ if (!tokensWithAddresses.length) return rxjs.of({
4329
+ success: [],
4330
+ errors: []
4331
+ });
4332
+ const balanceDefs = getBalanceDefs(tokensWithAddresses);
4333
+ const queries = buildQueries$5(networkId, balanceDefs, miniMetadata);
4334
+ return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
4335
+ success: balances,
4336
+ errors: []
4337
+ })));
4405
4338
  };
4406
4339
 
4407
4340
  const SubForeignAssetsBalanceModule = {
@@ -4435,9 +4368,15 @@ const fetchBalances$4 = async ({
4435
4368
  connector,
4436
4369
  miniMetadata
4437
4370
  }) => {
4371
+ if (!tokensWithAddresses.length) return {
4372
+ success: [],
4373
+ errors: []
4374
+ };
4438
4375
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
4439
4376
  if (!miniMetadata?.data) {
4440
- log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}.`);
4377
+ log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}.`, {
4378
+ tokensWithAddresses
4379
+ });
4441
4380
  return {
4442
4381
  success: [],
4443
4382
  errors: balanceDefs.map(def => ({
@@ -4493,7 +4432,7 @@ const fetchBalances$4 = async ({
4493
4432
  networkId,
4494
4433
  tokenId: token.id,
4495
4434
  source: MODULE_TYPE$3,
4496
- status: "cache",
4435
+ status: "live",
4497
4436
  values: [{
4498
4437
  type: "free",
4499
4438
  label: "free",
@@ -4684,13 +4623,17 @@ const getTransferCallData$3 = ({
4684
4623
  };
4685
4624
  };
4686
4625
 
4687
- const SUBSCRIPTION_INTERVAL$2 = 6_000;
4626
+ const SUBSCRIPTION_INTERVAL$1 = 6_000;
4688
4627
  const subscribeBalances$3 = ({
4689
4628
  networkId,
4690
4629
  tokensWithAddresses,
4691
4630
  connector,
4692
4631
  miniMetadata
4693
4632
  }) => {
4633
+ if (!tokensWithAddresses.length) return rxjs.of({
4634
+ success: [],
4635
+ errors: []
4636
+ });
4694
4637
  return new rxjs.Observable(subscriber => {
4695
4638
  const abortController = new AbortController();
4696
4639
 
@@ -4707,7 +4650,7 @@ const subscribeBalances$3 = ({
4707
4650
  });
4708
4651
  if (abortController.signal.aborted) return;
4709
4652
  subscriber.next(balances);
4710
- setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
4653
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$1);
4711
4654
  } catch (error) {
4712
4655
  log.error("Error", {
4713
4656
  module: MODULE_TYPE$3,
@@ -5456,6 +5399,10 @@ const fetchBalances$3 = async ({
5456
5399
  connector,
5457
5400
  miniMetadata
5458
5401
  }) => {
5402
+ if (!tokensWithAddresses.length) return {
5403
+ success: [],
5404
+ errors: []
5405
+ };
5459
5406
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
5460
5407
  if (!miniMetadata?.data) {
5461
5408
  log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$2} balances on ${networkId}.`);
@@ -5737,6 +5684,11 @@ const subscribeBalances$2 = ({
5737
5684
  connector,
5738
5685
  miniMetadata
5739
5686
  }) => {
5687
+ if (!tokensWithAddresses.length) return rxjs.of({
5688
+ success: [],
5689
+ errors: []
5690
+ });
5691
+
5740
5692
  // could be use as shared observable key if we decide to cache the sub
5741
5693
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
5742
5694
  const baseQueries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
@@ -6940,6 +6892,10 @@ const fetchBalances$2 = async ({
6940
6892
  tokensWithAddresses,
6941
6893
  connector
6942
6894
  }) => {
6895
+ if (!tokensWithAddresses.length) return {
6896
+ success: [],
6897
+ errors: []
6898
+ };
6943
6899
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
6944
6900
  if (!balanceDefs.length) return {
6945
6901
  success: [],
@@ -7127,13 +7083,17 @@ const getTransferCallData$1 = async ({
7127
7083
  };
7128
7084
  };
7129
7085
 
7130
- const SUBSCRIPTION_INTERVAL$1 = 6_000;
7086
+ const SUBSCRIPTION_INTERVAL = 6_000;
7131
7087
  const subscribeBalances$1 = ({
7132
7088
  networkId,
7133
7089
  tokensWithAddresses,
7134
7090
  connector,
7135
7091
  miniMetadata
7136
7092
  }) => {
7093
+ if (!tokensWithAddresses.length) return rxjs.of({
7094
+ success: [],
7095
+ errors: []
7096
+ });
7137
7097
  return new rxjs.Observable(subscriber => {
7138
7098
  const abortController = new AbortController();
7139
7099
 
@@ -7150,7 +7110,7 @@ const subscribeBalances$1 = ({
7150
7110
  });
7151
7111
  if (abortController.signal.aborted) return;
7152
7112
  subscriber.next(balances);
7153
- setTimeout(poll, SUBSCRIPTION_INTERVAL$1);
7113
+ setTimeout(poll, SUBSCRIPTION_INTERVAL);
7154
7114
  } catch (error) {
7155
7115
  log.error("Error", {
7156
7116
  module: MODULE_TYPE$1,
@@ -7188,12 +7148,77 @@ const SubPsp22TokenConfigSchema = z__default.default.strictObject({
7188
7148
  const MODULE_TYPE = chaindataProvider.SubTokensTokenSchema.shape.type.value;
7189
7149
  const PLATFORM = chaindataProvider.SubTokensTokenSchema.shape.platform.value;
7190
7150
 
7151
+ const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
7152
+ const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
7153
+ storage: [miniMetadata.extra.palletId, "Accounts"]
7154
+ });
7155
+ return balanceDefs.map(({
7156
+ token,
7157
+ address
7158
+ }) => {
7159
+ const scaleCoder = networkStorageCoders?.storage;
7160
+ const getStateKey = onChainId => {
7161
+ try {
7162
+ return scaleCoder.keys.enc(address, scale.papiParse(onChainId));
7163
+ } catch {
7164
+ return null;
7165
+ }
7166
+ };
7167
+ const stateKey = getStateKey(token.onChainId);
7168
+ if (!stateKey) {
7169
+ log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
7170
+ return null;
7171
+ }
7172
+ const decodeResult = changes => {
7173
+ /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
7174
+
7175
+ const decoded = scale.decodeScale(scaleCoder, changes[0], `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
7176
+ free: 0n,
7177
+ reserved: 0n,
7178
+ frozen: 0n
7179
+ };
7180
+ const free = (decoded?.free ?? 0n).toString();
7181
+ const reserved = (decoded?.reserved ?? 0n).toString();
7182
+ const frozen = (decoded?.frozen ?? 0n).toString();
7183
+ const balanceValues = [{
7184
+ type: "free",
7185
+ label: "free",
7186
+ amount: free.toString()
7187
+ }, {
7188
+ type: "reserved",
7189
+ label: "reserved",
7190
+ amount: reserved.toString()
7191
+ }, {
7192
+ type: "locked",
7193
+ label: "frozen",
7194
+ amount: frozen.toString()
7195
+ }];
7196
+ return {
7197
+ source: "substrate-tokens",
7198
+ status: "live",
7199
+ address,
7200
+ networkId,
7201
+ tokenId: token.id,
7202
+ values: balanceValues
7203
+ };
7204
+ };
7205
+ return {
7206
+ stateKeys: [stateKey],
7207
+ decodeResult
7208
+ };
7209
+ }).filter(util.isNotNil);
7210
+ };
7211
+
7191
7212
  const fetchBalances$1 = async ({
7192
7213
  networkId,
7193
7214
  tokensWithAddresses,
7194
7215
  connector,
7195
7216
  miniMetadata
7196
7217
  }) => {
7218
+ if (!tokensWithAddresses.length) return {
7219
+ success: [],
7220
+ errors: []
7221
+ };
7197
7222
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
7198
7223
  if (!miniMetadata?.data) {
7199
7224
  log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE} balances on ${networkId}.`);
@@ -7229,7 +7254,7 @@ const fetchBalances$1 = async ({
7229
7254
  };
7230
7255
  }
7231
7256
  const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
7232
- const balances = await new RpcStateQueryHelper(connector, queries).fetch();
7257
+ const balances = await fetchRpcQueryPack(connector, networkId, queries);
7233
7258
  return balanceDefs.reduce((acc, def) => {
7234
7259
  const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
7235
7260
  if (balance) acc.success.push(balance);
@@ -7256,67 +7281,6 @@ const fetchBalances$1 = async ({
7256
7281
  errors: []
7257
7282
  });
7258
7283
  };
7259
- const buildQueries$4 = (networkId, balanceDefs, miniMetadata) => {
7260
- const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
7261
- storage: [miniMetadata.extra.palletId, "Accounts"]
7262
- });
7263
- return balanceDefs.map(({
7264
- token,
7265
- address
7266
- }) => {
7267
- const scaleCoder = networkStorageCoders?.storage;
7268
- const getStateKey = onChainId => {
7269
- try {
7270
- return scaleCoder.keys.enc(address, scale.papiParse(onChainId));
7271
- } catch {
7272
- return null;
7273
- }
7274
- };
7275
- const stateKey = getStateKey(token.onChainId);
7276
- if (!stateKey) {
7277
- log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
7278
- return null;
7279
- }
7280
- const decodeResult = change => {
7281
- /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
7282
-
7283
- const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
7284
- free: 0n,
7285
- reserved: 0n,
7286
- frozen: 0n
7287
- };
7288
- const free = (decoded?.free ?? 0n).toString();
7289
- const reserved = (decoded?.reserved ?? 0n).toString();
7290
- const frozen = (decoded?.frozen ?? 0n).toString();
7291
- const balanceValues = [{
7292
- type: "free",
7293
- label: "free",
7294
- amount: free.toString()
7295
- }, {
7296
- type: "reserved",
7297
- label: "reserved",
7298
- amount: reserved.toString()
7299
- }, {
7300
- type: "locked",
7301
- label: "frozen",
7302
- amount: frozen.toString()
7303
- }];
7304
- return {
7305
- source: "substrate-tokens",
7306
- status: "live",
7307
- address,
7308
- networkId,
7309
- tokenId: token.id,
7310
- values: balanceValues
7311
- };
7312
- };
7313
- return {
7314
- chainId: networkId,
7315
- stateKey,
7316
- decodeResult
7317
- };
7318
- }).filter(util.isNotNil);
7319
- };
7320
7284
 
7321
7285
  const fetchTokens = async ({
7322
7286
  networkId,
@@ -7517,46 +7481,22 @@ const getCallDataOptions = (to, token, value, type, config) => {
7517
7481
  }] : []));
7518
7482
  };
7519
7483
 
7520
- const SUBSCRIPTION_INTERVAL = 6_000;
7521
7484
  const subscribeBalances = ({
7522
7485
  networkId,
7523
7486
  tokensWithAddresses,
7524
7487
  connector,
7525
7488
  miniMetadata
7526
7489
  }) => {
7527
- return new rxjs.Observable(subscriber => {
7528
- const abortController = new AbortController();
7529
-
7530
- // on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
7531
- // => poll values for each block
7532
- const poll = async () => {
7533
- try {
7534
- if (abortController.signal.aborted) return;
7535
- const balances = await fetchBalances$1({
7536
- networkId,
7537
- tokensWithAddresses: tokensWithAddresses,
7538
- connector,
7539
- miniMetadata
7540
- });
7541
- if (abortController.signal.aborted) return;
7542
- subscriber.next(balances);
7543
- setTimeout(poll, SUBSCRIPTION_INTERVAL);
7544
- } catch (error) {
7545
- log.error("Error", {
7546
- module: MODULE_TYPE,
7547
- networkId,
7548
- miniMetadata,
7549
- addressesByToken: tokensWithAddresses,
7550
- error
7551
- });
7552
- subscriber.error(error);
7553
- }
7554
- };
7555
- poll();
7556
- return () => {
7557
- abortController.abort();
7558
- };
7559
- }).pipe(rxjs.distinctUntilChanged(lodash.isEqual));
7490
+ if (!tokensWithAddresses.length) return rxjs.of({
7491
+ success: [],
7492
+ errors: []
7493
+ });
7494
+ const balanceDefs = getBalanceDefs(tokensWithAddresses);
7495
+ const queries = buildQueries$4(networkId, balanceDefs, miniMetadata);
7496
+ return getRpcQueryPack$(connector, networkId, queries).pipe(rxjs.map(balances => ({
7497
+ success: balances,
7498
+ errors: []
7499
+ })));
7560
7500
  };
7561
7501
 
7562
7502
  const SubTokensBalanceModule = {
@@ -10426,14 +10366,18 @@ class BalancesProvider {
10426
10366
  acc[networkId][tokenId] = addresses;
10427
10367
  return acc;
10428
10368
  }, {});
10429
- return rxjs.combineLatest(lodash.toPairs(addressesByTokenIdByNetworkId).map(([networkId]) => this.getNetworkBalances$(networkId, addressesByTokenIdByNetworkId[networkId]))).pipe(rxjs.map(results => {
10430
- return {
10431
- status: results.some(({
10432
- status
10433
- }) => status === "initialising") ? "initialising" : "live",
10434
- balances: results.flatMap(result => result.balances)
10435
- };
10436
- }), rxjs.startWith({
10369
+ return rxjs.combineLatest({
10370
+ isStale: rxjs.timer(30_000).pipe(rxjs.map(() => true), rxjs.startWith(false)),
10371
+ results: rxjs.combineLatest(lodash.toPairs(addressesByTokenIdByNetworkId).map(([networkId]) => this.getNetworkBalances$(networkId, addressesByTokenIdByNetworkId[networkId])))
10372
+ }).pipe(rxjs.map(({
10373
+ isStale,
10374
+ results
10375
+ }) => ({
10376
+ status: !isStale && results.some(({
10377
+ status
10378
+ }) => status === "initialising") ? "initialising" : "live",
10379
+ balances: results.flatMap(result => result.balances)
10380
+ })), rxjs.startWith({
10437
10381
  status: "initialising",
10438
10382
  balances: this.getStoredBalances(addressesByTokenId)
10439
10383
  }), rxjs.distinctUntilChanged(lodash.isEqual));