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

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.
@@ -11,9 +11,9 @@ import pako from 'pako';
11
11
  import { parseAbi, erc20Abi, getContract, ContractFunctionExecutionError, hexToString, erc20Abi_bytes32, encodeFunctionData, isHex, hexToBigInt, withRetry } from 'viem';
12
12
  import { assign, omit, isEqual, fromPairs, toPairs, keys, keyBy, uniq, values, groupBy as groupBy$1 } from 'lodash';
13
13
  import z from 'zod/v4';
14
- import { Observable, distinctUntilChanged, of, timer, switchMap, from, firstValueFrom, scan, share, map, switchAll, combineLatest, mergeMap, toArray, interval, startWith, exhaustMap, BehaviorSubject, debounceTime, takeUntil, withLatestFrom, concatMap, tap } from 'rxjs';
14
+ import { Observable, distinctUntilChanged, of, map, timer, switchMap, from, firstValueFrom, combineLatest, scan, share, switchAll, mergeMap, toArray, interval, startWith, exhaustMap, BehaviorSubject, debounceTime, takeUntil, withLatestFrom, concatMap, filter, tap } from 'rxjs';
15
15
  import isEqual$1 from 'lodash/isEqual';
16
- import { unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, decodeScale, parseMetadataRpc, getStorageKeyPrefix, compactMetadata, encodeMetadata, papiParse, papiStringify, toHex, getMetadataVersion, encodeStateKey } from '@talismn/scale';
16
+ import { unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, decodeScale, parseMetadataRpc, getStorageKeyPrefix, compactMetadata, encodeMetadata, toHex, papiParse, papiStringify, getMetadataVersion, encodeStateKey } from '@talismn/scale';
17
17
  import { Metadata, TypeRegistry } from '@polkadot/types';
18
18
  import groupBy from 'lodash/groupBy';
19
19
  import { mergeUint8, toHex as toHex$1 } from '@polkadot-api/utils';
@@ -1766,7 +1766,7 @@ const getTransferCallData$8 = ({
1766
1766
  };
1767
1767
  };
1768
1768
 
