@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.js
CHANGED
|
@@ -1778,6 +1778,21 @@ const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
|
|
1778
1778
|
}));
|
|
1779
1779
|
};
|
|
1780
1780
|
//#endregion
|
|
1781
|
+
//#region src/modules/shared/fetchBestBlockHash.ts
|
|
1782
|
+
/**
|
|
1783
|
+
* Hash of the current best block, to pin a poll's reads to.
|
|
1784
|
+
*
|
|
1785
|
+
* Chain state moves between the round-trips of a multi-call poll, and unpinned reads each
|
|
1786
|
+
* run against whatever block is best when they land. Combining values read at different
|
|
1787
|
+
* blocks fabricates data (eg a dtao basket claim total read one block after its
|
|
1788
|
+
* per-validator breakdown leaves an unattributed remainder).
|
|
1789
|
+
*/
|
|
1790
|
+
const fetchBestBlockHash = async (connector, networkId) => {
|
|
1791
|
+
const blockHash = await connector.send(networkId, "chain_getBlockHash", []);
|
|
1792
|
+
if (!blockHash) throw new Error(`Failed to fetch best block hash on ${networkId}`);
|
|
1793
|
+
return blockHash;
|
|
1794
|
+
};
|
|
1795
|
+
//#endregion
|
|
1781
1796
|
//#region src/modules/shared/parseMetadataRpcCached.ts
|
|
1782
1797
|
/**
|
|
1783
1798
|
* parseMetadataRpc does a full metadata decode + dynamic-builder build (tens to hundreds
|
|
@@ -1825,11 +1840,15 @@ const getRuntimeCall = (builder, apiName, method) => {
|
|
|
1825
1840
|
}
|
|
1826
1841
|
return call;
|
|
1827
1842
|
};
|
|
1828
|
-
const fetchRuntimeCallResult = async (connector, networkId, metadataRpcOrBuilder, apiName, method, args) => {
|
|
1843
|
+
const fetchRuntimeCallResult = async (connector, networkId, metadataRpcOrBuilder, apiName, method, args, at) => {
|
|
1829
1844
|
try {
|
|
1830
1845
|
const builder = typeof metadataRpcOrBuilder === "string" ? parseMetadataRpcCached(metadataRpcOrBuilder).builder : metadataRpcOrBuilder;
|
|
1831
1846
|
const call = getRuntimeCall(builder, apiName, method);
|
|
1832
|
-
const hex = await connector.send(networkId, "state_call", [
|
|
1847
|
+
const hex = await connector.send(networkId, "state_call", [
|
|
1848
|
+
`${apiName}_${method}`,
|
|
1849
|
+
(0, _talismn_scale.toHex)(call.args.enc(args)),
|
|
1850
|
+
...at ? [at] : []
|
|
1851
|
+
]);
|
|
1833
1852
|
const start = performance.now();
|
|
1834
1853
|
const result = call.value.dec(hex);
|
|
1835
1854
|
(0, _talismn_util.reportJsActivity)(`runtimeCall decode ${networkId} ${apiName}.${method} (~${Math.round((hex?.length ?? 0) / 2048)}KB)`, performance.now() - start);
|
|
@@ -1840,10 +1859,10 @@ const fetchRuntimeCallResult = async (connector, networkId, metadataRpcOrBuilder
|
|
|
1840
1859
|
};
|
|
1841
1860
|
//#endregion
|
|
1842
1861
|
//#region src/modules/shared/rpcQueryPack.ts
|
|
1843
|
-
const fetchRpcQueryPack = async (connector, networkId, queries) => {
|
|
1862
|
+
const fetchRpcQueryPack = async (connector, networkId, queries, at) => {
|
|
1844
1863
|
const allStateKeys = queries.flatMap(({ stateKeys }) => stateKeys).filter(_talismn_util.isNotNil);
|
|
1845
1864
|
if (!allStateKeys.length) return queries.map(({ stateKeys, decodeResult }) => decodeResult(stateKeys.map(() => null)));
|
|
1846
|
-
const [result] = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
|
1865
|
+
const [result] = await connector.send(networkId, "state_queryStorageAt", at ? [allStateKeys, at] : [allStateKeys]);
|
|
1847
1866
|
if (!result) throw new Error(`Empty state_queryStorageAt response on ${networkId}`);
|
|
1848
1867
|
return decodeRpcQueryPackChunked(queries, new Map(result.changes), { label: `rpcQueryPack decode ${networkId}` });
|
|
1849
1868
|
};
|
|
@@ -3927,6 +3946,74 @@ const taoToAlphaCeil = (tao, scaledAlphaPrice) => {
|
|
|
3927
3946
|
return (tao * ALPHA_PRICE_SCALE + scaledAlphaPrice - 1n) / scaledAlphaPrice;
|
|
3928
3947
|
};
|
|
3929
3948
|
//#endregion
|
|
3949
|
+
//#region src/modules/substrate-dtao/basketClaims.ts
|
|
3950
|
+
/** label of the claimable-rewards values emitted on root staking balances */
|
|
3951
|
+
const CLAIMABLE_REWARDS_LABEL = "Claimable rewards";
|
|
3952
|
+
/** Whether a lock is a claimable root rewards entitlement */
|
|
3953
|
+
const isDTaoClaimableLock = (lock) => lock.label === CLAIMABLE_REWARDS_LABEL;
|
|
3954
|
+
/** Sum of a balance's claimable root rewards (TAO plancks, marked NAV quote) */
|
|
3955
|
+
const getDTaoClaimablePlancks = (locks) => (locks ?? []).filter(isDTaoClaimableLock).reduce((sum, lock) => sum + lock.amount.planck, 0n);
|
|
3956
|
+
/**
|
|
3957
|
+
* The target pair's claimable rewards, null once its entitlement is gone (eg claimed elsewhere).
|
|
3958
|
+
* Sourced from balances rather than staking positions: the chain keeps basket entitlement
|
|
3959
|
+
* after a full unstake, so a claim can have no stake left on its validator.
|
|
3960
|
+
*/
|
|
3961
|
+
const findDTaoClaimablePlancks = (balances, { networkId, address, hotkey }) => {
|
|
3962
|
+
const balance = balances.find((b) => b.token?.type === "substrate-dtao" && b.token.networkId === networkId && b.token.netuid === 0 && b.token.hotkey === hotkey && (0, _talismn_crypto.isAddressEqual)(b.address, address));
|
|
3963
|
+
const claimablePlancks = balance ? getDTaoClaimablePlancks(balance.locks) : 0n;
|
|
3964
|
+
return claimablePlancks > 0n ? claimablePlancks : null;
|
|
3965
|
+
};
|
|
3966
|
+
/**
|
|
3967
|
+
* Fetches the TAO each coldkey would realize by redeeming its validator beta baskets
|
|
3968
|
+
* (Bittensor spec 441 "Root Reborn": root dividends accrue in per-validator escrow funds
|
|
3969
|
+
* and must be claimed manually; the payout is TAO staked back onto the root position).
|
|
3970
|
+
*
|
|
3971
|
+
* Amounts are marked NAV quotes (BetaBasketRuntimeApi), so they move with subnet pool
|
|
3972
|
+
* prices as well as accrual. Attribution is per validator hotkey via
|
|
3973
|
+
* `get_root_basket_positions`, which walks the chain's own coldkey→hotkeys index and so
|
|
3974
|
+
* includes validators the coldkey fully unstaked from (entitlement survives unstaking).
|
|
3975
|
+
*
|
|
3976
|
+
* It is the only entitlement read: the coldkey-wide `get_root_basket_owed` total sums the
|
|
3977
|
+
* same positions, and claiming is per validator hotkey, so entitlement outside a position
|
|
3978
|
+
* would be unclaimable anyway.
|
|
3979
|
+
*/
|
|
3980
|
+
const fetchBasketClaims = async (connector, networkId, metadataRpc, addresses, at) => {
|
|
3981
|
+
if (!addresses.length) return [];
|
|
3982
|
+
const { unifiedMetadata, builder } = parseMetadataRpcCached(metadataRpc);
|
|
3983
|
+
if (!hasRuntimeApi(unifiedMetadata, "BetaBasketRuntimeApi", "get_root_basket_positions")) return [];
|
|
3984
|
+
try {
|
|
3985
|
+
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]) => ({
|
|
3986
|
+
address,
|
|
3987
|
+
hotkey,
|
|
3988
|
+
amount: payoutTao
|
|
3989
|
+
})));
|
|
3990
|
+
} catch (cause) {
|
|
3991
|
+
log_default.warn(`Failed to fetch beta basket claims on ${networkId}`, { cause });
|
|
3992
|
+
throw cause;
|
|
3993
|
+
}
|
|
3994
|
+
};
|
|
3995
|
+
//#endregion
|
|
3996
|
+
//#region src/modules/substrate-dtao/fetchStorageKeysPaged.ts
|
|
3997
|
+
const PAGE_SIZE = 1e3;
|
|
3998
|
+
/**
|
|
3999
|
+
* Prefix scan via state_getKeysPaged. The unpaged state_getKeys is classified as heavy storage
|
|
4000
|
+
* work and rejected by some public nodes (eg the bittensor testnet one: "Storage work rate limit
|
|
4001
|
+
* exceeded"), while the paged variant is universally allowed.
|
|
4002
|
+
*/
|
|
4003
|
+
const fetchStorageKeysPaged = async (connector, networkId, keyPrefix, at) => {
|
|
4004
|
+
const keys = [];
|
|
4005
|
+
let startKey;
|
|
4006
|
+
do {
|
|
4007
|
+
const params = [keyPrefix, PAGE_SIZE];
|
|
4008
|
+
if (startKey || at) params.push(startKey ?? null);
|
|
4009
|
+
if (at) params.push(at);
|
|
4010
|
+
const page = await connector.send(networkId, "state_getKeysPaged", params);
|
|
4011
|
+
keys.push(...page);
|
|
4012
|
+
startKey = page.length === PAGE_SIZE ? page[page.length - 1] : void 0;
|
|
4013
|
+
} while (startKey);
|
|
4014
|
+
return keys;
|
|
4015
|
+
};
|
|
4016
|
+
//#endregion
|
|
3930
4017
|
//#region src/modules/substrate-dtao/convictionLocks.ts
|
|
3931
4018
|
const convictionLockKey = (address, netuid) => `${address}:${netuid}`;
|
|
3932
4019
|
const getConvictionLockLabel = (lockType) => lockType === "perpetual" ? "Perpetual Conviction Lock" : "Decaying Conviction Lock";
|
|
@@ -3974,17 +4061,17 @@ const toBigIntValue = (value) => {
|
|
|
3974
4061
|
* the chain keeps a zero-mass Lock entry alive while it still carries conviction ("ghost" lock),
|
|
3975
4062
|
* even after the coldkey fully unstaked from the subnet.
|
|
3976
4063
|
*/
|
|
3977
|
-
const fetchConvictionLocks = async (connector, networkId, metadataRpc, addresses) => {
|
|
4064
|
+
const fetchConvictionLocks = async (connector, networkId, metadataRpc, addresses, at) => {
|
|
3978
4065
|
if (!addresses.length) return [];
|
|
3979
4066
|
const { unifiedMetadata, builder } = parseMetadataRpcCached(metadataRpc);
|
|
3980
4067
|
if (!hasRuntimeApi(unifiedMetadata, "StakeInfoRuntimeApi", "get_coldkey_lock") || !hasStorageItems(unifiedMetadata, "SubtensorModule", ["Lock", "DecayingLock"])) return [];
|
|
3981
4068
|
try {
|
|
3982
4069
|
const lockStorageCoder = builder.buildStorage("SubtensorModule", "Lock");
|
|
3983
4070
|
const decayingLockStorageCoder = builder.buildStorage("SubtensorModule", "DecayingLock");
|
|
3984
|
-
const lockStorageKeys = await fetchConvictionLockStorageKeys(connector, networkId, addresses, lockStorageCoder);
|
|
4071
|
+
const lockStorageKeys = await fetchConvictionLockStorageKeys(connector, networkId, addresses, lockStorageCoder, at);
|
|
3985
4072
|
if (!lockStorageKeys.length) return [];
|
|
3986
4073
|
const hotkeyByPair = new Map(lockStorageKeys.map(({ address, netuid, hotkey }) => [convictionLockKey(address, netuid), hotkey]));
|
|
3987
|
-
const [lockModesByPair, lockStates] = await Promise.all([fetchConvictionLockModes(connector, networkId, lockStorageKeys, decayingLockStorageCoder), fetchColdkeyLockStates(connector, networkId, builder, lockStorageKeys)]);
|
|
4074
|
+
const [lockModesByPair, lockStates] = await Promise.all([fetchConvictionLockModes(connector, networkId, lockStorageKeys, decayingLockStorageCoder, at), fetchColdkeyLockStates(connector, networkId, builder, lockStorageKeys, at)]);
|
|
3988
4075
|
return lockStates.flatMap(({ address, netuid, lockState }) => {
|
|
3989
4076
|
const amount = lockState?.locked_mass ?? 0n;
|
|
3990
4077
|
const convictionRaw = toBigIntValue(lockState?.conviction);
|
|
@@ -4012,7 +4099,7 @@ const fetchConvictionLocks = async (connector, networkId, metadataRpc, addresses
|
|
|
4012
4099
|
* Encoding only the first map key (the coldkey) yields the storage key prefix covering all of the
|
|
4013
4100
|
* coldkey's (netuid, hotkey) entries, using the hashers declared in metadata.
|
|
4014
4101
|
*/
|
|
4015
|
-
const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, storageCoder) => {
|
|
4102
|
+
const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, storageCoder, at) => {
|
|
4016
4103
|
return (await Promise.all(addresses.map(async (address) => {
|
|
4017
4104
|
let keyPrefix;
|
|
4018
4105
|
try {
|
|
@@ -4023,7 +4110,7 @@ const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, s
|
|
|
4023
4110
|
}
|
|
4024
4111
|
let stateKeys;
|
|
4025
4112
|
try {
|
|
4026
|
-
stateKeys = await connector
|
|
4113
|
+
stateKeys = await fetchStorageKeysPaged(connector, networkId, keyPrefix, at);
|
|
4027
4114
|
} catch (cause) {
|
|
4028
4115
|
log_default.warn(`Failed to fetch conviction Lock keys (address=${address}) on ${networkId}`, { cause });
|
|
4029
4116
|
throw cause;
|
|
@@ -4043,7 +4130,7 @@ const fetchConvictionLockStorageKeys = async (connector, networkId, addresses, s
|
|
|
4043
4130
|
});
|
|
4044
4131
|
}))).flat();
|
|
4045
4132
|
};
|
|
4046
|
-
const fetchConvictionLockModes = async (connector, networkId, pairs, storageCoder) => {
|
|
4133
|
+
const fetchConvictionLockModes = async (connector, networkId, pairs, storageCoder, at) => {
|
|
4047
4134
|
const queries = pairs.map(({ address, netuid }) => {
|
|
4048
4135
|
let stateKey;
|
|
4049
4136
|
try {
|
|
@@ -4064,15 +4151,15 @@ const fetchConvictionLockModes = async (connector, networkId, pairs, storageCode
|
|
|
4064
4151
|
}
|
|
4065
4152
|
};
|
|
4066
4153
|
});
|
|
4067
|
-
return new Map(await fetchRpcQueryPack(connector, networkId, queries));
|
|
4154
|
+
return new Map(await fetchRpcQueryPack(connector, networkId, queries, at));
|
|
4068
4155
|
};
|
|
4069
|
-
const fetchColdkeyLockStates = async (connector, networkId, builder, pairs) => {
|
|
4156
|
+
const fetchColdkeyLockStates = async (connector, networkId, builder, pairs, at) => {
|
|
4070
4157
|
return Promise.all(pairs.map(async ({ address, netuid }) => {
|
|
4071
4158
|
try {
|
|
4072
4159
|
return {
|
|
4073
4160
|
address,
|
|
4074
4161
|
netuid,
|
|
4075
|
-
lockState: await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_coldkey_lock", [address, netuid])
|
|
4162
|
+
lockState: await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_coldkey_lock", [address, netuid], at)
|
|
4076
4163
|
};
|
|
4077
4164
|
} catch (cause) {
|
|
4078
4165
|
log_default.warn(`Failed to fetch get_coldkey_lock for (netuid=${netuid}, address=${address}) on ${networkId}`, { cause });
|
|
@@ -4085,29 +4172,122 @@ const fetchColdkeyLockStates = async (connector, networkId, builder, pairs) => {
|
|
|
4085
4172
|
const MODULE_TYPE$5 = _talismn_chaindata_provider.SubDTaoTokenSchema.shape.type.value;
|
|
4086
4173
|
const PLATFORM$5 = _talismn_chaindata_provider.SubDTaoTokenSchema.shape.platform.value;
|
|
4087
4174
|
//#endregion
|
|
4088
|
-
//#region src/modules/substrate-dtao/
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4175
|
+
//#region src/modules/substrate-dtao/rootStakeHold.ts
|
|
4176
|
+
/**
|
|
4177
|
+
* Extracts the root-stake hold from a balance's raw values (BalanceJson), if any.
|
|
4178
|
+
* While present, the pair's root stake cannot leave root — unstake/move/swap/transfer
|
|
4179
|
+
* would fail with `RootStakeLocked`. Only present while the window was still running
|
|
4180
|
+
* as of the last balances poll.
|
|
4181
|
+
*/
|
|
4182
|
+
const findDTaoRootStakeHold = (balance) => {
|
|
4183
|
+
for (const value of balance?.values ?? []) {
|
|
4184
|
+
const hold = value.meta?.rootStakeHold;
|
|
4185
|
+
if (hold?.type === "root-stake-hold") return hold;
|
|
4186
|
+
}
|
|
4187
|
+
return null;
|
|
4188
|
+
};
|
|
4189
|
+
/**
|
|
4190
|
+
* Fetches active root-stake hold windows (spec 441): when `RootStakeUnlockInterval` is
|
|
4191
|
+
* non-zero, root stake cannot leave root (remove/move/swap/transfer) until `interval`
|
|
4192
|
+
* blocks after the pair's last root stake add/remove/claim (`LastColdkeyHotkeyStakeBlock`).
|
|
4193
|
+
* Pairs already past their window are omitted — a returned hold means the pair's root
|
|
4194
|
+
* stake is currently unremovable.
|
|
4195
|
+
*
|
|
4196
|
+
* The interval is 0 (disabled) unless governance enables it — either by setting the
|
|
4197
|
+
* storage entry or via a runtime default that leaves it unset (metadata fallback). The
|
|
4198
|
+
* per-pair queries only run when it is non-zero.
|
|
4199
|
+
*/
|
|
4200
|
+
const fetchRootStakeHolds = async (connector, networkId, metadataRpc, rootPairs, at) => {
|
|
4201
|
+
if (!rootPairs.length) return [];
|
|
4202
|
+
const { unifiedMetadata, builder } = parseMetadataRpcCached(metadataRpc);
|
|
4203
|
+
if (!hasStorageItems(unifiedMetadata, "SubtensorModule", ["RootStakeUnlockInterval", "LastColdkeyHotkeyStakeBlock"])) return [];
|
|
4204
|
+
try {
|
|
4205
|
+
const interval = await fetchUnlockInterval(connector, networkId, unifiedMetadata, builder, at);
|
|
4206
|
+
if (interval === 0n) return [];
|
|
4207
|
+
const { currentBlock, lastStakeBlocks } = await fetchLastStakeBlocks(connector, networkId, builder, rootPairs, at);
|
|
4208
|
+
return lastStakeBlocks.flatMap(({ address, hotkey, lastStakeBlock }) => {
|
|
4209
|
+
if (lastStakeBlock === 0n) return [];
|
|
4210
|
+
const unlockAtBlock = lastStakeBlock + interval;
|
|
4211
|
+
if (unlockAtBlock <= BigInt(currentBlock)) return [];
|
|
4212
|
+
return [{
|
|
4213
|
+
address,
|
|
4214
|
+
hotkey,
|
|
4215
|
+
unlockAtBlock: Number(unlockAtBlock)
|
|
4216
|
+
}];
|
|
4104
4217
|
});
|
|
4218
|
+
} catch (cause) {
|
|
4219
|
+
log_default.warn(`Failed to fetch root stake holds on ${networkId}`, { cause });
|
|
4220
|
+
throw cause;
|
|
4105
4221
|
}
|
|
4106
|
-
|
|
4222
|
+
};
|
|
4223
|
+
const fetchUnlockInterval = async (connector, networkId, unifiedMetadata, builder, at) => {
|
|
4224
|
+
const storageCoder = builder.buildStorage("SubtensorModule", "RootStakeUnlockInterval");
|
|
4225
|
+
const [interval] = await fetchRpcQueryPack(connector, networkId, [{
|
|
4226
|
+
stateKeys: [storageCoder.keys.enc()],
|
|
4227
|
+
decodeResult: (changes) => {
|
|
4228
|
+
const encoded = changes[0] ?? getUnlockIntervalFallback(unifiedMetadata, networkId);
|
|
4229
|
+
const decoded = (0, _talismn_scale.decodeScale)(storageCoder, encoded, `Failed to decode RootStakeUnlockInterval on ${networkId}`);
|
|
4230
|
+
if (decoded === null) throw new Error(`Failed to decode RootStakeUnlockInterval on ${networkId}`);
|
|
4231
|
+
return decoded;
|
|
4232
|
+
}
|
|
4233
|
+
}], at);
|
|
4234
|
+
return interval ?? 0n;
|
|
4235
|
+
};
|
|
4236
|
+
const getUnlockIntervalFallback = (unifiedMetadata, networkId) => {
|
|
4237
|
+
const fallback = unifiedMetadata.pallets.find((pallet) => pallet.name === "SubtensorModule")?.storage?.items.find((item) => item.name === "RootStakeUnlockInterval")?.fallback;
|
|
4238
|
+
if (!fallback) throw new Error(`Missing RootStakeUnlockInterval metadata fallback on ${networkId}`);
|
|
4239
|
+
return fallback;
|
|
4240
|
+
};
|
|
4241
|
+
/**
|
|
4242
|
+
* Reads each pair's `LastColdkeyHotkeyStakeBlock` plus the current block number, all from
|
|
4243
|
+
* one state snapshot: `System.Number` rides along as an extra key of the same
|
|
4244
|
+
* `state_queryStorageAt` instead of costing a separate `chain_getHeader` round trip.
|
|
4245
|
+
*/
|
|
4246
|
+
const fetchLastStakeBlocks = async (connector, networkId, builder, rootPairs, at) => {
|
|
4247
|
+
const numberCoder = builder.buildStorage("System", "Number");
|
|
4248
|
+
const currentBlockQuery = {
|
|
4249
|
+
stateKeys: [numberCoder.keys.enc()],
|
|
4250
|
+
decodeResult: (changes) => {
|
|
4251
|
+
const decoded = (0, _talismn_scale.decodeScale)(numberCoder, changes[0], `Failed to decode System.Number on ${networkId}`);
|
|
4252
|
+
if (decoded === null) throw new Error(`Failed to decode System.Number on ${networkId}`);
|
|
4253
|
+
return Number(decoded);
|
|
4254
|
+
}
|
|
4255
|
+
};
|
|
4256
|
+
const storageCoder = builder.buildStorage("SubtensorModule", "LastColdkeyHotkeyStakeBlock");
|
|
4257
|
+
const [currentBlock, ...lastStakeBlocks] = await fetchRpcQueryPack(connector, networkId, [currentBlockQuery, ...rootPairs.map(({ address, hotkey }) => {
|
|
4258
|
+
let stateKey;
|
|
4259
|
+
try {
|
|
4260
|
+
stateKey = storageCoder.keys.enc(address, hotkey);
|
|
4261
|
+
} catch (cause) {
|
|
4262
|
+
log_default.warn(`Failed to encode LastColdkeyHotkeyStakeBlock key (address=${address}, hotkey=${hotkey}) on ${networkId}`, { cause });
|
|
4263
|
+
throw cause;
|
|
4264
|
+
}
|
|
4265
|
+
return {
|
|
4266
|
+
stateKeys: [stateKey],
|
|
4267
|
+
decodeResult: (changes) => {
|
|
4268
|
+
const hexValue = changes[0];
|
|
4269
|
+
if (!hexValue) return {
|
|
4270
|
+
address,
|
|
4271
|
+
hotkey,
|
|
4272
|
+
lastStakeBlock: 0n
|
|
4273
|
+
};
|
|
4274
|
+
const decoded = (0, _talismn_scale.decodeScale)(storageCoder, hexValue, `Failed to decode LastColdkeyHotkeyStakeBlock for (address=${address}, hotkey=${hotkey}) on ${networkId}`);
|
|
4275
|
+
if (decoded === null) throw new Error(`Failed to decode LastColdkeyHotkeyStakeBlock for (address=${address}, hotkey=${hotkey}) on ${networkId}`);
|
|
4276
|
+
return {
|
|
4277
|
+
address,
|
|
4278
|
+
hotkey,
|
|
4279
|
+
lastStakeBlock: decoded
|
|
4280
|
+
};
|
|
4281
|
+
}
|
|
4282
|
+
};
|
|
4283
|
+
})], at);
|
|
4284
|
+
return {
|
|
4285
|
+
currentBlock,
|
|
4286
|
+
lastStakeBlocks
|
|
4287
|
+
};
|
|
4107
4288
|
};
|
|
4108
4289
|
//#endregion
|
|
4109
4290
|
//#region src/modules/substrate-dtao/fetchBalances.ts
|
|
4110
|
-
const ROOT_NETUID = 0;
|
|
4111
4291
|
const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, miniMetadata, signal }) => {
|
|
4112
4292
|
if (!tokensWithAddresses.length) return {
|
|
4113
4293
|
success: [],
|
|
@@ -4150,29 +4330,36 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
|
|
|
4150
4330
|
const addresses = (0, lodash_es.uniq)(balanceDefs.map((def) => def.address));
|
|
4151
4331
|
try {
|
|
4152
4332
|
const { builder } = parseMetadataRpcCached(miniMetadata.data);
|
|
4153
|
-
const
|
|
4154
|
-
const
|
|
4155
|
-
const
|
|
4156
|
-
const
|
|
4157
|
-
for (const [address, stakes] of stakeInfos) for (const stake of stakes)
|
|
4158
|
-
|
|
4159
|
-
|
|
4333
|
+
const at = await fetchBestBlockHash(connector, networkId);
|
|
4334
|
+
const stakeInfos = await fetchRuntimeCallResult(connector, networkId, builder, "StakeInfoRuntimeApi", "get_stake_info_for_coldkeys", [addresses], at);
|
|
4335
|
+
const rootPairs = [];
|
|
4336
|
+
const seenRootPairs = /* @__PURE__ */ new Set();
|
|
4337
|
+
for (const [address, stakes] of stakeInfos) for (const stake of stakes) {
|
|
4338
|
+
if (stake.netuid !== 0) continue;
|
|
4339
|
+
const pairKey = `${address}:${stake.hotkey}`;
|
|
4340
|
+
if (seenRootPairs.has(pairKey)) continue;
|
|
4341
|
+
seenRootPairs.add(pairKey);
|
|
4342
|
+
rootPairs.push({
|
|
4160
4343
|
address,
|
|
4161
|
-
stake.hotkey
|
|
4162
|
-
|
|
4163
|
-
]);
|
|
4344
|
+
hotkey: stake.hotkey
|
|
4345
|
+
});
|
|
4164
4346
|
}
|
|
4165
|
-
const [
|
|
4347
|
+
const [convictionLocks, basketClaims, rootStakeHolds] = miniMetadata.data ? await Promise.all([
|
|
4348
|
+
fetchConvictionLocks(connector, networkId, miniMetadata.data, addresses, at),
|
|
4349
|
+
fetchBasketClaims(connector, networkId, miniMetadata.data, addresses, at),
|
|
4350
|
+
fetchRootStakeHolds(connector, networkId, miniMetadata.data, rootPairs, at)
|
|
4351
|
+
]) : [
|
|
4352
|
+
[],
|
|
4353
|
+
[],
|
|
4354
|
+
[]
|
|
4355
|
+
];
|
|
4166
4356
|
const upsertBalance = (acc, address, tokenId, balance) => {
|
|
4167
4357
|
const key = `${address}:${tokenId}`;
|
|
4168
4358
|
const recordedBalance = acc[key];
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
...balance.convictionLock !== void 0 && { convictionLock: balance.convictionLock }
|
|
4174
|
-
};
|
|
4175
|
-
else acc[key] = balance;
|
|
4359
|
+
acc[key] = recordedBalance ? (0, lodash_es.mergeWith)({}, recordedBalance, balance, (recorded, incoming, field) => {
|
|
4360
|
+
if (field === "stake" && recorded !== void 0) return recorded + incoming;
|
|
4361
|
+
return incoming;
|
|
4362
|
+
}) : balance;
|
|
4176
4363
|
};
|
|
4177
4364
|
const slicer = (0, _talismn_util.createTimeSlicer)({
|
|
4178
4365
|
signal,
|
|
@@ -4189,21 +4376,21 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
|
|
|
4189
4376
|
netuid: stake.netuid
|
|
4190
4377
|
};
|
|
4191
4378
|
upsertBalance(balancesRaw, address, balance.tokenId, balance);
|
|
4192
|
-
if (stake.netuid === ROOT_NETUID) {
|
|
4193
|
-
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey) ?? /* @__PURE__ */ new Map();
|
|
4194
|
-
const alreadyClaimedMap = rootClaimedAmounts.get(address)?.get(stake.hotkey) ?? /* @__PURE__ */ new Map();
|
|
4195
|
-
calculatePendingRootClaimable({
|
|
4196
|
-
stake: stake.stake,
|
|
4197
|
-
hotkey: stake.hotkey,
|
|
4198
|
-
address,
|
|
4199
|
-
networkId,
|
|
4200
|
-
validatorRootClaimableRate: claimableRates,
|
|
4201
|
-
alreadyClaimedByNetuid: alreadyClaimedMap
|
|
4202
|
-
}).forEach((balance) => {
|
|
4203
|
-
upsertBalance(balancesRaw, address, balance.tokenId, balance);
|
|
4204
|
-
});
|
|
4205
|
-
}
|
|
4206
4379
|
}, { slicer });
|
|
4380
|
+
const upsertRootPairFields = (items, getFields) => (0, _talismn_util.forEachWithYield)(items, (item) => {
|
|
4381
|
+
const tokenId = (0, _talismn_chaindata_provider.subDTaoTokenId)(networkId, 0, item.hotkey);
|
|
4382
|
+
upsertBalance(balancesRaw, item.address, tokenId, {
|
|
4383
|
+
address: item.address,
|
|
4384
|
+
tokenId,
|
|
4385
|
+
baseTokenId: (0, _talismn_chaindata_provider.subDTaoTokenId)(networkId, 0),
|
|
4386
|
+
stake: 0n,
|
|
4387
|
+
hotkey: item.hotkey,
|
|
4388
|
+
netuid: 0,
|
|
4389
|
+
...getFields(item)
|
|
4390
|
+
});
|
|
4391
|
+
}, { slicer });
|
|
4392
|
+
await upsertRootPairFields(basketClaims, ({ amount }) => ({ claimable: amount }));
|
|
4393
|
+
await upsertRootPairFields(rootStakeHolds, ({ unlockAtBlock }) => ({ rootStakeHoldUnlockBlock: unlockAtBlock }));
|
|
4207
4394
|
await (0, _talismn_util.forEachWithYield)(convictionLocks, ({ address, netuid, lock }) => {
|
|
4208
4395
|
const balance = {
|
|
4209
4396
|
address,
|
|
@@ -4240,21 +4427,35 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
|
|
|
4240
4427
|
const stake = balancesRaw[`${def.address}:${def.token.id}`];
|
|
4241
4428
|
if (!stake) return null;
|
|
4242
4429
|
const stakeAmount = BigInt(stake.stake?.toString() ?? "0");
|
|
4243
|
-
const
|
|
4430
|
+
const claimableAmount = BigInt(stake.claimable?.toString() ?? "0");
|
|
4244
4431
|
const convictionLockAmount = BigInt(stake.convictionLock?.amount?.toString() ?? "0");
|
|
4245
4432
|
const convictionLockConviction = BigInt(stake.convictionLock?.convictionRaw ?? "0");
|
|
4246
|
-
const
|
|
4247
|
-
|
|
4433
|
+
const rootStakeHoldMeta = stake.rootStakeHoldUnlockBlock !== void 0 ? { rootStakeHold: {
|
|
4434
|
+
type: "root-stake-hold",
|
|
4435
|
+
unlockAtBlock: stake.rootStakeHoldUnlockBlock
|
|
4436
|
+
} } : void 0;
|
|
4248
4437
|
const values = [{
|
|
4249
4438
|
type: "free",
|
|
4250
4439
|
label: stake.netuid === 0 ? "Root Staking" : `Subnet Staking`,
|
|
4251
|
-
amount: stakeAmount.toString()
|
|
4252
|
-
|
|
4253
|
-
type: "locked",
|
|
4254
|
-
label: "Pending root claim",
|
|
4255
|
-
amount: pendingRootClaimAmount.toString(),
|
|
4256
|
-
includeInTransferable: true
|
|
4440
|
+
amount: stakeAmount.toString(),
|
|
4441
|
+
...rootStakeHoldMeta && { meta: rootStakeHoldMeta }
|
|
4257
4442
|
}];
|
|
4443
|
+
if (claimableAmount > 0n) {
|
|
4444
|
+
const claimable = {
|
|
4445
|
+
label: CLAIMABLE_REWARDS_LABEL,
|
|
4446
|
+
amount: claimableAmount.toString()
|
|
4447
|
+
};
|
|
4448
|
+
values.push({
|
|
4449
|
+
...claimable,
|
|
4450
|
+
type: "locked",
|
|
4451
|
+
includeInTransferable: true
|
|
4452
|
+
});
|
|
4453
|
+
values.push({
|
|
4454
|
+
...claimable,
|
|
4455
|
+
type: "extra",
|
|
4456
|
+
includeInTotal: true
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4258
4459
|
if (stake.convictionLock && (convictionLockAmount > 0n || convictionLockConviction > 0n)) {
|
|
4259
4460
|
const convictionLockMeta = { convictionLock: {
|
|
4260
4461
|
type: "conviction-lock",
|
|
@@ -4268,12 +4469,6 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
|
|
|
4268
4469
|
meta: convictionLockMeta
|
|
4269
4470
|
});
|
|
4270
4471
|
}
|
|
4271
|
-
if (hasZeroStake && hasPendingRootClaim) values.push({
|
|
4272
|
-
type: "extra",
|
|
4273
|
-
label: "Pending root claim",
|
|
4274
|
-
amount: pendingRootClaimAmount.toString(),
|
|
4275
|
-
includeInTotal: true
|
|
4276
|
-
});
|
|
4277
4472
|
return {
|
|
4278
4473
|
address: def.address,
|
|
4279
4474
|
networkId,
|
|
@@ -4299,124 +4494,6 @@ const fetchBalances$5 = async ({ networkId, tokensWithAddresses, connector, mini
|
|
|
4299
4494
|
};
|
|
4300
4495
|
}
|
|
4301
4496
|
};
|
|
4302
|
-
const buildStorageCoder = (metadataRpc, pallet, entry) => {
|
|
4303
|
-
const { builder } = parseMetadataRpcCached(metadataRpc);
|
|
4304
|
-
return builder.buildStorage(pallet, entry);
|
|
4305
|
-
};
|
|
4306
|
-
const buildRootClaimableStorageCoder = async (_connector, networkId, metadataRpc) => {
|
|
4307
|
-
let storageCoder = null;
|
|
4308
|
-
if (metadataRpc) try {
|
|
4309
|
-
storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimable");
|
|
4310
|
-
} catch (cause) {
|
|
4311
|
-
log_default.warn(`Failed to build storage coder for SubtensorModule.RootClaimable using provided metadata on ${networkId}`, { cause });
|
|
4312
|
-
}
|
|
4313
|
-
return storageCoder;
|
|
4314
|
-
};
|
|
4315
|
-
const buildRootClaimedStorageCoder = async (networkId, metadataRpc) => {
|
|
4316
|
-
let storageCoder = null;
|
|
4317
|
-
if (metadataRpc) try {
|
|
4318
|
-
storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimed");
|
|
4319
|
-
} catch (cause) {
|
|
4320
|
-
log_default.warn(`Failed to build storage coder for SubtensorModule.RootClaimed using provided metadata on ${networkId}`, { cause });
|
|
4321
|
-
}
|
|
4322
|
-
return storageCoder;
|
|
4323
|
-
};
|
|
4324
|
-
const buildRootClaimableQueries = (networkId, hotkeys, storageCoder) => {
|
|
4325
|
-
return hotkeys.map((hotkey) => {
|
|
4326
|
-
let stateKey;
|
|
4327
|
-
try {
|
|
4328
|
-
stateKey = storageCoder.keys.enc(hotkey);
|
|
4329
|
-
} catch (cause) {
|
|
4330
|
-
log_default.warn(`Failed to encode storage key for hotkey ${hotkey} on ${networkId}`, { cause });
|
|
4331
|
-
throw cause;
|
|
4332
|
-
}
|
|
4333
|
-
const decodeResult = (changes) => {
|
|
4334
|
-
const hexValue = changes[0];
|
|
4335
|
-
if (!hexValue) return [hotkey, /* @__PURE__ */ new Map()];
|
|
4336
|
-
const decoded = (0, _talismn_scale.decodeScale)(storageCoder, hexValue, `Failed to decode RootClaimable for hotkey ${hotkey} on ${networkId}`);
|
|
4337
|
-
if (decoded === null) throw new Error(`Failed to decode RootClaimable for hotkey ${hotkey} on ${networkId}`);
|
|
4338
|
-
return [hotkey, new Map(decoded)];
|
|
4339
|
-
};
|
|
4340
|
-
return {
|
|
4341
|
-
stateKeys: [stateKey],
|
|
4342
|
-
decodeResult
|
|
4343
|
-
};
|
|
4344
|
-
});
|
|
4345
|
-
};
|
|
4346
|
-
const fetchRootClaimableRates = async (connector, networkId, metadataRpc, hotkeys) => {
|
|
4347
|
-
if (!hotkeys.length) return /* @__PURE__ */ new Map();
|
|
4348
|
-
const storageCoder = await buildRootClaimableStorageCoder(connector, networkId, metadataRpc);
|
|
4349
|
-
if (!storageCoder) return new Map(hotkeys.map((hotkey) => [hotkey, /* @__PURE__ */ new Map()]));
|
|
4350
|
-
const queries = buildRootClaimableQueries(networkId, hotkeys, storageCoder);
|
|
4351
|
-
try {
|
|
4352
|
-
const results = await fetchRpcQueryPack(connector, networkId, queries);
|
|
4353
|
-
return new Map(results);
|
|
4354
|
-
} catch (cause) {
|
|
4355
|
-
log_default.warn(`Failed to fetch RootClaimable for hotkeys on ${networkId}`, { cause });
|
|
4356
|
-
throw cause;
|
|
4357
|
-
}
|
|
4358
|
-
};
|
|
4359
|
-
const buildRootClaimedQueries = (networkId, addressHotkeyNetuidPairs, storageCoder) => {
|
|
4360
|
-
return addressHotkeyNetuidPairs.map(([address, hotkey, netuid]) => {
|
|
4361
|
-
let stateKey;
|
|
4362
|
-
try {
|
|
4363
|
-
stateKey = storageCoder.keys.enc(netuid, hotkey, address);
|
|
4364
|
-
} catch (cause) {
|
|
4365
|
-
log_default.warn(`Failed to encode storage key for RootClaimed (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`, { cause });
|
|
4366
|
-
throw cause;
|
|
4367
|
-
}
|
|
4368
|
-
const decodeResult = (changes) => {
|
|
4369
|
-
const hexValue = changes[0];
|
|
4370
|
-
if (!hexValue) return [
|
|
4371
|
-
address,
|
|
4372
|
-
hotkey,
|
|
4373
|
-
netuid,
|
|
4374
|
-
0n
|
|
4375
|
-
];
|
|
4376
|
-
const decoded = (0, _talismn_scale.decodeScale)(storageCoder, hexValue, `Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
|
|
4377
|
-
if (decoded === null) throw new Error(`Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
|
|
4378
|
-
return [
|
|
4379
|
-
address,
|
|
4380
|
-
hotkey,
|
|
4381
|
-
netuid,
|
|
4382
|
-
decoded
|
|
4383
|
-
];
|
|
4384
|
-
};
|
|
4385
|
-
return {
|
|
4386
|
-
stateKeys: [stateKey],
|
|
4387
|
-
decodeResult
|
|
4388
|
-
};
|
|
4389
|
-
});
|
|
4390
|
-
};
|
|
4391
|
-
const fetchRootClaimedAmounts = async (connector, networkId, metadataRpc, addressHotkeyNetuidPairs) => {
|
|
4392
|
-
if (!addressHotkeyNetuidPairs.length) return /* @__PURE__ */ new Map();
|
|
4393
|
-
const storageCoder = await buildRootClaimedStorageCoder(networkId, metadataRpc);
|
|
4394
|
-
if (!storageCoder) {
|
|
4395
|
-
const result = /* @__PURE__ */ new Map();
|
|
4396
|
-
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
4397
|
-
if (!result.has(address)) result.set(address, /* @__PURE__ */ new Map());
|
|
4398
|
-
const addressMap = result.get(address);
|
|
4399
|
-
if (!addressMap.has(hotkey)) addressMap.set(hotkey, /* @__PURE__ */ new Map());
|
|
4400
|
-
addressMap.get(hotkey).set(netuid, 0n);
|
|
4401
|
-
}
|
|
4402
|
-
return result;
|
|
4403
|
-
}
|
|
4404
|
-
const queries = buildRootClaimedQueries(networkId, addressHotkeyNetuidPairs, storageCoder);
|
|
4405
|
-
try {
|
|
4406
|
-
const results = await fetchRpcQueryPack(connector, networkId, queries);
|
|
4407
|
-
const result = /* @__PURE__ */ new Map();
|
|
4408
|
-
for (const [address, hotkey, netuid, claimed] of results) {
|
|
4409
|
-
if (!result.has(address)) result.set(address, /* @__PURE__ */ new Map());
|
|
4410
|
-
const addressMap = result.get(address);
|
|
4411
|
-
if (!addressMap.has(hotkey)) addressMap.set(hotkey, /* @__PURE__ */ new Map());
|
|
4412
|
-
addressMap.get(hotkey).set(netuid, claimed);
|
|
4413
|
-
}
|
|
4414
|
-
return result;
|
|
4415
|
-
} catch (cause) {
|
|
4416
|
-
log_default.warn(`Failed to fetch RootClaimed for address-hotkey-netuid pairs on ${networkId}`, { cause });
|
|
4417
|
-
throw cause;
|
|
4418
|
-
}
|
|
4419
|
-
};
|
|
4420
4497
|
//#endregion
|
|
4421
4498
|
//#region src/modules/substrate-dtao/fetchTokens.ts
|
|
4422
4499
|
const NATIVE_TOKEN_SYMBOLS = {
|
|
@@ -4455,7 +4532,7 @@ const fetchTokens$5 = async ({ networkId, connector, tokens, miniMetadata }) =>
|
|
|
4455
4532
|
const fetchTransferableTokensMap = async (connector, metadata, networkId) => {
|
|
4456
4533
|
const { builder } = parseMetadataRpcCached(metadata);
|
|
4457
4534
|
const transferToggleCodec = builder.buildStorage("SubtensorModule", "TransferToggle");
|
|
4458
|
-
const transferToggleKeys = await connector
|
|
4535
|
+
const transferToggleKeys = await fetchStorageKeysPaged(connector, networkId, (0, _talismn_scale.getStorageKeyPrefix)("SubtensorModule", "TransferToggle"));
|
|
4459
4536
|
const transferToggleResults = await connector.send(networkId, "state_queryStorageAt", [transferToggleKeys]);
|
|
4460
4537
|
return (0, lodash_es.fromPairs)((transferToggleResults.length ? transferToggleResults[0].changes : []).map(([key, value]) => {
|
|
4461
4538
|
const [netuid] = transferToggleCodec.keys.dec(key);
|
|
@@ -4493,18 +4570,28 @@ const getData$3 = (metadataRpc) => {
|
|
|
4493
4570
|
pallet: "SubtensorModule",
|
|
4494
4571
|
items: [
|
|
4495
4572
|
"TransferToggle",
|
|
4496
|
-
"RootClaimable",
|
|
4497
|
-
"RootClaimed",
|
|
4498
4573
|
"Lock",
|
|
4499
|
-
"DecayingLock"
|
|
4574
|
+
"DecayingLock",
|
|
4575
|
+
"RootStakeUnlockInterval",
|
|
4576
|
+
"LastColdkeyHotkeyStakeBlock"
|
|
4500
4577
|
]
|
|
4501
|
-
}], [{
|
|
4502
|
-
runtimeApi: "StakeInfoRuntimeApi",
|
|
4503
|
-
methods: ["get_stake_info_for_coldkeys", "get_coldkey_lock"]
|
|
4504
4578
|
}, {
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
}]
|
|
4579
|
+
pallet: "System",
|
|
4580
|
+
items: ["Number"]
|
|
4581
|
+
}], [
|
|
4582
|
+
{
|
|
4583
|
+
runtimeApi: "StakeInfoRuntimeApi",
|
|
4584
|
+
methods: ["get_stake_info_for_coldkeys", "get_coldkey_lock"]
|
|
4585
|
+
},
|
|
4586
|
+
{
|
|
4587
|
+
runtimeApi: "SubnetInfoRuntimeApi",
|
|
4588
|
+
methods: ["get_all_dynamic_info"]
|
|
4589
|
+
},
|
|
4590
|
+
{
|
|
4591
|
+
runtimeApi: "BetaBasketRuntimeApi",
|
|
4592
|
+
methods: ["get_root_basket_positions"]
|
|
4593
|
+
}
|
|
4594
|
+
]);
|
|
4508
4595
|
return (0, _talismn_scale.encodeMetadata)(metadata);
|
|
4509
4596
|
};
|
|
4510
4597
|
//#endregion
|
|
@@ -4530,7 +4617,8 @@ const getTransferCallData$5 = ({ from, to, value, token, metadataRpc }) => {
|
|
|
4530
4617
|
};
|
|
4531
4618
|
//#endregion
|
|
4532
4619
|
//#region src/modules/substrate-dtao/isEffectivelyEqualDTaoBalance.ts
|
|
4533
|
-
/**
|
|
4620
|
+
/** basket claimable amounts are marked NAV quotes: they move with subnet pool prices and
|
|
4621
|
+
* dividend accrual on (nearly) every block; the display is informational */
|
|
4534
4622
|
const CLAIM_DRIFT_TOLERANCE_BPS = 100n;
|
|
4535
4623
|
/**
|
|
4536
4624
|
* subnet staking positions auto-compound: dividend injections land once per subnet tempo
|
|
@@ -4541,8 +4629,8 @@ const CLAIM_DRIFT_TOLERANCE_BPS = 100n;
|
|
|
4541
4629
|
const STAKE_DRIFT_TOLERANCE_BPS = 100n;
|
|
4542
4630
|
/**
|
|
4543
4631
|
* dtao balances embed values that drift on (nearly) every block even when the user's
|
|
4544
|
-
* position is untouched:
|
|
4545
|
-
*
|
|
4632
|
+
* position is untouched: staking positions auto-compound, basket claimable quotes move,
|
|
4633
|
+
* and conviction locks decay.
|
|
4546
4634
|
*
|
|
4547
4635
|
* Without special handling, every 6s poll re-emits the full result set, which defeats
|
|
4548
4636
|
* every distinctUntilChanged stage downstream and forces the whole pipeline
|
|
@@ -4554,9 +4642,7 @@ const STAKE_DRIFT_TOLERANCE_BPS = 100n;
|
|
|
4554
4642
|
* drift-prone values above (relative to the previously EMITTED value, so movement
|
|
4555
4643
|
* accumulates and a sustained move still surfaces)
|
|
4556
4644
|
* - "drift" when ONLY the drift-prone values moved, beyond tolerance. The stabilizer
|
|
4557
|
-
* re-emits these at most once per refresh interval
|
|
4558
|
-
* values (a young pending claim can grow >1% per poll indefinitely, so a purely
|
|
4559
|
-
* relative tolerance can never suppress it)
|
|
4645
|
+
* re-emits these at most once per refresh interval
|
|
4560
4646
|
* - "changed" for structural changes (stake, locks, status, value set) — emitted
|
|
4561
4647
|
* immediately
|
|
4562
4648
|
*/
|
|
@@ -4575,14 +4661,13 @@ const isWithinDriftTolerance = (previous, next, toleranceBps) => {
|
|
|
4575
4661
|
const max = a > b ? a : b;
|
|
4576
4662
|
return diff * 10000n <= max * toleranceBps;
|
|
4577
4663
|
};
|
|
4578
|
-
/** the only labels whose amounts accrue per block (see fetchBalances) */
|
|
4579
|
-
const PENDING_ROOT_CLAIM_LABEL = "Pending root claim";
|
|
4580
4664
|
const changed = (reason) => ({
|
|
4581
4665
|
equivalence: "changed",
|
|
4582
4666
|
reason
|
|
4583
4667
|
});
|
|
4584
|
-
/**
|
|
4585
|
-
const isDriftProneValue = (value) => value.label ===
|
|
4668
|
+
/** claimable quotes and decaying locks may appear as their amounts cross zero */
|
|
4669
|
+
const isDriftProneValue = (value) => value.label === "Claimable rewards" || hasConvictionLock(value);
|
|
4670
|
+
const hasConvictionLock = (value) => !!value.meta?.convictionLock;
|
|
4586
4671
|
const classifyValue = (previous, next) => {
|
|
4587
4672
|
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");
|
|
4588
4673
|
const previousMeta = previous.meta;
|
|
@@ -4590,8 +4675,9 @@ const classifyValue = (previous, next) => {
|
|
|
4590
4675
|
const previousLock = previousMeta?.convictionLock;
|
|
4591
4676
|
const nextLock = nextMeta?.convictionLock;
|
|
4592
4677
|
if (previousLock?.type !== nextLock?.type || previousLock?.hotkey !== nextLock?.hotkey || previousLock?.lockType !== nextLock?.lockType) return changed("conviction lock meta");
|
|
4678
|
+
if (previousMeta?.rootStakeHold?.unlockAtBlock !== nextMeta?.rootStakeHold?.unlockAtBlock) return changed("root stake hold meta");
|
|
4593
4679
|
let drift = false;
|
|
4594
|
-
if (previous.amount !== next.amount) if (previous.label ===
|
|
4680
|
+
if (previous.amount !== next.amount) if (previous.label === "Claimable rewards") {
|
|
4595
4681
|
if (!isWithinDriftTolerance(previous.amount, next.amount, CLAIM_DRIFT_TOLERANCE_BPS)) drift = true;
|
|
4596
4682
|
} else if (previousLock) drift = true;
|
|
4597
4683
|
else if (isWithinDriftTolerance(previous.amount, next.amount, STAKE_DRIFT_TOLERANCE_BPS)) drift = true;
|
|
@@ -4614,7 +4700,7 @@ const classifyBalance = (previous, next) => {
|
|
|
4614
4700
|
for (const [key, previousValue] of previousByKey) {
|
|
4615
4701
|
const nextValue = nextByKey.get(key);
|
|
4616
4702
|
if (!nextValue) {
|
|
4617
|
-
if (!
|
|
4703
|
+
if (!hasConvictionLock(previousValue)) return changed(`value removed: ${key}`);
|
|
4618
4704
|
drift = true;
|
|
4619
4705
|
continue;
|
|
4620
4706
|
}
|
|
@@ -7486,11 +7572,14 @@ exports.excludeFromFeePayableLocks = excludeFromFeePayableLocks;
|
|
|
7486
7572
|
exports.excludeFromTransferableAmount = excludeFromTransferableAmount;
|
|
7487
7573
|
exports.filterBaseLocks = filterBaseLocks;
|
|
7488
7574
|
exports.filterMirrorTokens = filterMirrorTokens;
|
|
7575
|
+
exports.findDTaoClaimablePlancks = findDTaoClaimablePlancks;
|
|
7489
7576
|
exports.findDTaoConvictionLock = findDTaoConvictionLock;
|
|
7577
|
+
exports.findDTaoRootStakeHold = findDTaoRootStakeHold;
|
|
7490
7578
|
exports.getBalanceFingerprint = getBalanceFingerprint;
|
|
7491
7579
|
exports.getBalanceId = getBalanceId;
|
|
7492
7580
|
exports.getBalanceStorageFingerprint = getBalanceStorageFingerprint;
|
|
7493
7581
|
exports.getConvictionLockLabel = getConvictionLockLabel;
|
|
7582
|
+
exports.getDTaoClaimablePlancks = getDTaoClaimablePlancks;
|
|
7494
7583
|
exports.getEpochTransferFee = getEpochTransferFee;
|
|
7495
7584
|
exports.getExtension = getExtension;
|
|
7496
7585
|
exports.getLockTitle = getLockTitle;
|
|
@@ -7504,6 +7593,7 @@ exports.getTransferFeeConfig = getTransferFeeConfig;
|
|
|
7504
7593
|
exports.getTransferHook = getTransferHook;
|
|
7505
7594
|
exports.getValueId = getValueId;
|
|
7506
7595
|
exports.includeInTotalExtraAmount = includeInTotalExtraAmount;
|
|
7596
|
+
exports.isDTaoClaimableLock = isDTaoClaimableLock;
|
|
7507
7597
|
exports.isEqualBalanceArrays = isEqualBalanceArrays;
|
|
7508
7598
|
exports.isEqualBalancesResult = isEqualBalancesResult;
|
|
7509
7599
|
exports.isEqualMiniMetadatas = isEqualMiniMetadatas;
|