@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.d.mts +3838 -2950
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +3838 -2950
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +308 -218
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +307 -221
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
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", [
|
|
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
|
|
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/
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
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
|
-
|
|
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
|
|
4126
|
-
const
|
|
4127
|
-
const
|
|
4128
|
-
const
|
|
4129
|
-
for (const [address, stakes] of stakeInfos) for (const stake of stakes)
|
|
4130
|
-
|
|
4131
|
-
|
|
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
|
-
|
|
4135
|
-
]);
|
|
4316
|
+
hotkey: stake.hotkey
|
|
4317
|
+
});
|
|
4136
4318
|
}
|
|
4137
|
-
const [
|
|
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
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
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
|
|
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
|
|
4219
|
-
|
|
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
|
|
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
|
-
|
|
4478
|
-
|
|
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
|
-
/**
|
|
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:
|
|
4517
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
4557
|
-
const isDriftProneValue = (value) => value.label ===
|
|
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 ===
|
|
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 (!
|
|
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
|