1769
- const SUBSCRIPTION_INTERVAL$8 = 6_000;
1769
+ const SUBSCRIPTION_INTERVAL$6 = 6_000;
1770
1770
  const subscribeBalances$8 = ({
1771
1771
  networkId,
1772
1772
  tokensWithAddresses,
@@ -1784,7 +1784,7 @@ const subscribeBalances$8 = ({
1784
1784
  });
1785
1785
  if (abortController.signal.aborted) return;
1786
1786
  subscriber.next(balances);
1787
- setTimeout(poll, SUBSCRIPTION_INTERVAL$8);
1787
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$6);
1788
1788
  } catch (error) {
1789
1789
  log.error("Error", {
1790
1790
  module: MODULE_TYPE$8,
@@ -1980,7 +1980,7 @@ const getTransferCallData$7 = ({
1980
1980
  };
1981
1981
  };
1982
1982
 
1983
- const SUBSCRIPTION_INTERVAL$7 = 6_000;
1983
+ const SUBSCRIPTION_INTERVAL$5 = 6_000;
1984
1984
  const subscribeBalances$7 = ({
1985
1985
  networkId,
1986
1986
  tokensWithAddresses,
@@ -1998,7 +1998,7 @@ const subscribeBalances$7 = ({
1998
1998
  });
1999
1999
  if (abortController.signal.aborted) return;
2000
2000
  subscriber.next(balances);
2001
- setTimeout(poll, SUBSCRIPTION_INTERVAL$7);
2001
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$5);
2002
2002
  } catch (error) {
2003
2003
  log.error("Error", {
2004
2004
  module: MODULE_TYPE$7,
@@ -2313,7 +2313,7 @@ const getTransferCallData$6 = ({
2313
2313
  };
2314
2314
  };
2315
2315
 
2316
- const SUBSCRIPTION_INTERVAL$6 = 6_000;
2316
+ const SUBSCRIPTION_INTERVAL$4 = 6_000;
2317
2317
  const subscribeBalances$6 = ({
2318
2318
  networkId,
2319
2319
  tokensWithAddresses,
@@ -2331,7 +2331,7 @@ const subscribeBalances$6 = ({
2331
2331
  });
2332
2332
  if (abortController.signal.aborted) return;
2333
2333
  subscriber.next(balances);
2334
- setTimeout(poll, SUBSCRIPTION_INTERVAL$6);
2334
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
2335
2335
  } catch (error) {
2336
2336
  log.error("Error", {
2337
2337
  module: MODULE_TYPE$6,
@@ -3464,7 +3464,7 @@ const fetchBalances$6 = async ({
3464
3464
  }) => {
3465
3465
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
3466
3466
  if (!miniMetadata?.data) {
3467
- log.warn("MiniMetadata is required for fetching balances");
3467
+ log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$5} balances on ${networkId}.`);
3468
3468
  return {
3469
3469
  success: [],
3470
3470
  errors: balanceDefs.map(def => ({
@@ -3496,7 +3496,7 @@ const fetchBalances$6 = async ({
3496
3496
  }))
3497
3497
  };
3498
3498
  }
3499
- const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
3499
+ const queries = buildQueries$7(networkId, balanceDefs, miniMetadata);
3500
3500
  const balances = await new RpcStateQueryHelper(connector, queries).fetch();
3501
3501
  return balanceDefs.reduce((acc, def) => {
3502
3502
  const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
@@ -3524,7 +3524,7 @@ const fetchBalances$6 = async ({
3524
3524
  errors: []
3525
3525
  });
3526
3526
  };
3527
- const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3527
+ const buildQueries$7 = (networkId, balanceDefs, miniMetadata) => {
3528
3528
  const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
3529
3529
  storage: ["Assets", "Account"]
3530
3530
  });
@@ -3533,9 +3533,9 @@ const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3533
3533
  address
3534
3534
  }) => {
3535
3535
  const scaleCoder = networkStorageCoders?.storage;
3536
- const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
3536
+ const stateKey = tryEncode$2(scaleCoder, Number(token.assetId), address) ??
3537
3537
  // Asset Hub
3538
- tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
3538
+ tryEncode$2(scaleCoder, BigInt(token.assetId), address); // Astar
3539
3539
 
3540
3540
  if (!stateKey) {
3541
3541
  log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
@@ -3591,7 +3591,7 @@ const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3591
3591
  };
3592
3592
  }).filter(isNotNil);
3593
3593
  };
3594
- const tryEncode$1 = (scaleCoder, ...args) => {
3594
+ const tryEncode$2 = (scaleCoder, ...args) => {
3595
3595
  try {
3596
3596
  return scaleCoder?.keys?.enc?.(...args);
3597
3597
  } catch {
@@ -3834,46 +3834,163 @@ const getTransferAllEncodedArgs$2 = (assetId, to, codec) => {
3834
3834
  })]);
3835
3835
  };
3836
3836
 
3837
- const SUBSCRIPTION_INTERVAL$5 = 6_000;
3837
+ const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
3838
+ const {
3839
+ builder
3840
+ } = parseMetadataRpc(metadataRpc);
3841
+ const call = builder.buildRuntimeCall(apiName, method);
3842
+ const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
3843
+ return call.value.dec(hex);
3844
+ };
3845
+
3846
+ const tryGetConstantValue = (metadataRpc, pallet, constant) => {
3847
+ const {
3848
+ unifiedMetadata,
3849
+ builder
3850
+ } = parseMetadataRpc(metadataRpc);
3851
+ const encodedValue = unifiedMetadata.pallets.find(({
3852
+ name
3853
+ }) => name === pallet)?.constants.find(({
3854
+ name
3855
+ }) => name === constant)?.value;
3856
+ if (!encodedValue) return null;
3857
+ const codec = builder.buildConstant(pallet, constant);
3858
+ return codec.dec(encodedValue);
3859
+ };
3860
+
3861
+ const fetchRpcQueryPack = async (connector, networkId, queries) => {
3862
+ const allStateKeys = queries.flatMap(({
3863
+ stateKeys
3864
+ }) => stateKeys).filter(isNotNil);
3865
+
3866
+ // doing a query without keys would throw an error => return early
3867
+ if (!allStateKeys.length) return queries.map(({
3868
+ stateKeys,
3869
+ decodeResult
3870
+ }) => decodeResult(stateKeys.map(() => null)));
3871
+ const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
3872
+ return decodeRpcQueryPack(queries, result);
3873
+ };
3874
+ const getRpcQueryPack$ = (connector, networkId, queries, timeout = false) => {
3875
+ const allStateKeys = queries.flatMap(({
3876
+ stateKeys
3877
+ }) => stateKeys).filter(isNotNil);
3878
+
3879
+ // doing a query without keys would throw an error => return early
3880
+ if (!allStateKeys.length) return of(queries.map(({
3881
+ stateKeys,
3882
+ decodeResult
3883
+ }) => decodeResult(stateKeys.map(() => null))));
3884
+ return new Observable(subscriber => {
3885
+ const promUnsub = connector.subscribe(networkId, "state_subscribeStorage", "state_storage", [allStateKeys], (error, result) => {
3886
+ if (error) subscriber.error(error);else subscriber.next(decodeRpcQueryPack(queries, result));
3887
+ }, timeout);
3888
+ return () => {
3889
+ promUnsub.then(unsub => unsub("state_unsubscribeStorage"));
3890
+ };
3891
+ });
3892
+ };
3893
+ const decodeRpcQueryPack = (queries, result) => {
3894
+ return queries.reduce((acc, {
3895
+ stateKeys,
3896
+ decodeResult
3897
+ }) => {
3898
+ const changes = stateKeys.map(stateKey => {
3899
+ if (!stateKey) return null;
3900
+ const change = result.changes.find(([key]) => key === stateKey);
3901
+ if (!change) return null;
3902
+ return change[1];
3903
+ });
3904
+ acc.push(decodeResult(changes));
3905
+ return acc;
3906
+ }, []);
3907
+ };
3908
+
3838
3909
  const subscribeBalances$5 = ({
3839
3910
  networkId,
3840
3911
  tokensWithAddresses,
3841
3912
  connector,
3842
3913
  miniMetadata
3843
3914
  }) => {
3844
- return new Observable(subscriber => {
3845
- const abortController = new AbortController();
3915
+ const balanceDefs = getBalanceDefs(tokensWithAddresses);
3916
+ const queries = buildQueries$6(networkId, balanceDefs, miniMetadata);
3917
+ return getRpcQueryPack$(connector, networkId, queries).pipe(map(balances => ({
3918
+ success: balances,
3919
+ errors: []
3920
+ })));
3921
+ };
3922
+ const buildQueries$6 = (networkId, balanceDefs, miniMetadata) => {
3923
+ const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
3924
+ storage: ["Assets", "Account"]
3925
+ });
3926
+ return balanceDefs.map(({
3927
+ token,
3928
+ address
3929
+ }) => {
3930
+ const scaleCoder = networkStorageCoders?.storage;
3931
+ const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ??
3932
+ // Asset Hub
3933
+ tryEncode$1(scaleCoder, BigInt(token.assetId), address); // Astar
3846
3934
 
3847
- // on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
3848
- // => poll values for each block
3849
- const poll = async () => {
3850
- try {
3851
- if (abortController.signal.aborted) return;
3852
- const balances = await fetchBalances$6({
3853
- networkId,
3854
- tokensWithAddresses: tokensWithAddresses,
3855
- connector,
3856
- miniMetadata
3857
- });
3858
- if (abortController.signal.aborted) return;
3859
- subscriber.next(balances);
3860
- setTimeout(poll, SUBSCRIPTION_INTERVAL$5);
3861
- } catch (error) {
3862
- log.error("Error", {
3863
- module: MODULE_TYPE$5,
3864
- networkId,
3865
- miniMetadata,
3866
- addressesByToken: tokensWithAddresses,
3867
- error
3868
- });
3869
- subscriber.error(error);
3870
- }
3935
+ if (!stateKey) {
3936
+ log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3937
+ return null;
3938
+ } else log.log(`VALID assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
3939
+ const decodeResult = changes => {
3940
+ /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
3941
+
3942
+ const decoded = decodeScale(scaleCoder, changes[0], `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
3943
+ balance: 0n,
3944
+ status: {
3945
+ type: "Liquid"
3946
+ }};
3947
+ const isFrozen = decoded?.status?.type === "Frozen";
3948
+ const amount = (decoded?.balance ?? 0n).toString();
3949
+
3950
+ // due to the following balance calculations, which are made in the `Balance` type:
3951
+ //
3952
+ // total balance = (free balance) + (reserved balance)
3953
+ // transferable balance = (free balance) - (frozen balance)
3954
+ //
3955
+ // when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
3956
+ // of this balance to the value we received from the RPC.
3957
+ //
3958
+ // if we only set the `frozen` amount, then the `total` calculation will be incorrect!
3959
+ const free = amount;
3960
+ const frozen = token.isFrozen || isFrozen ? amount : "0";
3961
+
3962
+ // include balance values even if zero, so that newly-zero values overwrite old values
3963
+ const balanceValues = [{
3964
+ type: "free",
3965
+ label: "free",
3966
+ amount: free.toString()
3967
+ }, {
3968
+ type: "locked",
3969
+ label: "frozen",
3970
+ amount: frozen.toString()
3971
+ }];
3972
+ const balance = {
3973
+ source: "substrate-assets",
3974
+ status: "live",
3975
+ address,
3976
+ networkId,
3977
+ tokenId: token.id,
3978
+ values: balanceValues
3979
+ };
3980
+ return balance;
3871
3981
  };
3872
- poll();
3873
- return () => {
3874
- abortController.abort();
3982
+ return {
3983
+ stateKeys: [stateKey],
3984
+ decodeResult
3875
3985
  };
3876
- }).pipe(distinctUntilChanged(isEqual));
3986
+ }).filter(isNotNil);
3987
+ };
3988
+ const tryEncode$1 = (scaleCoder, ...args) => {
3989
+ try {
3990
+ return scaleCoder?.keys?.enc?.(...args);
3991
+ } catch {
3992
+ return null;
3993
+ }
3877
3994
  };
3878
3995
 
3879
3996
  const SubAssetsBalanceModule = {
@@ -3903,7 +4020,7 @@ const fetchBalances$5 = async ({
3903
4020
  }) => {
3904
4021
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
3905
4022
  if (!miniMetadata?.data) {
3906
- log.warn("MiniMetadata is required for fetching balances");
4023
+ log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$4} balances on ${networkId}.`);
3907
4024
  return {
3908
4025
  success: [],
3909
4026
  errors: balanceDefs.map(def => ({
@@ -4232,7 +4349,7 @@ const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
4232
4349
  })]);
4233
4350
  };
4234
4351
 
4235
- const SUBSCRIPTION_INTERVAL$4 = 6_000;
4352
+ const SUBSCRIPTION_INTERVAL$3 = 6_000;
4236
4353
  const subscribeBalances$4 = ({
4237
4354
  networkId,
4238
4355
  tokensWithAddresses,
@@ -4255,7 +4372,7 @@ const subscribeBalances$4 = ({
4255
4372
  });
4256
4373
  if (abortController.signal.aborted) return;
4257
4374
  subscriber.next(balances);
4258
- setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
4375
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
4259
4376
  } catch (error) {
4260
4377
  log.error("Error", {
4261
4378
  module: MODULE_TYPE$4,
@@ -4299,30 +4416,6 @@ const SubHydrationTokenConfigSchema = z.strictObject({
4299
4416
  const MODULE_TYPE$3 = SubHydrationTokenSchema.shape.type.value;
4300
4417
  const PLATFORM$3 = SubHydrationTokenSchema.shape.platform.value;
4301
4418
 
4302
- const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
4303
- const {
4304
- builder
4305
- } = parseMetadataRpc(metadataRpc);
4306
- const call = builder.buildRuntimeCall(apiName, method);
4307
- const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
4308
- return call.value.dec(hex);
4309
- };
4310
-
4311
- const tryGetConstantValue = (metadataRpc, pallet, constant) => {
4312
- const {
4313
- unifiedMetadata,
4314
- builder
4315
- } = parseMetadataRpc(metadataRpc);
4316
- const encodedValue = unifiedMetadata.pallets.find(({
4317
- name
4318
- }) => name === pallet)?.constants.find(({
4319
- name
4320
- }) => name === constant)?.value;
4321
- if (!encodedValue) return null;
4322
- const codec = builder.buildConstant(pallet, constant);
4323
- return codec.dec(encodedValue);
4324
- };
4325
-
4326
4419
  const fetchBalances$4 = async ({
4327
4420
  networkId,
4328
4421
  tokensWithAddresses,
@@ -4331,7 +4424,7 @@ const fetchBalances$4 = async ({
4331
4424
  }) => {
4332
4425
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
4333
4426
  if (!miniMetadata?.data) {
4334
- log.warn("MiniMetadata is required for fetching balances");
4427
+ log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$3} balances on ${networkId}.`);
4335
4428
  return {
4336
4429
  success: [],
4337
4430
  errors: balanceDefs.map(def => ({
@@ -4578,7 +4671,7 @@ const getTransferCallData$3 = ({
4578
4671
  };
4579
4672
  };
4580
4673
 
4581
- const SUBSCRIPTION_INTERVAL$3 = 6_000;
4674
+ const SUBSCRIPTION_INTERVAL$2 = 6_000;
4582
4675
  const subscribeBalances$3 = ({
4583
4676
  networkId,
4584
4677
  tokensWithAddresses,
@@ -4601,7 +4694,7 @@ const subscribeBalances$3 = ({
4601
4694
  });
4602
4695
  if (abortController.signal.aborted) return;
4603
4696
  subscriber.next(balances);
4604
- setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
4697
+ setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
4605
4698
  } catch (error) {
4606
4699
  log.error("Error", {
4607
4700
  module: MODULE_TYPE$3,
@@ -4633,140 +4726,6 @@ const SubHydrationBalanceModule = {
4633
4726
  const MODULE_TYPE$2 = SubNativeTokenSchema.shape.type.value;
4634
4727
  const PLATFORM$2 = SubNativeTokenSchema.shape.platform.value;
4635
4728
 
4636
- /**
4637
- * Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
4638
- */
4639
-
4640
- const fetchQueriesPack = async (connector, networkId, queries) => {
4641
- const allStateKeys = queries.flatMap(({
4642
- stateKeys
4643
- }) => stateKeys).filter(isNotNil);
4644
-
4645
- // doing a query with only null keys would throw an error => return early
4646
- if (!allStateKeys.length) return queries.map(({
4647
- stateKeys,
4648
- decodeResult
4649
- }) => decodeResult(stateKeys.map(() => null)));
4650
- const response = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
4651
- const results = queries.reduce((acc, {
4652
- stateKeys,
4653
- decodeResult
4654
- }) => {
4655
- const changes = stateKeys.map(stateKey => {
4656
- if (!stateKey) return null;
4657
- const change = response[0].changes.find(([key]) => key === stateKey);
4658
- if (!change) return null;
4659
- return change[1];
4660
- });
4661
- acc.push(decodeResult(changes));
4662
- return acc;
4663
- }, []);
4664
- return results;
4665
- };
4666
-
4667
- /**
4668
- * Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
4669
- */
4670
- // export class RpcStateQueriesHelper<T> {
4671
- // #connector: ChainConnector
4672
- // #queries: Array<RpcStateQueries<T>>
4673
-
4674
- // constructor(connector: ChainConnector, queries: Array<RpcStateQueries<T>>) {
4675
- // this.#connector = connector
4676
- // this.#queries = queries
4677
- // }
4678
-
4679
- // async subscribe(
4680
- // networkId: DotNetworkId,
4681
- // callback: SubscriptionCallback<T[]>,
4682
- // timeout: number | false = false,
4683
- // subscribeMethod = "state_subscribeStorage",
4684
- // responseMethod = "state_storage",
4685
- // unsubscribeMethod = "state_unsubscribeStorage",
4686
- // ): Promise<UnsubscribeFn> {
4687
- // const params = [this.#queries.flatMap(({ stateKeys }) => stateKeys)]
4688
-
4689
- // const unsub = this.#connector.subscribe(
4690
- // networkId,
4691
- // subscribeMethod,
4692
- // responseMethod,
4693
- // params,
4694
- // (error, result) => {
4695
- // error
4696
- // ? callback(error)
4697
- // : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result))
4698
- // },
4699
- // timeout,
4700
- // )
4701
-
4702
- // const subscriptions = queries.map(([chainId, queries]) => {
4703
- // const params = [queries.map(({ stateKey }) => stateKey)]
4704
-
4705
- // const unsub = this.#connector.subscribe(
4706
- // networkId,
4707
- // subscribeMethod,
4708
- // responseMethod,
4709
- // params,
4710
- // (error, result) => {
4711
- // error
4712
- // ? callback(error)
4713
- // : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result))
4714
- // },
4715
- // timeout,
4716
- // )
4717
-
4718
- // return () => unsub.then((unsubscribe) => unsubscribe(unsubscribeMethod))
4719
- // })
4720
-
4721
- // return () => subscriptions.forEach((unsubscribe) => unsubscribe())
4722
- // }
4723
-
4724
- // async fetch(method = "state_queryStorageAt"): Promise<T[]> {
4725
- // const queriesByChain = groupBy(this.#queries, "chainId")
4726
-
4727
- // const resultsByChain = await Promise.all(
4728
- // Object.entries(queriesByChain).map(async ([chainId, queries]) => {
4729
- // const params = [queries.map(({ stateKey }) => stateKey)]
4730
-
4731
- // const result = (await this.#connector.send(chainId, method, params))[0]
4732
- // return this.#distributeChangesToQueryDecoders.call(this, chainId, result)
4733
- // }),
4734
- // )
4735
-
4736
- // return resultsByChain.flatMap((result) => result)
4737
- // }
4738
-
4739
- // #distributeChangesToQueryDecoders(chainId: DotNetworkId, result: unknown): T[] {
4740
- // if (typeof result !== "object" || result === null) return []
4741
- // if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return []
4742
- // if (!Array.isArray(result.changes)) return []
4743
-
4744
- // return result.changes.flatMap(([reference, change]: [unknown, unknown]): [T] | [] => {
4745
- // if (typeof reference !== "string") {
4746
- // log.warn(`Received non-string reference in RPC result: ${reference}`)
4747
- // return []
4748
- // }
4749
-
4750
- // if (typeof change !== "string" && change !== null) {
4751
- // log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`)
4752
- // return []
4753
- // }
4754
-
4755
- // const query = this.#queries.find(
4756
- // ({ chainId: cId, stateKey }) => cId === chainId && stateKey === reference,
4757
- // )
4758
- // if (!query) {
4759
- // log.warn(
4760
- // `Failed to find query:\n${reference} in\n${this.#queries.map(({ stateKey }) => stateKey)}`,
4761
- // )
4762
- // return []
4763
- // }
4764
-
4765
- // return [query.decodeResult(change)]
4766
- // })
4767
- // }
4768
- // }
4769
-
4770
4729
  const SUBTENSOR_ROOT_NETUID$1 = 0;
4771
4730
  const SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 = 1000000n;
4772
4731
  const TAO_DECIMALS$1 = 9n;
@@ -5486,7 +5445,7 @@ const fetchBalances$3 = async ({
5486
5445
  }) => {
5487
5446
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
5488
5447
  if (!miniMetadata?.data) {
5489
- log.warn("MiniMetadata is required for fetching balances");
5448
+ log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE$2} balances on ${networkId}.`);
5490
5449
  return {
5491
5450
  success: [],
5492
5451
  errors: balanceDefs.map(def => ({
@@ -5518,12 +5477,12 @@ const fetchBalances$3 = async ({
5518
5477
  }))
5519
5478
  };
5520
5479
  }
5521
- const queries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
5522
- const partialBalances = await fetchQueriesPack(connector, networkId, queries);
5480
+ const baseQueries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
5481
+ const partialBalances = await fetchRpcQueryPack(connector, networkId, baseQueries);
5523
5482
 
5524
5483
  // now for each balance that includes nomPoolStaking, we need to fetch the metadata for the pool
5525
5484
  const nomPoolQueries = buildNomPoolQueries(networkId, partialBalances, miniMetadata);
5526
- const balances = await fetchQueriesPack(connector, networkId, nomPoolQueries);
5485
+ const balances = await fetchRpcQueryPack(connector, networkId, nomPoolQueries);
5527
5486
 
5528
5487
  // TODO ⚠️ dedupe locks
5529
5488
 
@@ -5759,46 +5718,32 @@ const getTransferAllEncodedArgs = (to, codec) => {
5759
5718
  })]);
5760
5719
  };
5761
5720
 
5762
- const SUBSCRIPTION_INTERVAL$2 = 6_000;
5763
5721
  const subscribeBalances$2 = ({
5764
5722
  networkId,
5765
5723
  tokensWithAddresses,
5766
5724
  connector,
5767
5725
  miniMetadata
5768
5726
  }) => {
5769
- return new Observable(subscriber => {
5770
- const abortController = new AbortController();
5771
-
5772
- // on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
5773
- // => poll values for each block
5774
- const poll = async () => {
5775
- try {
5776
- if (abortController.signal.aborted) return;
5777
- const balances = await fetchBalances$3({
5778
- networkId,
5779
- tokensWithAddresses: tokensWithAddresses,
5780
- connector,
5781
- miniMetadata
5782
- });
5783
- if (abortController.signal.aborted) return;
5784
- subscriber.next(balances);
5785
- setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
5786
- } catch (error) {
5787
- log.error("Error", {
5788
- module: MODULE_TYPE$2,
5789
- networkId,
5790
- miniMetadata,
5791
- addressesByToken: tokensWithAddresses,
5792
- error
5793
- });
5794
- subscriber.error(error);
5795
- }
5796
- };
5797
- poll();
5798
- return () => {
5799
- abortController.abort();
5727
+ // could be use as shared observable key if we decide to cache the sub
5728
+ const balanceDefs = getBalanceDefs(tokensWithAddresses);
5729
+ const baseQueries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
5730
+ const baseBalances$ = getRpcQueryPack$(connector, networkId, baseQueries).pipe(switchMap(partialBalances => {
5731
+ // now for each balance that includes nomPoolStaking, we need to fetch the metadata for the pool
5732
+ const nomPoolQueries = buildNomPoolQueries(networkId, partialBalances, miniMetadata);
5733
+ return getRpcQueryPack$(connector, networkId, nomPoolQueries);
5734
+ }));
5735
+ const subtensorBalancesByAddress$ = getSubtensorStakingBalances$(connector, networkId, balanceDefs, miniMetadata);
5736
+ return combineLatest([baseBalances$, subtensorBalancesByAddress$]).pipe(map(([baseBalances, subtensorBalancesByAddress]) => {
5737
+ // add subtensor balances to base balances
5738
+ for (const [address, subtensorBalances] of toPairs(subtensorBalancesByAddress)) {
5739
+ const balance = baseBalances.find(b => b.address === address);
5740
+ if (balance?.values) balance.values.push(...subtensorBalances);
5741
+ }
5742
+ return {
5743
+ success: baseBalances,
5744
+ errors: []
5800
5745
  };
5801
- }).pipe(distinctUntilChanged(isEqual));
5746
+ }));
5802
5747
  };
5803
5748
 
5804
5749
  const SubNativeBalanceModule = {
@@ -7238,7 +7183,7 @@ const fetchBalances$1 = async ({
7238
7183
  }) => {
7239
7184
  const balanceDefs = getBalanceDefs(tokensWithAddresses);
7240
7185
  if (!miniMetadata?.data) {
7241
- log.warn("MiniMetadata is required for fetching balances");
7186
+ log.warn(`MiniMetadata is required for fetching ${MODULE_TYPE} balances on ${networkId}.`);
7242
7187
  return {
7243
7188
  success: [],
7244
7189
  errors: balanceDefs.map(def => ({
@@ -10458,9 +10403,17 @@ class BalancesProvider {
10458
10403
  miniMetadatas: values(miniMetadatas).filter(isNotNil)
10459
10404
  })));
10460
10405
  }
10461
- getBalances$(addressesByToken) {
10462
- const networkIds = uniq(keys(addressesByToken).map(tokenId => parseTokenId(tokenId).networkId));
10463
- return combineLatest(networkIds.map(networkId => this.getNetworkBalances$(networkId, addressesByToken))).pipe(map(results => {
10406
+
10407
+ // this is the only public method
10408
+ getBalances$(addressesByTokenId) {
10409
+ // split by network
10410
+ const addressesByTokenIdByNetworkId = toPairs(addressesByTokenId).reduce((acc, [tokenId, addresses]) => {
10411
+ const networkId = parseTokenId(tokenId).networkId;
10412
+ if (!acc[networkId]) acc[networkId] = {};
10413
+ acc[networkId][tokenId] = addresses;
10414
+ return acc;
10415
+ }, {});
10416
+ return combineLatest(toPairs(addressesByTokenIdByNetworkId).map(([networkId]) => this.getNetworkBalances$(networkId, addressesByTokenIdByNetworkId[networkId]))).pipe(map(results => {
10464
10417
  return {
10465
10418
  status: results.some(({
10466
10419
  status
@@ -10469,9 +10422,17 @@ class BalancesProvider {
10469
10422
  };
10470
10423
  }), startWith({
10471
10424
  status: "initialising",
10472
- balances: this.getStoredBalances(addressesByToken)
10425
+ balances: this.getStoredBalances(addressesByTokenId)
10473
10426
  }), distinctUntilChanged(isEqual));
10474
10427
  }
10428
+ fetchBalances(addressesByTokenId) {
10429
+ // TODO: better
10430
+ return firstValueFrom(this.getBalances$(addressesByTokenId).pipe(filter(({
10431
+ status
10432
+ }) => status === "live"), map(({
10433
+ balances
10434
+ }) => balances)));
10435
+ }
10475
10436
  getNetworkBalances$(networkId, addressesByTokenId) {
10476
10437
  const network$ = this.#chaindataProvider.getNetworkById$(networkId);
10477
10438
  const tokensMapById$ = this.#chaindataProvider.getTokensMapById$();
@@ -10543,7 +10504,42 @@ class BalancesProvider {
10543
10504
  }));
10544
10505
  }
10545
10506
  getNetworkMiniMetadatas$(networkId) {
10546
- return this.#chaindataProvider.getNetworkById$(networkId).pipe(switchMap(network => isNetworkDot(network) && this.#chainConnectors.substrate ? from(getMiniMetadatas(this.#chainConnectors.substrate, this.#chaindataProvider, networkId)) : of([])));
10507
+ return this.#chaindataProvider.getNetworkById$(networkId).pipe(switchMap(network => isNetworkDot(network) && this.#chainConnectors.substrate ? from(getSpecVersion(this.#chainConnectors.substrate, networkId)).pipe(switchMap(specVersion => this.getMiniMetadatas$(networkId, specVersion))) : of([])));
10508
+ }
10509
+ getMiniMetadatas$(networkId, specVersion) {
10510
+ return combineLatest({
10511
+ defaultMiniMetadatas: this.getDefaultMiniMetadatas$(networkId, specVersion),
10512
+ storedMiniMetadatas: this.getStoredMiniMetadatas$(networkId, specVersion)
10513
+ }).pipe(switchMap(({
10514
+ storedMiniMetadatas,
10515
+ defaultMiniMetadatas
10516
+ }) => {
10517
+ if (defaultMiniMetadatas.length) return of(defaultMiniMetadatas);
10518
+ if (storedMiniMetadatas.length) return of(storedMiniMetadatas);
10519
+ if (!this.#chainConnectors.substrate) return of([]);
10520
+ return from(
10521
+ // fetch them from the chain
10522
+ getMiniMetadatas(this.#chainConnectors.substrate, this.#chaindataProvider, networkId)).pipe(
10523
+ // and persist in storage for later reuse
10524
+ tap(newMiniMetadatas => {
10525
+ if (!newMiniMetadatas.length) return;
10526
+ const storage = this.#storage.getValue();
10527
+ const miniMetadatas = assign(
10528
+ // keep minimetadatas of other networks
10529
+ keyBy(values(storage.miniMetadatas).filter(m => m.chainId !== networkId), m => m.id),
10530
+ // add the ones for our network
10531
+ keyBy(newMiniMetadatas, m => m.id));
10532
+ this.#storage.next(assign({}, storage, {
10533
+ miniMetadatas
10534
+ }));
10535
+ }));
10536
+ }));
10537
+ }
10538
+ getStoredMiniMetadatas$(networkId, specVersion) {
10539
+ return this.storage$.pipe(map(storage => storage.miniMetadatas.filter(m => m.chainId === networkId && m.specVersion === specVersion && m.version === MINIMETADATA_VERSION)), distinctUntilChanged(isEqual));
10540
+ }
10541
+ getDefaultMiniMetadatas$(networkId, specVersion) {
10542
+ return this.#chaindataProvider.miniMetadatas$.pipe(map(miniMetadatas => miniMetadatas.filter(m => m.chainId === networkId && m.specVersion === specVersion && m.version === MINIMETADATA_VERSION)), distinctUntilChanged(isEqual));
10547
10543
  }
10548
10544
  getStoredBalances(addressesByToken) {
10549
10545
  const balanceDefs = toPairs(addressesByToken).flatMap(([tokenId, addresses]) => addresses.map(address => [tokenId, address]));
@@ -10554,11 +10550,4 @@ class BalancesProvider {
10554
10550
  }
10555
10551
  }
10556
10552
 
10557
- // const getStoredBalances = (
10558
- // storedBalances: Record<string, IBalance>,
10559
- // addressesByToken: Record<TokenId, Address[]>,
10560
- // ): IBalance[] => {
10561
-
10562
- // }
10563
-
10564
10553
  export { BALANCE_MODULES, Balance, BalanceFormatter, BalanceValueGetter, Balances, BalancesProvider, Change24hCurrencyFormatter, DefaultBalanceModule, EvmErc20BalanceModule, EvmErc20TokenConfigSchema, EvmNativeBalanceModule, EvmNativeTokenConfigSchema, EvmUniswapV2BalanceModule, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, ONE_ALPHA_TOKEN$1 as ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, RpcStateQueryHelper, SCALE_FACTOR$1 as SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 as SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID$1 as SUBTENSOR_ROOT_NETUID, SubAssetsBalanceModule, SubAssetsTokenConfigSchema, SubForeignAssetsBalanceModule, SubForeignAssetsTokenConfigSchema, SubHydrationBalanceModule, SubHydrationTokenConfigSchema, SubNativeBalanceModule, SubNativeMiniMetadataExtraSchema, SubNativeModuleConfigSchema, SubNativeTokenConfigSchema, SubPsp22BalanceModule, SubPsp22TokenConfigSchema, SubTokensBalanceModule, SubTokensMiniMetadataExtraSchema, SubTokensModuleConfigSchema, SubTokensTokenConfigSchema, SumBalancesFormatter, TalismanBalancesDatabase, abiMulticall, balances, buildNetworkStorageCoders, buildStorageCoders, calculateAlphaPrice$1 as calculateAlphaPrice, calculateTaoAmountFromAlpha$1 as calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo$1 as calculateTaoFromDynamicInfo, compress, configureStore, db, decodeOutput, decompress, defaultBalanceModules, deriveMiniMetadataId, detectTransferMethod, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getLockedType$1 as getLockedType, getUniqueChainIds, getValueId, includeInTotalExtraAmount, makeContractCaller, uniswapV2PairAbi };