@talismn/balances 3.0.0 → 3.0.1

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.
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import BigNumber from "bignumber.js";
2
2
  import { EvmErc20TokenSchema, EvmNativeTokenSchema, EvmUniswapV2TokenSchema, MINIMETADATA_VERSION, MINIMETADATA_VERSION as MINIMETADATA_VERSION$1, SolNativeTokenSchema, SolSplTokenSchema, SolToken2022TokenSchema, SubAssetsTokenSchema, SubDTaoTokenSchema, SubForeignAssetsTokenSchema, SubHydrationTokenSchema, SubNativeTokenSchema, SubPsp22TokenSchema, SubTokensTokenSchema, TokenBaseSchema, TokenSchema, evmErc20TokenId, evmNativeTokenId, evmUniswapV2TokenId, getCleanToken, isNetworkDot, isTokenOfType, parseEvmErc20TokenId, parseSolSplTokenId, parseSolToken2022TokenId, parseSubDTaoTokenId, parseTokenId, solNativeTokenId, solSplTokenId, solToken2022TokenId, subAssetTokenId, subDTaoTokenId, subForeignAssetTokenId, subHydrationTokenId, subNativeTokenId, subPsp22TokenId, subTokensTokenId } from "@talismn/chaindata-provider";
3
- import { getAccountPlatformFromAddress, isEthereumAddress, isOnCurveSolanaAddress, isSolanaAddress, normalizeAddress } from "@talismn/crypto";
3
+ import { getAccountPlatformFromAddress, isAddressEqual, isEthereumAddress, isOnCurveSolanaAddress, isSolanaAddress, normalizeAddress } from "@talismn/crypto";
4
4
  import { BigMath, LruMap, createTimeSlicer, forEachWithYield, getSharedObservable, isAbortError, isArrayOf, isBigInt, isErrorOfName, isNotNil, isTruthy, keepAlive, mapWithYield, planckToTokens, reportJsActivity, switchMapChunked, yieldToEventLoop } from "@talismn/util";
5
- import { assign, fromPairs, isEqual, keyBy, keys, omit, toPairs, uniq, values } from "lodash-es";
5
+ import { assign, fromPairs, isEqual, keyBy, keys, mergeWith, omit, toPairs, uniq, values } from "lodash-es";
6
6
  import { BehaviorSubject, EMPTY, Observable, ReplaySubject, auditTime, catchError, combineLatest, defer, distinctUntilChanged, filter, firstValueFrom, from, map, of, shareReplay, startWith, switchMap, tap, timer } from "rxjs";
7
7
  import { encodeFunctionData, erc20Abi, erc20Abi_bytes32, getContract, hexToString, parseAbi, withRetry } from "viem";
8
8
  import PQueue from "p-queue";
@@ -1750,6 +1750,21 @@ const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
1750
1750
  }));
1751
1751
  };
1752
1752
  //#endregion
1753
+ //#region src/modules/shared/fetchBestBlockHash.ts
1754
+ /**
1755
+ * Hash of the current best block, to pin a poll's reads to.
1756
+ *
1757
+ * Chain state moves between the round-trips of a multi-call poll, and unpinned reads each
1758
+ * run against whatever block is best when they land. Combining values read at different
1759
+ * blocks fabricates data (eg a dtao basket claim total read one block after its
1760
+ * per-validator breakdown leaves an unattributed remainder).
1761
+ */
1762
+ const fetchBestBlockHash = async (connector, networkId) => {
1763
+ const blockHash = await connector.send(networkId, "chain_getBlockHash", []);
1764
+ if (!blockHash) throw new Error(`Failed to fetch best block hash on ${networkId}`);
1765
+ return blockHash;
1766
+ };
1767
+ //#endregion
1753
1768
  //#region src/modules/shared/parseMetadataRpcCached.ts
1754
1769
  /**
1755
1770
  * parseMetadataRpc does a full metadata decode + dynamic-builder build (tens to hundreds
@@ -1797,11 +1812,15 @@ const getRuntimeCall = (builder, apiName, method) => {
1797
1812
  }
1798
1813
  return call;
1799
1814
  };
1800
- const fetchRuntimeCallResult = async (connector, networkId, metadataRpcOrBuilder, apiName, method, args) => {
1815
+ const fetchRuntimeCallResult = async (connector, networkId, metadataRpcOrBuilder, apiName, method, args, at) => {
1801
1816
  try {
1802
1817
  const builder = typeof metadataRpcOrBuilder === "string" ? parseMetadataRpcCached(metadataRpcOrBuilder).builder : metadataRpcOrBuilder;
1803
1818
  const call = getRuntimeCall(builder, apiName, method);
1804
- const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex(call.args.enc(args))]);
1819
+ const hex = await connector.send(networkId, "state_call", [
1820
+ `${apiName}_${method}`,
1821
+ toHex(call.args.enc(args)),
1822
+ ...at ? [at] : []
1823
+ ]);
1805
1824
  const start = performance.now();
1806
1825
  const result = call.value.dec(hex);
1807
1826
  reportJsActivity(`runtimeCall decode ${networkId} ${apiName}.${method} (~${Math.round((hex?.length ?? 0) / 2048)}KB)`, performance.now() - start);
@@ -1812,10 +1831,10 @@ const fetchRuntimeCallResult = async (connector, networkId, metadataRpcOrBuilder
1812
1831
  };
1813
1832
  //#endregion
1814
1833
  //#region src/modules/shared/rpcQueryPack.ts
1815
- const fetchRpcQueryPack = async (connector, networkId, queries) => {
1834
+ const fetchRpcQueryPack = async (connector, networkId, queries, at) => {
1816
1835
  const allStateKeys = queries.flatMap(({ stateKeys }) => stateKeys).filter(isNotNil);
1817
1836
  if (!allStateKeys.length) return queries.map(({ stateKeys, decodeResult }) => decodeResult(stateKeys.map(() => null)));
1818
- const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
1837
+ const [result] = await connector.send(networkId, "state_queryStorageAt", at ? [allStateKeys, at] : [allStateKeys]);
1819
1838
  if (!result) throw new Error(`Empty state_queryStorageAt response on ${networkId}`);
1820
1839
  return decodeRpcQueryPackChunked(queries, new Map(result.changes), { label: `rpcQueryPack decode ${networkId}` });
1821
1840
  };
@@ -3899,6 +3918,74 @@ const taoToAlphaCeil = (tao, scaledAlphaPrice) => {
3899
3918
  return (tao * ALPHA_PRICE_SCALE + scaledAlphaPrice - 1n) / scaledAlphaPrice;
3900
3919
  };
3901
3920
  //#endregion
3921
+ //#region src/modules/substrate-dtao/basketClaims.ts
3922
+ /** label of the claimable-rewards values emitted on root staking balances */
3923
+ const CLAIMABLE_REWARDS_LABEL = "Claimable rewards";
3924
+ /** Whether a lock is a claimable root rewards entitlement */
3925
+ const isDTaoClaimableLock = (lock) => lock.label === CLAIMABLE_REWARDS_LABEL;
3926
+ /** Sum of a balance's claimable root rewards (TAO plancks, marked NAV quote) */
3927
+ const getDTaoClaimablePlancks = (locks) => (locks ?? []).filter(isDTaoClaimableLock).reduce((sum, lock) => sum + lock.amount.planck, 0n);
3928
+ /**
3929
+ * The target pair's claimable rewards, null once its entitlement is gone (eg claimed elsewhere).
3930
+ * Sourced from balances rather than staking positions: the chain keeps basket entitlement
3931
+ * after a full unstake, so a claim can have no stake left on its validator.
3932
+ */
3933
+ const findDTaoClaimablePlancks = (balances, { networkId, address, hotkey }) => {
3934
+ const balance = balances.find((b) => b.token?.type === "substrate-dtao" && b.token.networkId === networkId && b.token.netuid === 0 && b.token.hotkey === hotkey && isAddressEqual(b.address, address));
3935
+ const claimablePlancks = balance ? getDTaoClaimablePlancks(balance.locks) : 0n;
3936
+ return claimablePlancks > 0n ? claimablePlancks : null;
3937
+ };
3938
+ /**
3939
+ * Fetches the TAO each coldkey would realize by redeeming its validator beta baskets
3940
+ * (Bittensor spec 441 "Root Reborn": root dividends accrue in per-validator escrow funds
3941
+ * and must be claimed manually; the payout is TAO staked back onto the root position).
3942
+ *
3943
+ * Amounts are marked NAV quotes (BetaBasketRuntimeApi), so they move with subnet pool
3944
+ * prices as well as accrual. Attribution is per validator hotkey via
3945
+ * `get_root_basket_positions`, which walks the chain's own coldkey→hotkeys index and so
3946
+ * includes validators the coldkey fully unstaked from (entitlement survives unstaking).
3947
+ *
3948
+ * It is the only entitlement read: the coldkey-wide `get_root_basket_owed` total sums the
3949
+ * same positions, and claiming is per validator hotkey, so entitlement outside a position
3950
+ * would be unclaimable anyway.
3951
+ */
3952
+ const fetchBasketClaims = async (connector, networkId, metadataRpc, addresses, at) => {
3953
+ if (!addresses.length) return [];
3954
+ const { unifiedMetadata, builder } = parseMetadataRpcCached(metadataRpc);
3955
+ if (!hasRuntimeApi(unifiedMetadata, "BetaBasketRuntimeApi", "get_root_basket_positions")) return [];
3956
+ try {
3957
+ return (await Promise.all(addresses.map(async (address) => [address, await fetchRuntimeCallResult(connector, networkId, builder, "BetaBasketRuntimeApi", "get_root_basket_positions", [address], at)]))).flatMap(([address, positions]) => positions.filter(([, , payoutTao]) => payoutTao > 0n).map(([hotkey, , payoutTao]) => ({
3958
+ address,
3959
+ hotkey,
3960
+ amount: payoutTao
3961
+ })));
3962
+ } catch (cause) {
3963
+ log_default.warn(`Failed to fetch beta basket claims on ${networkId}`, { cause });
3964
+ throw cause;
3965
+ }
3966
+ };
3967
+ //#endregion
3968
+ //#region src/modules/substrate-dtao/fetchStorageKeysPaged.ts
3969
+ const PAGE_SIZE = 1e3;
3970
+ /**
3971
+ * Prefix scan via state_getKeysPaged. The unpaged state_getKeys is classified as heavy storage
3972
+ * work and rejected by some public nodes (eg the bittensor testnet one: "Storage work rate limit
3973
+ * exceeded"), while the paged variant is universally allowed.
3974
+ */
3975
+ const fetchStorageKeysPaged = async (connector, networkId, keyPrefix, at) => {
3976
+ const keys = [];
3977
+ let startKey;
3978
+ do {
3979
+ const params = [keyPrefix, PAGE_SIZE];
3980
+ if (startKey || at) params.push(startKey ?? null);
3981
+ if (at) params.push(at);
3982
+ const page = await connector.send(networkId, "state_getKeysPaged", params);
3983
+ keys.push(...page);
3984
+ startKey = page.length === PAGE_SIZE ? page[page.length - 1] : void 0;
3985
+ } while (startKey);
3986
+ return keys;
3987
+ };
3988
+ //#endregion
3902
3989
  //#region src/modules/substrate-dtao/convictionLocks.ts
3903
3990
  const convictionLockKey = (address, netuid) => `${address}:${netuid}`;
3904
3991
  const getConvictionLockLabel = (lockType) => lockType === "perpetual" ? "Perpetual Conviction Lock" : "Decaying Conviction Lock";
@@ -3946,17 +4033,17 @@ const toBigIntValue = (value) => {
3946
4033
  * the chain keeps a zero-mass Lock entry alive while it still carries conviction ("ghost" lock),
3947
4034
  * even after the coldkey fully unstaked from the subnet.
3948
4035
  */
3949
- const fetchConvictionLocks = async (connector, networkId, metadataRpc, addresses) => {
4036
+ const fetchConvictionLocks = async (connector, networkId, metadataRpc, addresses, at) => {
3950
4037
  if (!addresses.length) return [];
3951
4038
  const { unifiedMetadata, builder } = parseMetadataRpcCached(metadataRpc);
3952
4039
  if (!hasRuntimeApi(unifiedMetadata, "StakeInfoRuntimeApi", "get_coldkey_lock") || !hasStorageItems(unifiedMetadata, "SubtensorModule", ["Lock", "DecayingLock"])) return [];
3953
4040
  try {
3954
4041
  const lockStorageCoder = builder.buildStorage("SubtensorModule", "Lock");
3955
4042
  const decayingLockStorageCoder = builder.buildStorage("SubtensorModule", "DecayingLock");
3956
- const lockStorageKeys = await fetchConvictionLockStorageKeys(connector, networkId, addresses, lockStorageCoder);
4043
+ const lockStorageKeys = await fetchConvictionLockStorageKeys(connector, networkId, addresses, lockStorageCoder, at);
3957
4044
  if (!lockStorageKeys.length) return [];
3958
4045
  const hotkeyByPair = new Map(lockStorageKeys.map(({ address, netuid, hotkey }) => [convictionLockKey(address, netuid), hotkey]));
3959
- const [lockModesByPair, lockStates] = await Promise.all([fetchConvictionLockModes(connector, networkId, lockStorageKeys, decayingLockStorageCoder), fetchColdkeyLockStates(connector, networkId, builder, lockStorageKeys)]);
4046
+ const [lockModesByPair, lockStates] = await Promise.all([fetchConvictionLockModes(connector, networkId, lockStorageKeys, decayingLockStorageCoder, at), fetchColdkeyLockStates(connector, networkId, builder, lockStorageKeys, at)]);
3960
4047
  return lockStates.flatMap(({ address, netuid, lockState }) => {
3961
4048
  const amount = lockState?.locked_mass ?? 0n;
3962
4049
  const convictionRaw = toBigIntValue(lockState?.conviction);
@@ -3984,7 +4071,7 @@ const fetchConvictionLocks = async (connector, networkId, metadataRpc, addresses
3984
4071
  * Encoding only the first map key (the coldkey) yields the storage key prefix covering all of the
3985
4072
  * coldkey's (netuid, hotkey) entries, using the hashers declared in metadata.
3986
4073
  */
3987
- const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, storageCoder) => {
4074
+ const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, storageCoder, at) => {
3988
4075
  return (await Promise.all(addresses.map(async (address) => {
3989
4076
  let keyPrefix;
3990
4077
  try {
@@ -3995,7 +4082,7 @@ const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, s
3995
4082
  }
3996
4083
  let stateKeys;
3997
4084
  try {
3998
- stateKeys = await connector.send(networkId, "state_getKeys", [keyPrefix]);
4085
+ stateKeys = await fetchStorageKeysPaged(connector, networkId, keyPrefix, at);
3999
4086
  } catch (cause) {
4000
4087
  log_default.warn(`Failed to fetch conviction Lock keys (address=${address}) on ${networkId}`, { cause });
4001
4088
  throw cause;
@@ -4015,7 +4102,7 @@ const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, s
4015
4102
  });
4016
4103
  }))).flat();
4017
4104
  };
4018
- const fetchConvictionLockModes = async (connector, networkId, pairs, storageCoder) => {
4105
+ const fetchConvictionLockModes = async (connector, networkId, pairs, storageCoder, at) => {
4019
4106
  const queries = pairs.map(({ address, netuid }) => {
4020
4107
  let stateKey;
4021
4108
  try {
@@ -4036,15 +4123,15 @@ const fetchConvictionLockModes = async (connector, networkId, pairs, storageCode
4036
4123
  }
4037
4124
  };
4038
4125
  });
4039
- return new Map(await fetchRpcQueryPack(connector, networkId, queries));
4126
+ return new Map(await fetchRpcQueryPack(connector, networkId, queries, at));
4040
4127
  };
4041
- const fetchColdkeyLockStates = async (connector, networkId, builder, pairs) => {
4128
+ const fetchColdkeyLockStates = async (connector, networkId, builder, pairs, at) => {
4042
4129
  return Promise.all(pairs.map(async ({ address, netuid }) => {
4043
4130
  try {
4044
4131
  return {
4045
4132
  address,
4046
4133
  netuid,
4047
- lockState: await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_coldkey_lock", [address, netuid])
4134
+ lockState: await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_coldkey_lock", [address, netuid], at)
4048
4135
  };
4049
4136
  } catch (cause) {
4050
4137
  log_default.warn(`Failed to fetch get_coldkey_lock for (netuid=${netuid}, address=${address}) on ${networkId}`, { cause });
@@ -4057,29 +4144,122 @@ const fetchColdkeyLockStates = async (connector, networkId, builder, pairs) => {
4057
4144
  const MODULE_TYPE$5 = SubDTaoTokenSchema.shape.type.value;
4058
4145
  const PLATFORM$5 = SubDTaoTokenSchema.shape.platform.value;
4059
4146
  //#endregion
4060
- //#region src/modules/substrate-dtao/calculatePendingRootClaimable.ts
4061
- const calculatePendingRootClaimable = ({ stake, hotkey, address, networkId, validatorRootClaimableRate, alreadyClaimedByNetuid }) => {
4062
- const pendingRootClaimBalances = [];
4063
- for (const [netuid, claimableRate] of validatorRootClaimableRate) {
4064
- if (claimableRate === 0n) continue;
4065
- const totalClaimable = stake * claimableRate + (1n << 31n) >> 32n;
4066
- const alreadyClaimed = alreadyClaimedByNetuid.get(netuid) ?? 0n;
4067
- const pendingRootClaim = totalClaimable > alreadyClaimed ? totalClaimable - alreadyClaimed : 0n;
4068
- pendingRootClaimBalances.push({
4069
- address,
4070
- tokenId: subDTaoTokenId(networkId, netuid, hotkey),
4071
- baseTokenId: subDTaoTokenId(networkId, netuid),
4072
- hotkey,
4073
- netuid,
4074
- pendingRootClaim,
4075
- stake: 0n
4147
+ //#region src/modules/substrate-dtao/rootStakeHold.ts
4148
+ /**
4149
+ * Extracts the root-stake hold from a balance's raw values (BalanceJson), if any.
4150
+ * While present, the pair's root stake cannot leave root — unstake/move/swap/transfer
4151
+ * would fail with `RootStakeLocked`. Only present while the window was still running
4152
+ * as of the last balances poll.
4153
+ */
4154
+ const findDTaoRootStakeHold = (balance) => {
4155
+ for (const value of balance?.values ?? []) {
4156
+ const hold = value.meta?.rootStakeHold;
4157
+ if (hold?.type === "root-stake-hold") return hold;
4158
+ }
4159
+ return null;
4160
+ };
4161
+ /**
4162
+ * Fetches active root-stake hold windows (spec 441): when `RootStakeUnlockInterval` is
4163
+ * non-zero, root stake cannot leave root (remove/move/swap/transfer) until `interval`
4164
+ * blocks after the pair's last root stake add/remove/claim (`LastColdkeyHotkeyStakeBlock`).
4165
+ * Pairs already past their window are omitted — a returned hold means the pair's root
4166
+ * stake is currently unremovable.
4167
+ *
4168
+ * The interval is 0 (disabled) unless governance enables it — either by setting the
4169
+ * storage entry or via a runtime default that leaves it unset (metadata fallback). The
4170
+ * per-pair queries only run when it is non-zero.
4171
+ */
4172
+ const fetchRootStakeHolds = async (connector, networkId, metadataRpc, rootPairs, at) => {
4173
+ if (!rootPairs.length) return [];
4174
+ const { unifiedMetadata, builder } = parseMetadataRpcCached(metadataRpc);
4175
+ if (!hasStorageItems(unifiedMetadata, "SubtensorModule", ["RootStakeUnlockInterval", "LastColdkeyHotkeyStakeBlock"])) return [];
4176
+ try {
4177
+ const interval = await fetchUnlockInterval(connector, networkId, unifiedMetadata, builder, at);
4178
+ if (interval === 0n) return [];
4179
+ const { currentBlock, lastStakeBlocks } = await fetchLastStakeBlocks(connector, networkId, builder, rootPairs, at);
4180
+ return lastStakeBlocks.flatMap(({ address, hotkey, lastStakeBlock }) => {
4181
+ if (lastStakeBlock === 0n) return [];
4182
+ const unlockAtBlock = lastStakeBlock + interval;
4183
+ if (unlockAtBlock <= BigInt(currentBlock)) return [];
4184
+ return [{
4185
+ address,
4186
+ hotkey,
4187
+ unlockAtBlock: Number(unlockAtBlock)
4188
+ }];
4076
4189
  });
4190
+ } catch (cause) {
4191
+ log_default.warn(`Failed to fetch root stake holds on ${networkId}`, { cause });
4192
+ throw cause;
4077
4193
  }
4078
- return pendingRootClaimBalances;
4194
+ };
4195
+ const fetchUnlockInterval = async (connector, networkId, unifiedMetadata, builder, at) => {
4196
+ const storageCoder = builder.buildStorage("SubtensorModule", "RootStakeUnlockInterval");
4197
+ const [interval] = await fetchRpcQueryPack(connector, networkId, [{
4198
+ stateKeys: [storageCoder.keys.enc()],
4199
+ decodeResult: (changes) => {
4200
+ const encoded = changes[0] ?? getUnlockIntervalFallback(unifiedMetadata, networkId);
4201
+ const decoded = decodeScale(storageCoder, encoded, `Failed to decode RootStakeUnlockInterval on ${networkId}`);
4202
+ if (decoded === null) throw new Error(`Failed to decode RootStakeUnlockInterval on ${networkId}`);
4203
+ return decoded;
4204
+ }
4205
+ }], at);
4206
+ return interval ?? 0n;
4207
+ };
4208
+ const getUnlockIntervalFallback = (unifiedMetadata, networkId) => {
4209
+ const fallback = unifiedMetadata.pallets.find((pallet) => pallet.name === "SubtensorModule")?.storage?.items.find((item) => item.name === "RootStakeUnlockInterval")?.fallback;
4210
+ if (!fallback) throw new Error(`Missing RootStakeUnlockInterval metadata fallback on ${networkId}`);
4211
+ return fallback;
4212
+ };
4213
+ /**
4214
+ * Reads each pair's `LastColdkeyHotkeyStakeBlock` plus the current block number, all from
4215
+ * one state snapshot: `System.Number` rides along as an extra key of the same
4216
+ * `state_queryStorageAt` instead of costing a separate `chain_getHeader` round trip.
4217
+ */
4218
+ const fetchLastStakeBlocks = async (connector, networkId, builder, rootPairs, at) => {
4219
+ const numberCoder = builder.buildStorage("System", "Number");
4220
+ const currentBlockQuery = {
4221
+ stateKeys: [numberCoder.keys.enc()],
4222
+ decodeResult: (changes) => {
4223
+ const decoded = decodeScale(numberCoder, changes[0], `Failed to decode System.Number on ${networkId}`);
4224
+ if (decoded === null) throw new Error(`Failed to decode System.Number on ${networkId}`);
4225
+ return Number(decoded);
4226
+ }
4227
+ };
4228
+ const storageCoder = builder.buildStorage("SubtensorModule", "LastColdkeyHotkeyStakeBlock");
4229
+ const [currentBlock, ...lastStakeBlocks] = await fetchRpcQueryPack(connector, networkId, [currentBlockQuery, ...rootPairs.map(({ address, hotkey }) => {
4230
+ let stateKey;
4231
+ try {
4232
+ stateKey = storageCoder.keys.enc(address, hotkey);
4233
+ } catch (cause) {
4234
+ log_default.warn(`Failed to encode LastColdkeyHotkeyStakeBlock key (address=${address}, hotkey=${hotkey}) on ${networkId}`, { cause });
4235
+ throw cause;
4236
+ }
4237
+ return {
4238
+ stateKeys: [stateKey],
4239
+ decodeResult: (changes) => {
4240
+ const hexValue = changes[0];
4241
+ if (!hexValue) return {
4242
+ address,
4243
+ hotkey,
4244
+ lastStakeBlock: 0n
4245
+ };
4246
+ const decoded = decodeScale(storageCoder, hexValue, `Failed to decode LastColdkeyHotkeyStakeBlock for (address=${address}, hotkey=${hotkey}) on ${networkId}`);
4247
+ if (decoded === null) throw new Error(`Failed to decode LastColdkeyHotkeyStakeBlock for (address=${address}, hotkey=${hotkey}) on ${networkId}`);
4248
+ return {
4249
+ address,
4250
+ hotkey,
4251
+ lastStakeBlock: decoded
4252
+ };
4253
+ }
4254
+ };
4255
+ })], at);
4256
+ return {
4257
+ currentBlock,
4258
+ lastStakeBlocks
4259
+ };
4079
4260
  };
4080
4261
  //#endregion
4081
4262
  //#region src/modules/substrate-dtao/fetchBalances.ts
4082
- const ROOT_NETUID = 0;
4083
4263
  const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, miniMetadata, signal }) => {
4084
4264
  if (!tokensWithAddresses.length) return {
4085
4265
  success: [],
@@ -4122,29 +4302,36 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
4122
4302
  const addresses = uniq(balanceDefs.map((def) => def.address));
4123
4303
  try {
4124
4304
  const { builder } = parseMetadataRpcCached(miniMetadata.data);
4125
- const stakeInfos = await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_stake_info_for_coldkeys", [addresses]);
4126
- const rootHotkeys = uniq(stakeInfos.flatMap(([, stakes]) => stakes.filter((stake) => stake.netuid === ROOT_NETUID).map((stake) => stake.hotkey)));
4127
- const rootClaimableRatesByHotkey = rootHotkeys.length && miniMetadata.data ? await fetchRootClaimableRates(connector, networkId, miniMetadata.data, rootHotkeys) : /* @__PURE__ */ new Map();
4128
- const addressHotkeyNetuidPairs = [];
4129
- for (const [address, stakes] of stakeInfos) for (const stake of stakes) if (stake.netuid === ROOT_NETUID) {
4130
- const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey);
4131
- if (claimableRates) for (const netuid of claimableRates.keys()) addressHotkeyNetuidPairs.push([
4305
+ const at = await fetchBestBlockHash(connector, networkId);
4306
+ const stakeInfos = await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_stake_info_for_coldkeys", [addresses], at);
4307
+ const rootPairs = [];
4308
+ const seenRootPairs = /* @__PURE__ */ new Set();
4309
+ for (const [address, stakes] of stakeInfos) for (const stake of stakes) {
4310
+ if (stake.netuid !== 0) continue;
4311
+ const pairKey = `${address}:${stake.hotkey}`;
4312
+ if (seenRootPairs.has(pairKey)) continue;
4313
+ seenRootPairs.add(pairKey);
4314
+ rootPairs.push({
4132
4315
  address,
4133
- stake.hotkey,
4134
- netuid
4135
- ]);
4316
+ hotkey: stake.hotkey
4317
+ });
4136
4318
  }
4137
- const [rootClaimedAmounts, convictionLocks] = await Promise.all([addressHotkeyNetuidPairs.length && miniMetadata.data ? fetchRootClaimedAmounts(connector, networkId, miniMetadata.data, addressHotkeyNetuidPairs) : Promise.resolve(/* @__PURE__ */ new Map()), miniMetadata.data ? fetchConvictionLocks(connector, networkId, miniMetadata.data, addresses) : Promise.resolve([])]);
4319
+ const [convictionLocks, basketClaims, rootStakeHolds] = miniMetadata.data ? await Promise.all([
4320
+ fetchConvictionLocks(connector, networkId, miniMetadata.data, addresses, at),
4321
+ fetchBasketClaims(connector, networkId, miniMetadata.data, addresses, at),
4322
+ fetchRootStakeHolds(connector, networkId, miniMetadata.data, rootPairs, at)
4323
+ ]) : [
4324
+ [],
4325
+ [],
4326
+ []
4327
+ ];
4138
4328
  const upsertBalance = (acc, address, tokenId, balance) => {
4139
4329
  const key = `${address}:${tokenId}`;
4140
4330
  const recordedBalance = acc[key];
4141
- if (recordedBalance) acc[key] = {
4142
- ...recordedBalance,
4143
- stake: recordedBalance.stake + balance.stake,
4144
- ...balance.pendingRootClaim !== void 0 && { pendingRootClaim: balance.pendingRootClaim },
4145
- ...balance.convictionLock !== void 0 && { convictionLock: balance.convictionLock }
4146
- };
4147
- else acc[key] = balance;
4331
+ acc[key] = recordedBalance ? mergeWith({}, recordedBalance, balance, (recorded, incoming, field) => {
4332
+ if (field === "stake" && recorded !== void 0) return recorded + incoming;
4333
+ return incoming;
4334
+ }) : balance;
4148
4335
  };
4149
4336
  const slicer = createTimeSlicer({
4150
4337
  signal,
@@ -4161,21 +4348,21 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
4161
4348
  netuid: stake.netuid
4162
4349
  };
4163
4350
  upsertBalance(balancesRaw, address, balance.tokenId, balance);
4164
- if (stake.netuid === ROOT_NETUID) {
4165
- const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey) ?? /* @__PURE__ */ new Map();
4166
- const alreadyClaimedMap = rootClaimedAmounts.get(address)?.get(stake.hotkey) ?? /* @__PURE__ */ new Map();
4167
- calculatePendingRootClaimable({
4168
- stake: stake.stake,
4169
- hotkey: stake.hotkey,
4170
- address,
4171
- networkId,
4172
- validatorRootClaimableRate: claimableRates,
4173
- alreadyClaimedByNetuid: alreadyClaimedMap
4174
- }).forEach((balance) => {
4175
- upsertBalance(balancesRaw, address, balance.tokenId, balance);
4176
- });
4177
- }
4178
4351
  }, { slicer });
4352
+ const upsertRootPairFields = (items, getFields) => forEachWithYield(items, (item) => {
4353
+ const tokenId = subDTaoTokenId(networkId, 0, item.hotkey);
4354
+ upsertBalance(balancesRaw, item.address, tokenId, {
4355
+ address: item.address,
4356
+ tokenId,
4357
+ baseTokenId: subDTaoTokenId(networkId, 0),
4358
+ stake: 0n,
4359
+ hotkey: item.hotkey,
4360
+ netuid: 0,
4361
+ ...getFields(item)
4362
+ });
4363
+ }, { slicer });
4364
+ await upsertRootPairFields(basketClaims, ({ amount }) => ({ claimable: amount }));
4365
+ await upsertRootPairFields(rootStakeHolds, ({ unlockAtBlock }) => ({ rootStakeHoldUnlockBlock: unlockAtBlock }));
4179
4366
  await forEachWithYield(convictionLocks, ({ address, netuid, lock }) => {
4180
4367
  const balance = {
4181
4368
  address,
@@ -4212,21 +4399,35 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
4212
4399
  const stake = balancesRaw[`${def.address}:${def.token.id}`];
4213
4400
  if (!stake) return null;
4214
4401
  const stakeAmount = BigInt(stake.stake?.toString() ?? "0");
4215
- const pendingRootClaimAmount = BigInt(stake.pendingRootClaim?.toString() ?? "0");
4402
+ const claimableAmount = BigInt(stake.claimable?.toString() ?? "0");
4216
4403
  const convictionLockAmount = BigInt(stake.convictionLock?.amount?.toString() ?? "0");
4217
4404
  const convictionLockConviction = BigInt(stake.convictionLock?.convictionRaw ?? "0");
4218
- const hasZeroStake = stakeAmount === 0n;
4219
- const hasPendingRootClaim = pendingRootClaimAmount > 0n;
4405
+ const rootStakeHoldMeta = stake.rootStakeHoldUnlockBlock !== void 0 ? { rootStakeHold: {
4406
+ type: "root-stake-hold",
4407
+ unlockAtBlock: stake.rootStakeHoldUnlockBlock
4408
+ } } : void 0;
4220
4409
  const values = [{
4221
4410
  type: "free",
4222
4411
  label: stake.netuid === 0 ? "Root Staking" : `Subnet Staking`,
4223
- amount: stakeAmount.toString()
4224
- }, {
4225
- type: "locked",
4226
- label: "Pending root claim",
4227
- amount: pendingRootClaimAmount.toString(),
4228
- includeInTransferable: true
4412
+ amount: stakeAmount.toString(),
4413
+ ...rootStakeHoldMeta && { meta: rootStakeHoldMeta }
4229
4414
  }];
4415
+ if (claimableAmount > 0n) {
4416
+ const claimable = {
4417
+ label: CLAIMABLE_REWARDS_LABEL,
4418
+ amount: claimableAmount.toString()
4419
+ };
4420
+ values.push({
4421
+ ...claimable,
4422
+ type: "locked",
4423
+ includeInTransferable: true
4424
+ });
4425
+ values.push({
4426
+ ...claimable,
4427
+ type: "extra",
4428
+ includeInTotal: true
4429
+ });
4430
+ }
4230
4431
  if (stake.convictionLock && (convictionLockAmount > 0n || convictionLockConviction > 0n)) {
4231
4432
  const convictionLockMeta = { convictionLock: {
4232
4433
  type: "conviction-lock",
@@ -4240,12 +4441,6 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
4240
4441
  meta: convictionLockMeta
4241
4442
  });
4242
4443
  }
4243
- if (hasZeroStake && hasPendingRootClaim) values.push({
4244
- type: "extra",
4245
- label: "Pending root claim",
4246
- amount: pendingRootClaimAmount.toString(),
4247
- includeInTotal: true
4248
- });
4249
4444
  return {
4250
4445
  address: def.address,
4251
4446
  networkId,
@@ -4271,124 +4466,6 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
4271
4466
  };
4272
4467
  }
4273
4468
  };
4274
- const buildStorageCoder = (metadataRpc, pallet, entry) => {
4275
- const { builder } = parseMetadataRpcCached(metadataRpc);
4276
- return builder.buildStorage(pallet, entry);
4277
- };
4278
- const buildRootClaimableStorageCoder = async (_connector, networkId, metadataRpc) => {
4279
- let storageCoder = null;
4280
- if (metadataRpc) try {
4281
- storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimable");
4282
- } catch (cause) {
4283
- log_default.warn(`Failed to build storage coder for SubtensorModule.RootClaimable using provided metadata on ${networkId}`, { cause });
4284
- }
4285
- return storageCoder;
4286
- };
4287
- const buildRootClaimedStorageCoder = async (networkId, metadataRpc) => {
4288
- let storageCoder = null;
4289
- if (metadataRpc) try {
4290
- storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimed");
4291
- } catch (cause) {
4292
- log_default.warn(`Failed to build storage coder for SubtensorModule.RootClaimed using provided metadata on ${networkId}`, { cause });
4293
- }
4294
- return storageCoder;
4295
- };
4296
- const buildRootClaimableQueries = (networkId, hotkeys, storageCoder) => {
4297
- return hotkeys.map((hotkey) => {
4298
- let stateKey;
4299
- try {
4300
- stateKey = storageCoder.keys.enc(hotkey);
4301
- } catch (cause) {
4302
- log_default.warn(`Failed to encode storage key for hotkey ${hotkey} on ${networkId}`, { cause });
4303
- throw cause;
4304
- }
4305
- const decodeResult = (changes) => {
4306
- const hexValue = changes[0];
4307
- if (!hexValue) return [hotkey, /* @__PURE__ */ new Map()];
4308
- const decoded = decodeScale(storageCoder, hexValue, `Failed to decode RootClaimable for hotkey ${hotkey} on ${networkId}`);
4309
- if (decoded === null) throw new Error(`Failed to decode RootClaimable for hotkey ${hotkey} on ${networkId}`);
4310
- return [hotkey, new Map(decoded)];
4311
- };
4312
- return {
4313
- stateKeys: [stateKey],
4314
- decodeResult
4315
- };
4316
- });
4317
- };
4318
- const fetchRootClaimableRates = async (connector, networkId, metadataRpc, hotkeys) => {
4319
- if (!hotkeys.length) return /* @__PURE__ */ new Map();
4320
- const storageCoder = await buildRootClaimableStorageCoder(connector, networkId, metadataRpc);
4321
- if (!storageCoder) return new Map(hotkeys.map((hotkey) => [hotkey, /* @__PURE__ */ new Map()]));
4322
- const queries = buildRootClaimableQueries(networkId, hotkeys, storageCoder);
4323
- try {
4324
- const results = await fetchRpcQueryPack(connector, networkId, queries);
4325
- return new Map(results);
4326
- } catch (cause) {
4327
- log_default.warn(`Failed to fetch RootClaimable for hotkeys on ${networkId}`, { cause });
4328
- throw cause;
4329
- }
4330
- };
4331
- const buildRootClaimedQueries = (networkId, addressHotkeyNetuidPairs, storageCoder) => {
4332
- return addressHotkeyNetuidPairs.map(([address, hotkey, netuid]) => {
4333
- let stateKey;
4334
- try {
4335
- stateKey = storageCoder.keys.enc(netuid, hotkey, address);
4336
- } catch (cause) {
4337
- log_default.warn(`Failed to encode storage key for RootClaimed (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`, { cause });
4338
- throw cause;
4339
- }
4340
- const decodeResult = (changes) => {
4341
- const hexValue = changes[0];
4342
- if (!hexValue) return [
4343
- address,
4344
- hotkey,
4345
- netuid,
4346
- 0n
4347
- ];
4348
- const decoded = decodeScale(storageCoder, hexValue, `Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
4349
- if (decoded === null) throw new Error(`Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
4350
- return [
4351
- address,
4352
- hotkey,
4353
- netuid,
4354
- decoded
4355
- ];
4356
- };
4357
- return {
4358
- stateKeys: [stateKey],
4359
- decodeResult
4360
- };
4361
- });
4362
- };
4363
- const fetchRootClaimedAmounts = async (connector, networkId, metadataRpc, addressHotkeyNetuidPairs) => {
4364
- if (!addressHotkeyNetuidPairs.length) return /* @__PURE__ */ new Map();
4365
- const storageCoder = await buildRootClaimedStorageCoder(networkId, metadataRpc);
4366
- if (!storageCoder) {
4367
- const result = /* @__PURE__ */ new Map();
4368
- for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
4369
- if (!result.has(address)) result.set(address, /* @__PURE__ */ new Map());
4370
- const addressMap = result.get(address);
4371
- if (!addressMap.has(hotkey)) addressMap.set(hotkey, /* @__PURE__ */ new Map());
4372
- addressMap.get(hotkey).set(netuid, 0n);
4373
- }
4374
- return result;
4375
- }
4376
- const queries = buildRootClaimedQueries(networkId, addressHotkeyNetuidPairs, storageCoder);
4377
- try {
4378
- const results = await fetchRpcQueryPack(connector, networkId, queries);
4379
- const result = /* @__PURE__ */ new Map();
4380
- for (const [address, hotkey, netuid, claimed] of results) {
4381
- if (!result.has(address)) result.set(address, /* @__PURE__ */ new Map());
4382
- const addressMap = result.get(address);
4383
- if (!addressMap.has(hotkey)) addressMap.set(hotkey, /* @__PURE__ */ new Map());
4384
- addressMap.get(hotkey).set(netuid, claimed);
4385
- }
4386
- return result;
4387
- } catch (cause) {
4388
- log_default.warn(`Failed to fetch RootClaimed for address-hotkey-netuid pairs on ${networkId}`, { cause });
4389
- throw cause;
4390
- }
4391
- };
4392
4469
  //#endregion
4393
4470
  //#region src/modules/substrate-dtao/fetchTokens.ts
4394
4471
  const NATIVE_TOKEN_SYMBOLS = {
@@ -4427,7 +4504,7 @@ const fetchTokens$5 = async ({ networkId, connector, tokens, miniMetadata }) =>
4427
4504
  const fetchTransferableTokensMap = async (connector, metadata, networkId) => {
4428
4505
  const { builder } = parseMetadataRpcCached(metadata);
4429
4506
  const transferToggleCodec = builder.buildStorage("SubtensorModule", "TransferToggle");
4430
- const transferToggleKeys = await connector.send(networkId, "state_getKeys", [getStorageKeyPrefix("SubtensorModule", "TransferToggle")]);
4507
+ const transferToggleKeys = await fetchStorageKeysPaged(connector, networkId, getStorageKeyPrefix("SubtensorModule", "TransferToggle"));
4431
4508
  const transferToggleResults = await connector.send(networkId, "state_queryStorageAt", [transferToggleKeys]);
4432
4509
  return fromPairs((transferToggleResults.length ? transferToggleResults[0].changes : []).map(([key, value]) => {
4433
4510
  const [netuid] = transferToggleCodec.keys.dec(key);
@@ -4465,18 +4542,28 @@ const getData$3 = (metadataRpc) => {
4465
4542
  pallet: "SubtensorModule",
4466
4543
  items: [
4467
4544
  "TransferToggle",
4468
- "RootClaimable",
4469
- "RootClaimed",
4470
4545
  "Lock",
4471
- "DecayingLock"
4546
+ "DecayingLock",
4547
+ "RootStakeUnlockInterval",
4548
+ "LastColdkeyHotkeyStakeBlock"
4472
4549
  ]
4473
- }], [{
4474
- runtimeApi: "StakeInfoRuntimeApi",
4475
- methods: ["get_stake_info_for_coldkeys", "get_coldkey_lock"]
4476
4550
  }, {
4477
- runtimeApi: "SubnetInfoRuntimeApi",
4478
- methods: ["get_all_dynamic_info"]
4479
- }]);
4551
+ pallet: "System",
4552
+ items: ["Number"]
4553
+ }], [
4554
+ {
4555
+ runtimeApi: "StakeInfoRuntimeApi",
4556
+ methods: ["get_stake_info_for_coldkeys", "get_coldkey_lock"]
4557
+ },
4558
+ {
4559
+ runtimeApi: "SubnetInfoRuntimeApi",
4560
+ methods: ["get_all_dynamic_info"]
4561
+ },
4562
+ {
4563
+ runtimeApi: "BetaBasketRuntimeApi",
4564
+ methods: ["get_root_basket_positions"]
4565
+ }
4566
+ ]);
4480
4567
  return encodeMetadata(metadata);
4481
4568
  };
4482
4569
  //#endregion
@@ -4502,7 +4589,8 @@ const getTransferCallData$5 = ({ from, to, value, token, metadataRpc }) => {
4502
4589
  };
4503
4590
  //#endregion
4504
4591
  //#region src/modules/substrate-dtao/isEffectivelyEqualDTaoBalance.ts
4505
- /** root dividends accrue continuously; the pending-claim display is informational */
4592
+ /** basket claimable amounts are marked NAV quotes: they move with subnet pool prices and
4593
+ * dividend accrual on (nearly) every block; the display is informational */
4506
4594
  const CLAIM_DRIFT_TOLERANCE_BPS = 100n;
4507
4595
  /**
4508
4596
  * subnet staking positions auto-compound: dividend injections land once per subnet tempo
@@ -4513,8 +4601,8 @@ const CLAIM_DRIFT_TOLERANCE_BPS = 100n;
4513
4601
  const STAKE_DRIFT_TOLERANCE_BPS = 100n;
4514
4602
  /**
4515
4603
  * dtao balances embed values that drift on (nearly) every block even when the user's
4516
- * position is untouched: "Pending root claim" amounts accrue continuously, staking
4517
- * positions auto-compound, and conviction locks decay.
4604
+ * position is untouched: staking positions auto-compound, basket claimable quotes move,
4605
+ * and conviction locks decay.
4518
4606
  *
4519
4607
  * Without special handling, every 6s poll re-emits the full result set, which defeats
4520
4608
  * every distinctUntilChanged stage downstream and forces the whole pipeline
@@ -4526,9 +4614,7 @@ const STAKE_DRIFT_TOLERANCE_BPS = 100n;
4526
4614
  * drift-prone values above (relative to the previously EMITTED value, so movement
4527
4615
  * accumulates and a sustained move still surfaces)
4528
4616
  * - "drift" when ONLY the drift-prone values moved, beyond tolerance. The stabilizer
4529
- * re-emits these at most once per refresh interval — important for fast-accruing
4530
- * values (a young pending claim can grow >1% per poll indefinitely, so a purely
4531
- * relative tolerance can never suppress it)
4617
+ * re-emits these at most once per refresh interval
4532
4618
  * - "changed" for structural changes (stake, locks, status, value set) — emitted
4533
4619
  * immediately
4534
4620
  */
@@ -4547,14 +4633,13 @@ const isWithinDriftTolerance = (previous, next, toleranceBps) => {
4547
4633
  const max = a > b ? a : b;
4548
4634
  return diff * 10000n <= max * toleranceBps;
4549
4635
  };
4550
- /** the only labels whose amounts accrue per block (see fetchBalances) */
4551
- const PENDING_ROOT_CLAIM_LABEL = "Pending root claim";
4552
4636
  const changed = (reason) => ({
4553
4637
  equivalence: "changed",
4554
4638
  reason
4555
4639
  });
4556
- /** claims accrue and locks decay — their values may appear/disappear as amounts cross zero */
4557
- const isDriftProneValue = (value) => value.label === PENDING_ROOT_CLAIM_LABEL || !!value.meta?.convictionLock;
4640
+ /** claimable quotes and decaying locks may appear as their amounts cross zero */
4641
+ const isDriftProneValue = (value) => value.label === "Claimable rewards" || hasConvictionLock(value);
4642
+ const hasConvictionLock = (value) => !!value.meta?.convictionLock;
4558
4643
  const classifyValue = (previous, next) => {
4559
4644
  if (("includeInTransferable" in previous ? previous.includeInTransferable : void 0) !== ("includeInTransferable" in next ? next.includeInTransferable : void 0) || ("includeInTotal" in previous ? previous.includeInTotal : void 0) !== ("includeInTotal" in next ? next.includeInTotal : void 0)) return changed("variant flags");
4560
4645
  const previousMeta = previous.meta;
@@ -4562,8 +4647,9 @@ const classifyValue = (previous, next) => {
4562
4647
  const previousLock = previousMeta?.convictionLock;
4563
4648
  const nextLock = nextMeta?.convictionLock;
4564
4649
  if (previousLock?.type !== nextLock?.type || previousLock?.hotkey !== nextLock?.hotkey || previousLock?.lockType !== nextLock?.lockType) return changed("conviction lock meta");
4650
+ if (previousMeta?.rootStakeHold?.unlockAtBlock !== nextMeta?.rootStakeHold?.unlockAtBlock) return changed("root stake hold meta");
4565
4651
  let drift = false;
4566
- if (previous.amount !== next.amount) if (previous.label === PENDING_ROOT_CLAIM_LABEL) {
4652
+ if (previous.amount !== next.amount) if (previous.label === "Claimable rewards") {
4567
4653
  if (!isWithinDriftTolerance(previous.amount, next.amount, CLAIM_DRIFT_TOLERANCE_BPS)) drift = true;
4568
4654
  } else if (previousLock) drift = true;
4569
4655
  else if (isWithinDriftTolerance(previous.amount, next.amount, STAKE_DRIFT_TOLERANCE_BPS)) drift = true;
@@ -4586,7 +4672,7 @@ const classifyBalance = (previous, next) => {
4586
4672
  for (const [key, previousValue] of previousByKey) {
4587
4673
  const nextValue = nextByKey.get(key);
4588
4674
  if (!nextValue) {
4589
- if (!isDriftProneValue(previousValue)) return changed(`value removed: ${key}`);
4675
+ if (!hasConvictionLock(previousValue)) return changed(`value removed: ${key}`);
4590
4676
  drift = true;
4591
4677
  continue;
4592
4678
  }
@@ -7401,6 +7487,6 @@ const mergeSortedBalanceArrays = async (arrays, slicer) => {
7401
7487
  };
7402
7488
  const sortByMiniMetadataId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
7403
7489
  //#endregion
7404
- export { ALPHA_PRICE_SCALE, BALANCE_MODULES, Balance, BalanceFormatter, BalanceValueGetter, Balances, BalancesProvider, Change24hCurrencyFormatter, EvmErc20BalanceModule, EvmErc20TokenConfigSchema, EvmNativeBalanceModule, EvmNativeTokenConfigSchema, EvmUniswapV2BalanceModule, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, MINIMETADATA_VERSION, PlanckSumBalancesFormatter, SolNativeBalanceModule, SolNativeTokenConfigSchema, SolSplBalanceModule, SolSplTokenConfigSchema, SolToken2022BalanceModule, SolToken2022TokenConfigSchema, SubAssetsBalanceModule, SubAssetsTokenConfigSchema, SubDTaoBalanceModule, SubDTaoTokenConfigSchema, SubForeignAssetsBalanceModule, SubForeignAssetsTokenConfigSchema, SubHydrationBalanceModule, SubHydrationTokenConfigSchema, SubNativeBalanceModule, SubNativeMiniMetadataExtraSchema, SubNativeModuleConfigSchema, SubNativeTokenConfigSchema, SubPsp22BalanceModule, SubPsp22TokenConfigSchema, SubTokensBalanceModule, SubTokensMiniMetadataExtraSchema, SubTokensModuleConfigSchema, SubTokensTokenConfigSchema, SumBalancesFormatter, TAO_DECIMALS, abiMulticall, alphaToTao, calculateToken2022TransferFee, deriveMiniMetadataId, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, findDTaoConvictionLock, getBalanceFingerprint, getBalanceId, getBalanceStorageFingerprint, getConvictionLockLabel, getEpochTransferFee, getExtension, getLockTitle, getLockedType, getMintExtensions, getRawLocks, getRawTotalPlanck, getSweepStaleVariant, getTokenMetadata, getTransferFeeConfig, getTransferHook, getValueId, includeInTotalExtraAmount, isEqualBalanceArrays, isEqualBalancesResult, isEqualMiniMetadatas, isEqualModuleResults, isNonTransferable, taoToAlpha, taoToAlphaCeil, uniswapV2PairAbi };
7490
+ export { ALPHA_PRICE_SCALE, BALANCE_MODULES, Balance, BalanceFormatter, BalanceValueGetter, Balances, BalancesProvider, Change24hCurrencyFormatter, EvmErc20BalanceModule, EvmErc20TokenConfigSchema, EvmNativeBalanceModule, EvmNativeTokenConfigSchema, EvmUniswapV2BalanceModule, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, MINIMETADATA_VERSION, PlanckSumBalancesFormatter, SolNativeBalanceModule, SolNativeTokenConfigSchema, SolSplBalanceModule, SolSplTokenConfigSchema, SolToken2022BalanceModule, SolToken2022TokenConfigSchema, SubAssetsBalanceModule, SubAssetsTokenConfigSchema, SubDTaoBalanceModule, SubDTaoTokenConfigSchema, SubForeignAssetsBalanceModule, SubForeignAssetsTokenConfigSchema, SubHydrationBalanceModule, SubHydrationTokenConfigSchema, SubNativeBalanceModule, SubNativeMiniMetadataExtraSchema, SubNativeModuleConfigSchema, SubNativeTokenConfigSchema, SubPsp22BalanceModule, SubPsp22TokenConfigSchema, SubTokensBalanceModule, SubTokensMiniMetadataExtraSchema, SubTokensModuleConfigSchema, SubTokensTokenConfigSchema, SumBalancesFormatter, TAO_DECIMALS, abiMulticall, alphaToTao, calculateToken2022TransferFee, deriveMiniMetadataId, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, findDTaoClaimablePlancks, findDTaoConvictionLock, findDTaoRootStakeHold, getBalanceFingerprint, getBalanceId, getBalanceStorageFingerprint, getConvictionLockLabel, getDTaoClaimablePlancks, getEpochTransferFee, getExtension, getLockTitle, getLockedType, getMintExtensions, getRawLocks, getRawTotalPlanck, getSweepStaleVariant, getTokenMetadata, getTransferFeeConfig, getTransferHook, getValueId, includeInTotalExtraAmount, isDTaoClaimableLock, isEqualBalanceArrays, isEqualBalancesResult, isEqualMiniMetadatas, isEqualModuleResults, isNonTransferable, taoToAlpha, taoToAlphaCeil, uniswapV2PairAbi };
7405
7491
 
7406
7492
  //# sourceMappingURL=index.mjs.map