@talismn/balances 1.3.0 → 1.3.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.
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { GetDynamicInfosResult, SubDTaoBalance } from "./types";
|
|
2
2
|
type DynamicInfo = NonNullable<GetDynamicInfosResult[number]>;
|
|
3
|
-
export declare const calculatePendingRootClaimable: ({ stake, hotkey, address, networkId, validatorRootClaimableRate, dynamicInfoByNetuid, }: {
|
|
3
|
+
export declare const calculatePendingRootClaimable: ({ stake, hotkey, address, networkId, validatorRootClaimableRate, dynamicInfoByNetuid, alreadyClaimedByNetuid, }: {
|
|
4
4
|
stake: bigint;
|
|
5
5
|
hotkey: string;
|
|
6
6
|
address: string;
|
|
7
7
|
networkId: string;
|
|
8
8
|
validatorRootClaimableRate: Map<number, bigint>;
|
|
9
9
|
dynamicInfoByNetuid: Record<number, DynamicInfo | undefined>;
|
|
10
|
+
alreadyClaimedByNetuid: Map<number, bigint>;
|
|
10
11
|
}) => SubDTaoBalance[];
|
|
11
12
|
export {};
|
|
@@ -2301,7 +2301,8 @@ const calculatePendingRootClaimable = ({
|
|
|
2301
2301
|
address,
|
|
2302
2302
|
networkId,
|
|
2303
2303
|
validatorRootClaimableRate,
|
|
2304
|
-
dynamicInfoByNetuid
|
|
2304
|
+
dynamicInfoByNetuid,
|
|
2305
|
+
alreadyClaimedByNetuid
|
|
2305
2306
|
}) => {
|
|
2306
2307
|
const pendingRootClaimBalances = [];
|
|
2307
2308
|
for (const [netuid, claimableRate] of validatorRootClaimableRate) {
|
|
@@ -2315,7 +2316,11 @@ const calculatePendingRootClaimable = ({
|
|
|
2315
2316
|
|
|
2316
2317
|
// Multiply claimable_rate by root_stake
|
|
2317
2318
|
// I96F32 multiplication: round((a * b) / 2^32)
|
|
2318
|
-
const
|
|
2319
|
+
const totalClaimable = stake * claimableRate + (1n << 31n) >> 32n;
|
|
2320
|
+
|
|
2321
|
+
// Subtract already claimed amount to get net pending claimable
|
|
2322
|
+
const alreadyClaimed = alreadyClaimedByNetuid.get(netuid) ?? 0n;
|
|
2323
|
+
const pendingRootClaim = totalClaimable > alreadyClaimed ? totalClaimable - alreadyClaimed : 0n;
|
|
2319
2324
|
pendingRootClaimBalances.push({
|
|
2320
2325
|
address,
|
|
2321
2326
|
tokenId: chaindataProvider.subDTaoTokenId(networkId, netuid, hotkey),
|
|
@@ -2382,6 +2387,23 @@ const fetchBalances$5 = async ({
|
|
|
2382
2387
|
const [stakeInfos, dynamicInfos] = await Promise.all([fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "StakeInfoRuntimeApi", "get_stake_info_for_coldkeys", [addresses]), fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "SubnetInfoRuntimeApi", "get_all_dynamic_info", [])]);
|
|
2383
2388
|
const rootHotkeys = lodashEs.uniq(stakeInfos.flatMap(([, stakes]) => stakes.filter(stake => stake.netuid === ROOT_NETUID).map(stake => stake.hotkey)));
|
|
2384
2389
|
const rootClaimableRatesByHotkey = rootHotkeys.length && miniMetadata.data ? await fetchRootClaimableRates(connector, networkId, miniMetadata.data, rootHotkeys) : new Map();
|
|
2390
|
+
|
|
2391
|
+
// Collect all (address, hotkey, netuid) pairs for root stakes to fetch RootClaimed amounts
|
|
2392
|
+
const addressHotkeyNetuidPairs = [];
|
|
2393
|
+
for (const [address, stakes] of stakeInfos) {
|
|
2394
|
+
for (const stake of stakes) {
|
|
2395
|
+
if (stake.netuid === ROOT_NETUID) {
|
|
2396
|
+
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey);
|
|
2397
|
+
if (claimableRates) {
|
|
2398
|
+
// For each netuid that has a claimable rate, we need to check RootClaimed
|
|
2399
|
+
for (const netuid of claimableRates.keys()) {
|
|
2400
|
+
addressHotkeyNetuidPairs.push([address, stake.hotkey, netuid]);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
const rootClaimedAmounts = addressHotkeyNetuidPairs.length && miniMetadata.data ? await fetchRootClaimedAmounts(connector, networkId, miniMetadata.data, addressHotkeyNetuidPairs) : new Map();
|
|
2385
2407
|
const dynamicInfoByNetuid = lodashEs.keyBy(dynamicInfos.filter(util.isNotNil), info => info.netuid);
|
|
2386
2408
|
|
|
2387
2409
|
// Upserts a balance into the accumulator, merging stake values if the balance already exists.
|
|
@@ -2421,13 +2443,16 @@ const fetchBalances$5 = async ({
|
|
|
2421
2443
|
|
|
2422
2444
|
// Root stake cases, we need to calculate the pending root claim and add to the balances
|
|
2423
2445
|
if (stake.netuid === ROOT_NETUID) {
|
|
2446
|
+
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey) ?? new Map();
|
|
2447
|
+
const alreadyClaimedMap = rootClaimedAmounts.get(address)?.get(stake.hotkey) ?? new Map();
|
|
2424
2448
|
const pendingRootClaimBalances = calculatePendingRootClaimable({
|
|
2425
2449
|
stake: stake.stake,
|
|
2426
2450
|
hotkey: stake.hotkey,
|
|
2427
2451
|
address,
|
|
2428
2452
|
networkId,
|
|
2429
|
-
validatorRootClaimableRate:
|
|
2430
|
-
dynamicInfoByNetuid
|
|
2453
|
+
validatorRootClaimableRate: claimableRates,
|
|
2454
|
+
dynamicInfoByNetuid,
|
|
2455
|
+
alreadyClaimedByNetuid: alreadyClaimedMap
|
|
2431
2456
|
});
|
|
2432
2457
|
pendingRootClaimBalances.forEach(balance => {
|
|
2433
2458
|
upsertBalance(acc, address, balance.tokenId, balance);
|
|
@@ -2542,6 +2567,19 @@ const buildRootClaimableStorageCoder = async (connector, networkId, metadataRpc)
|
|
|
2542
2567
|
}
|
|
2543
2568
|
return storageCoder;
|
|
2544
2569
|
};
|
|
2570
|
+
const buildRootClaimedStorageCoder = async (networkId, metadataRpc) => {
|
|
2571
|
+
let storageCoder = null;
|
|
2572
|
+
if (metadataRpc) {
|
|
2573
|
+
try {
|
|
2574
|
+
storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimed");
|
|
2575
|
+
} catch (cause) {
|
|
2576
|
+
log.warn(`Failed to build storage coder for SubtensorModule.RootClaimed using provided metadata on ${networkId}`, {
|
|
2577
|
+
cause
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
return storageCoder;
|
|
2582
|
+
};
|
|
2545
2583
|
const buildRootClaimableQueries = (networkId, hotkeys, storageCoder) => {
|
|
2546
2584
|
return hotkeys.map(hotkey => {
|
|
2547
2585
|
let stateKey = null;
|
|
@@ -2585,6 +2623,74 @@ const fetchRootClaimableRates = async (connector, networkId, metadataRpc, hotkey
|
|
|
2585
2623
|
return new Map(hotkeys.map(hotkey => [hotkey, new Map()]));
|
|
2586
2624
|
}
|
|
2587
2625
|
};
|
|
2626
|
+
const buildRootClaimedQueries = (networkId, addressHotkeyNetuidPairs, storageCoder) => {
|
|
2627
|
+
return addressHotkeyNetuidPairs.map(([address, hotkey, netuid]) => {
|
|
2628
|
+
let stateKey = null;
|
|
2629
|
+
try {
|
|
2630
|
+
// RootClaimed storage takes params: [netuid, hotkey, coldkey_ss58]
|
|
2631
|
+
stateKey = storageCoder.keys.enc(netuid, hotkey, address);
|
|
2632
|
+
} catch (cause) {
|
|
2633
|
+
log.warn(`Failed to encode storage key for RootClaimed (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`, {
|
|
2634
|
+
cause
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
const decodeResult = changes => {
|
|
2638
|
+
const hexValue = changes[0];
|
|
2639
|
+
if (!hexValue) {
|
|
2640
|
+
return [address, hotkey, netuid, 0n];
|
|
2641
|
+
}
|
|
2642
|
+
const decoded = scale.decodeScale(storageCoder, hexValue, `Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
|
|
2643
|
+
return [address, hotkey, netuid, decoded ?? 0n];
|
|
2644
|
+
};
|
|
2645
|
+
return {
|
|
2646
|
+
stateKeys: [stateKey],
|
|
2647
|
+
decodeResult
|
|
2648
|
+
};
|
|
2649
|
+
});
|
|
2650
|
+
};
|
|
2651
|
+
const fetchRootClaimedAmounts = async (connector, networkId, metadataRpc, addressHotkeyNetuidPairs) => {
|
|
2652
|
+
if (!addressHotkeyNetuidPairs.length) {
|
|
2653
|
+
return new Map();
|
|
2654
|
+
}
|
|
2655
|
+
const storageCoder = await buildRootClaimedStorageCoder(networkId, metadataRpc);
|
|
2656
|
+
if (!storageCoder) {
|
|
2657
|
+
// Fallback: return empty map for all pairs
|
|
2658
|
+
const result = new Map();
|
|
2659
|
+
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
2660
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2661
|
+
const addressMap = result.get(address);
|
|
2662
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2663
|
+
addressMap.get(hotkey).set(netuid, 0n);
|
|
2664
|
+
}
|
|
2665
|
+
return result;
|
|
2666
|
+
}
|
|
2667
|
+
const queries = buildRootClaimedQueries(networkId, addressHotkeyNetuidPairs, storageCoder);
|
|
2668
|
+
try {
|
|
2669
|
+
const results = await fetchRpcQueryPack(connector, networkId, queries);
|
|
2670
|
+
// Build a nested map: address -> hotkey -> netuid -> claimed amount
|
|
2671
|
+
const result = new Map();
|
|
2672
|
+
for (const [address, hotkey, netuid, claimed] of results) {
|
|
2673
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2674
|
+
const addressMap = result.get(address);
|
|
2675
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2676
|
+
addressMap.get(hotkey).set(netuid, claimed);
|
|
2677
|
+
}
|
|
2678
|
+
return result;
|
|
2679
|
+
} catch (cause) {
|
|
2680
|
+
log.warn(`Failed to fetch RootClaimed for address-hotkey-netuid pairs on ${networkId}`, {
|
|
2681
|
+
cause
|
|
2682
|
+
});
|
|
2683
|
+
// Fallback: return empty map for all pairs
|
|
2684
|
+
const result = new Map();
|
|
2685
|
+
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
2686
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2687
|
+
const addressMap = result.get(address);
|
|
2688
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2689
|
+
addressMap.get(hotkey).set(netuid, 0n);
|
|
2690
|
+
}
|
|
2691
|
+
return result;
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2588
2694
|
|
|
2589
2695
|
// hardcoded because we dont have access to native tokens from the balance module
|
|
2590
2696
|
const NATIVE_TOKEN_SYMBOLS = {
|
|
@@ -2684,7 +2790,7 @@ const getData$4 = metadataRpc => {
|
|
|
2684
2790
|
if (!isBittensor) return null;
|
|
2685
2791
|
scale.compactMetadata(metadata, [{
|
|
2686
2792
|
pallet: "SubtensorModule",
|
|
2687
|
-
items: ["TransferToggle", "RootClaimable"]
|
|
2793
|
+
items: ["TransferToggle", "RootClaimable", "RootClaimed"]
|
|
2688
2794
|
}], [{
|
|
2689
2795
|
runtimeApi: "StakeInfoRuntimeApi",
|
|
2690
2796
|
methods: ["get_stake_info_for_coldkeys"]
|
|
@@ -2301,7 +2301,8 @@ const calculatePendingRootClaimable = ({
|
|
|
2301
2301
|
address,
|
|
2302
2302
|
networkId,
|
|
2303
2303
|
validatorRootClaimableRate,
|
|
2304
|
-
dynamicInfoByNetuid
|
|
2304
|
+
dynamicInfoByNetuid,
|
|
2305
|
+
alreadyClaimedByNetuid
|
|
2305
2306
|
}) => {
|
|
2306
2307
|
const pendingRootClaimBalances = [];
|
|
2307
2308
|
for (const [netuid, claimableRate] of validatorRootClaimableRate) {
|
|
@@ -2315,7 +2316,11 @@ const calculatePendingRootClaimable = ({
|
|
|
2315
2316
|
|
|
2316
2317
|
// Multiply claimable_rate by root_stake
|
|
2317
2318
|
// I96F32 multiplication: round((a * b) / 2^32)
|
|
2318
|
-
const
|
|
2319
|
+
const totalClaimable = stake * claimableRate + (1n << 31n) >> 32n;
|
|
2320
|
+
|
|
2321
|
+
// Subtract already claimed amount to get net pending claimable
|
|
2322
|
+
const alreadyClaimed = alreadyClaimedByNetuid.get(netuid) ?? 0n;
|
|
2323
|
+
const pendingRootClaim = totalClaimable > alreadyClaimed ? totalClaimable - alreadyClaimed : 0n;
|
|
2319
2324
|
pendingRootClaimBalances.push({
|
|
2320
2325
|
address,
|
|
2321
2326
|
tokenId: chaindataProvider.subDTaoTokenId(networkId, netuid, hotkey),
|
|
@@ -2382,6 +2387,23 @@ const fetchBalances$5 = async ({
|
|
|
2382
2387
|
const [stakeInfos, dynamicInfos] = await Promise.all([fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "StakeInfoRuntimeApi", "get_stake_info_for_coldkeys", [addresses]), fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "SubnetInfoRuntimeApi", "get_all_dynamic_info", [])]);
|
|
2383
2388
|
const rootHotkeys = lodashEs.uniq(stakeInfos.flatMap(([, stakes]) => stakes.filter(stake => stake.netuid === ROOT_NETUID).map(stake => stake.hotkey)));
|
|
2384
2389
|
const rootClaimableRatesByHotkey = rootHotkeys.length && miniMetadata.data ? await fetchRootClaimableRates(connector, networkId, miniMetadata.data, rootHotkeys) : new Map();
|
|
2390
|
+
|
|
2391
|
+
// Collect all (address, hotkey, netuid) pairs for root stakes to fetch RootClaimed amounts
|
|
2392
|
+
const addressHotkeyNetuidPairs = [];
|
|
2393
|
+
for (const [address, stakes] of stakeInfos) {
|
|
2394
|
+
for (const stake of stakes) {
|
|
2395
|
+
if (stake.netuid === ROOT_NETUID) {
|
|
2396
|
+
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey);
|
|
2397
|
+
if (claimableRates) {
|
|
2398
|
+
// For each netuid that has a claimable rate, we need to check RootClaimed
|
|
2399
|
+
for (const netuid of claimableRates.keys()) {
|
|
2400
|
+
addressHotkeyNetuidPairs.push([address, stake.hotkey, netuid]);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
const rootClaimedAmounts = addressHotkeyNetuidPairs.length && miniMetadata.data ? await fetchRootClaimedAmounts(connector, networkId, miniMetadata.data, addressHotkeyNetuidPairs) : new Map();
|
|
2385
2407
|
const dynamicInfoByNetuid = lodashEs.keyBy(dynamicInfos.filter(util.isNotNil), info => info.netuid);
|
|
2386
2408
|
|
|
2387
2409
|
// Upserts a balance into the accumulator, merging stake values if the balance already exists.
|
|
@@ -2421,13 +2443,16 @@ const fetchBalances$5 = async ({
|
|
|
2421
2443
|
|
|
2422
2444
|
// Root stake cases, we need to calculate the pending root claim and add to the balances
|
|
2423
2445
|
if (stake.netuid === ROOT_NETUID) {
|
|
2446
|
+
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey) ?? new Map();
|
|
2447
|
+
const alreadyClaimedMap = rootClaimedAmounts.get(address)?.get(stake.hotkey) ?? new Map();
|
|
2424
2448
|
const pendingRootClaimBalances = calculatePendingRootClaimable({
|
|
2425
2449
|
stake: stake.stake,
|
|
2426
2450
|
hotkey: stake.hotkey,
|
|
2427
2451
|
address,
|
|
2428
2452
|
networkId,
|
|
2429
|
-
validatorRootClaimableRate:
|
|
2430
|
-
dynamicInfoByNetuid
|
|
2453
|
+
validatorRootClaimableRate: claimableRates,
|
|
2454
|
+
dynamicInfoByNetuid,
|
|
2455
|
+
alreadyClaimedByNetuid: alreadyClaimedMap
|
|
2431
2456
|
});
|
|
2432
2457
|
pendingRootClaimBalances.forEach(balance => {
|
|
2433
2458
|
upsertBalance(acc, address, balance.tokenId, balance);
|
|
@@ -2542,6 +2567,19 @@ const buildRootClaimableStorageCoder = async (connector, networkId, metadataRpc)
|
|
|
2542
2567
|
}
|
|
2543
2568
|
return storageCoder;
|
|
2544
2569
|
};
|
|
2570
|
+
const buildRootClaimedStorageCoder = async (networkId, metadataRpc) => {
|
|
2571
|
+
let storageCoder = null;
|
|
2572
|
+
if (metadataRpc) {
|
|
2573
|
+
try {
|
|
2574
|
+
storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimed");
|
|
2575
|
+
} catch (cause) {
|
|
2576
|
+
log.warn(`Failed to build storage coder for SubtensorModule.RootClaimed using provided metadata on ${networkId}`, {
|
|
2577
|
+
cause
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
return storageCoder;
|
|
2582
|
+
};
|
|
2545
2583
|
const buildRootClaimableQueries = (networkId, hotkeys, storageCoder) => {
|
|
2546
2584
|
return hotkeys.map(hotkey => {
|
|
2547
2585
|
let stateKey = null;
|
|
@@ -2585,6 +2623,74 @@ const fetchRootClaimableRates = async (connector, networkId, metadataRpc, hotkey
|
|
|
2585
2623
|
return new Map(hotkeys.map(hotkey => [hotkey, new Map()]));
|
|
2586
2624
|
}
|
|
2587
2625
|
};
|
|
2626
|
+
const buildRootClaimedQueries = (networkId, addressHotkeyNetuidPairs, storageCoder) => {
|
|
2627
|
+
return addressHotkeyNetuidPairs.map(([address, hotkey, netuid]) => {
|
|
2628
|
+
let stateKey = null;
|
|
2629
|
+
try {
|
|
2630
|
+
// RootClaimed storage takes params: [netuid, hotkey, coldkey_ss58]
|
|
2631
|
+
stateKey = storageCoder.keys.enc(netuid, hotkey, address);
|
|
2632
|
+
} catch (cause) {
|
|
2633
|
+
log.warn(`Failed to encode storage key for RootClaimed (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`, {
|
|
2634
|
+
cause
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
const decodeResult = changes => {
|
|
2638
|
+
const hexValue = changes[0];
|
|
2639
|
+
if (!hexValue) {
|
|
2640
|
+
return [address, hotkey, netuid, 0n];
|
|
2641
|
+
}
|
|
2642
|
+
const decoded = scale.decodeScale(storageCoder, hexValue, `Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
|
|
2643
|
+
return [address, hotkey, netuid, decoded ?? 0n];
|
|
2644
|
+
};
|
|
2645
|
+
return {
|
|
2646
|
+
stateKeys: [stateKey],
|
|
2647
|
+
decodeResult
|
|
2648
|
+
};
|
|
2649
|
+
});
|
|
2650
|
+
};
|
|
2651
|
+
const fetchRootClaimedAmounts = async (connector, networkId, metadataRpc, addressHotkeyNetuidPairs) => {
|
|
2652
|
+
if (!addressHotkeyNetuidPairs.length) {
|
|
2653
|
+
return new Map();
|
|
2654
|
+
}
|
|
2655
|
+
const storageCoder = await buildRootClaimedStorageCoder(networkId, metadataRpc);
|
|
2656
|
+
if (!storageCoder) {
|
|
2657
|
+
// Fallback: return empty map for all pairs
|
|
2658
|
+
const result = new Map();
|
|
2659
|
+
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
2660
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2661
|
+
const addressMap = result.get(address);
|
|
2662
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2663
|
+
addressMap.get(hotkey).set(netuid, 0n);
|
|
2664
|
+
}
|
|
2665
|
+
return result;
|
|
2666
|
+
}
|
|
2667
|
+
const queries = buildRootClaimedQueries(networkId, addressHotkeyNetuidPairs, storageCoder);
|
|
2668
|
+
try {
|
|
2669
|
+
const results = await fetchRpcQueryPack(connector, networkId, queries);
|
|
2670
|
+
// Build a nested map: address -> hotkey -> netuid -> claimed amount
|
|
2671
|
+
const result = new Map();
|
|
2672
|
+
for (const [address, hotkey, netuid, claimed] of results) {
|
|
2673
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2674
|
+
const addressMap = result.get(address);
|
|
2675
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2676
|
+
addressMap.get(hotkey).set(netuid, claimed);
|
|
2677
|
+
}
|
|
2678
|
+
return result;
|
|
2679
|
+
} catch (cause) {
|
|
2680
|
+
log.warn(`Failed to fetch RootClaimed for address-hotkey-netuid pairs on ${networkId}`, {
|
|
2681
|
+
cause
|
|
2682
|
+
});
|
|
2683
|
+
// Fallback: return empty map for all pairs
|
|
2684
|
+
const result = new Map();
|
|
2685
|
+
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
2686
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2687
|
+
const addressMap = result.get(address);
|
|
2688
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2689
|
+
addressMap.get(hotkey).set(netuid, 0n);
|
|
2690
|
+
}
|
|
2691
|
+
return result;
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2588
2694
|
|
|
2589
2695
|
// hardcoded because we dont have access to native tokens from the balance module
|
|
2590
2696
|
const NATIVE_TOKEN_SYMBOLS = {
|
|
@@ -2684,7 +2790,7 @@ const getData$4 = metadataRpc => {
|
|
|
2684
2790
|
if (!isBittensor) return null;
|
|
2685
2791
|
scale.compactMetadata(metadata, [{
|
|
2686
2792
|
pallet: "SubtensorModule",
|
|
2687
|
-
items: ["TransferToggle", "RootClaimable"]
|
|
2793
|
+
items: ["TransferToggle", "RootClaimable", "RootClaimed"]
|
|
2688
2794
|
}], [{
|
|
2689
2795
|
runtimeApi: "StakeInfoRuntimeApi",
|
|
2690
2796
|
methods: ["get_stake_info_for_coldkeys"]
|
|
@@ -2292,7 +2292,8 @@ const calculatePendingRootClaimable = ({
|
|
|
2292
2292
|
address,
|
|
2293
2293
|
networkId,
|
|
2294
2294
|
validatorRootClaimableRate,
|
|
2295
|
-
dynamicInfoByNetuid
|
|
2295
|
+
dynamicInfoByNetuid,
|
|
2296
|
+
alreadyClaimedByNetuid
|
|
2296
2297
|
}) => {
|
|
2297
2298
|
const pendingRootClaimBalances = [];
|
|
2298
2299
|
for (const [netuid, claimableRate] of validatorRootClaimableRate) {
|
|
@@ -2306,7 +2307,11 @@ const calculatePendingRootClaimable = ({
|
|
|
2306
2307
|
|
|
2307
2308
|
// Multiply claimable_rate by root_stake
|
|
2308
2309
|
// I96F32 multiplication: round((a * b) / 2^32)
|
|
2309
|
-
const
|
|
2310
|
+
const totalClaimable = stake * claimableRate + (1n << 31n) >> 32n;
|
|
2311
|
+
|
|
2312
|
+
// Subtract already claimed amount to get net pending claimable
|
|
2313
|
+
const alreadyClaimed = alreadyClaimedByNetuid.get(netuid) ?? 0n;
|
|
2314
|
+
const pendingRootClaim = totalClaimable > alreadyClaimed ? totalClaimable - alreadyClaimed : 0n;
|
|
2310
2315
|
pendingRootClaimBalances.push({
|
|
2311
2316
|
address,
|
|
2312
2317
|
tokenId: subDTaoTokenId(networkId, netuid, hotkey),
|
|
@@ -2373,6 +2378,23 @@ const fetchBalances$5 = async ({
|
|
|
2373
2378
|
const [stakeInfos, dynamicInfos] = await Promise.all([fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "StakeInfoRuntimeApi", "get_stake_info_for_coldkeys", [addresses]), fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "SubnetInfoRuntimeApi", "get_all_dynamic_info", [])]);
|
|
2374
2379
|
const rootHotkeys = uniq(stakeInfos.flatMap(([, stakes]) => stakes.filter(stake => stake.netuid === ROOT_NETUID).map(stake => stake.hotkey)));
|
|
2375
2380
|
const rootClaimableRatesByHotkey = rootHotkeys.length && miniMetadata.data ? await fetchRootClaimableRates(connector, networkId, miniMetadata.data, rootHotkeys) : new Map();
|
|
2381
|
+
|
|
2382
|
+
// Collect all (address, hotkey, netuid) pairs for root stakes to fetch RootClaimed amounts
|
|
2383
|
+
const addressHotkeyNetuidPairs = [];
|
|
2384
|
+
for (const [address, stakes] of stakeInfos) {
|
|
2385
|
+
for (const stake of stakes) {
|
|
2386
|
+
if (stake.netuid === ROOT_NETUID) {
|
|
2387
|
+
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey);
|
|
2388
|
+
if (claimableRates) {
|
|
2389
|
+
// For each netuid that has a claimable rate, we need to check RootClaimed
|
|
2390
|
+
for (const netuid of claimableRates.keys()) {
|
|
2391
|
+
addressHotkeyNetuidPairs.push([address, stake.hotkey, netuid]);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
const rootClaimedAmounts = addressHotkeyNetuidPairs.length && miniMetadata.data ? await fetchRootClaimedAmounts(connector, networkId, miniMetadata.data, addressHotkeyNetuidPairs) : new Map();
|
|
2376
2398
|
const dynamicInfoByNetuid = keyBy(dynamicInfos.filter(isNotNil), info => info.netuid);
|
|
2377
2399
|
|
|
2378
2400
|
// Upserts a balance into the accumulator, merging stake values if the balance already exists.
|
|
@@ -2412,13 +2434,16 @@ const fetchBalances$5 = async ({
|
|
|
2412
2434
|
|
|
2413
2435
|
// Root stake cases, we need to calculate the pending root claim and add to the balances
|
|
2414
2436
|
if (stake.netuid === ROOT_NETUID) {
|
|
2437
|
+
const claimableRates = rootClaimableRatesByHotkey.get(stake.hotkey) ?? new Map();
|
|
2438
|
+
const alreadyClaimedMap = rootClaimedAmounts.get(address)?.get(stake.hotkey) ?? new Map();
|
|
2415
2439
|
const pendingRootClaimBalances = calculatePendingRootClaimable({
|
|
2416
2440
|
stake: stake.stake,
|
|
2417
2441
|
hotkey: stake.hotkey,
|
|
2418
2442
|
address,
|
|
2419
2443
|
networkId,
|
|
2420
|
-
validatorRootClaimableRate:
|
|
2421
|
-
dynamicInfoByNetuid
|
|
2444
|
+
validatorRootClaimableRate: claimableRates,
|
|
2445
|
+
dynamicInfoByNetuid,
|
|
2446
|
+
alreadyClaimedByNetuid: alreadyClaimedMap
|
|
2422
2447
|
});
|
|
2423
2448
|
pendingRootClaimBalances.forEach(balance => {
|
|
2424
2449
|
upsertBalance(acc, address, balance.tokenId, balance);
|
|
@@ -2533,6 +2558,19 @@ const buildRootClaimableStorageCoder = async (connector, networkId, metadataRpc)
|
|
|
2533
2558
|
}
|
|
2534
2559
|
return storageCoder;
|
|
2535
2560
|
};
|
|
2561
|
+
const buildRootClaimedStorageCoder = async (networkId, metadataRpc) => {
|
|
2562
|
+
let storageCoder = null;
|
|
2563
|
+
if (metadataRpc) {
|
|
2564
|
+
try {
|
|
2565
|
+
storageCoder = buildStorageCoder(metadataRpc, "SubtensorModule", "RootClaimed");
|
|
2566
|
+
} catch (cause) {
|
|
2567
|
+
log.warn(`Failed to build storage coder for SubtensorModule.RootClaimed using provided metadata on ${networkId}`, {
|
|
2568
|
+
cause
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
return storageCoder;
|
|
2573
|
+
};
|
|
2536
2574
|
const buildRootClaimableQueries = (networkId, hotkeys, storageCoder) => {
|
|
2537
2575
|
return hotkeys.map(hotkey => {
|
|
2538
2576
|
let stateKey = null;
|
|
@@ -2576,6 +2614,74 @@ const fetchRootClaimableRates = async (connector, networkId, metadataRpc, hotkey
|
|
|
2576
2614
|
return new Map(hotkeys.map(hotkey => [hotkey, new Map()]));
|
|
2577
2615
|
}
|
|
2578
2616
|
};
|
|
2617
|
+
const buildRootClaimedQueries = (networkId, addressHotkeyNetuidPairs, storageCoder) => {
|
|
2618
|
+
return addressHotkeyNetuidPairs.map(([address, hotkey, netuid]) => {
|
|
2619
|
+
let stateKey = null;
|
|
2620
|
+
try {
|
|
2621
|
+
// RootClaimed storage takes params: [netuid, hotkey, coldkey_ss58]
|
|
2622
|
+
stateKey = storageCoder.keys.enc(netuid, hotkey, address);
|
|
2623
|
+
} catch (cause) {
|
|
2624
|
+
log.warn(`Failed to encode storage key for RootClaimed (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`, {
|
|
2625
|
+
cause
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
const decodeResult = changes => {
|
|
2629
|
+
const hexValue = changes[0];
|
|
2630
|
+
if (!hexValue) {
|
|
2631
|
+
return [address, hotkey, netuid, 0n];
|
|
2632
|
+
}
|
|
2633
|
+
const decoded = decodeScale(storageCoder, hexValue, `Failed to decode RootClaimed for (netuid=${netuid}, hotkey=${hotkey}, address=${address}) on ${networkId}`);
|
|
2634
|
+
return [address, hotkey, netuid, decoded ?? 0n];
|
|
2635
|
+
};
|
|
2636
|
+
return {
|
|
2637
|
+
stateKeys: [stateKey],
|
|
2638
|
+
decodeResult
|
|
2639
|
+
};
|
|
2640
|
+
});
|
|
2641
|
+
};
|
|
2642
|
+
const fetchRootClaimedAmounts = async (connector, networkId, metadataRpc, addressHotkeyNetuidPairs) => {
|
|
2643
|
+
if (!addressHotkeyNetuidPairs.length) {
|
|
2644
|
+
return new Map();
|
|
2645
|
+
}
|
|
2646
|
+
const storageCoder = await buildRootClaimedStorageCoder(networkId, metadataRpc);
|
|
2647
|
+
if (!storageCoder) {
|
|
2648
|
+
// Fallback: return empty map for all pairs
|
|
2649
|
+
const result = new Map();
|
|
2650
|
+
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
2651
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2652
|
+
const addressMap = result.get(address);
|
|
2653
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2654
|
+
addressMap.get(hotkey).set(netuid, 0n);
|
|
2655
|
+
}
|
|
2656
|
+
return result;
|
|
2657
|
+
}
|
|
2658
|
+
const queries = buildRootClaimedQueries(networkId, addressHotkeyNetuidPairs, storageCoder);
|
|
2659
|
+
try {
|
|
2660
|
+
const results = await fetchRpcQueryPack(connector, networkId, queries);
|
|
2661
|
+
// Build a nested map: address -> hotkey -> netuid -> claimed amount
|
|
2662
|
+
const result = new Map();
|
|
2663
|
+
for (const [address, hotkey, netuid, claimed] of results) {
|
|
2664
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2665
|
+
const addressMap = result.get(address);
|
|
2666
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2667
|
+
addressMap.get(hotkey).set(netuid, claimed);
|
|
2668
|
+
}
|
|
2669
|
+
return result;
|
|
2670
|
+
} catch (cause) {
|
|
2671
|
+
log.warn(`Failed to fetch RootClaimed for address-hotkey-netuid pairs on ${networkId}`, {
|
|
2672
|
+
cause
|
|
2673
|
+
});
|
|
2674
|
+
// Fallback: return empty map for all pairs
|
|
2675
|
+
const result = new Map();
|
|
2676
|
+
for (const [address, hotkey, netuid] of addressHotkeyNetuidPairs) {
|
|
2677
|
+
if (!result.has(address)) result.set(address, new Map());
|
|
2678
|
+
const addressMap = result.get(address);
|
|
2679
|
+
if (!addressMap.has(hotkey)) addressMap.set(hotkey, new Map());
|
|
2680
|
+
addressMap.get(hotkey).set(netuid, 0n);
|
|
2681
|
+
}
|
|
2682
|
+
return result;
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2579
2685
|
|
|
2580
2686
|
// hardcoded because we dont have access to native tokens from the balance module
|
|
2581
2687
|
const NATIVE_TOKEN_SYMBOLS = {
|
|
@@ -2675,7 +2781,7 @@ const getData$4 = metadataRpc => {
|
|
|
2675
2781
|
if (!isBittensor) return null;
|
|
2676
2782
|
compactMetadata(metadata, [{
|
|
2677
2783
|
pallet: "SubtensorModule",
|
|
2678
|
-
items: ["TransferToggle", "RootClaimable"]
|
|
2784
|
+
items: ["TransferToggle", "RootClaimable", "RootClaimed"]
|
|
2679
2785
|
}], [{
|
|
2680
2786
|
runtimeApi: "StakeInfoRuntimeApi",
|
|
2681
2787
|
methods: ["get_stake_info_for_coldkeys"]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talismn/balances",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"author": "Talisman",
|
|
5
5
|
"homepage": "https://talisman.xyz",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"scale-ts": "^1.6.1",
|
|
39
39
|
"viem": "^2.27.3",
|
|
40
40
|
"zod": "^3.25.76",
|
|
41
|
-
"@talismn/
|
|
42
|
-
"@talismn/sapi": "0.1.0",
|
|
43
|
-
"@talismn/chaindata-provider": "1.3.3",
|
|
41
|
+
"@talismn/chaindata-provider": "1.3.4",
|
|
44
42
|
"@talismn/crypto": "0.3.0",
|
|
43
|
+
"@talismn/chain-connectors": "0.0.13",
|
|
44
|
+
"@talismn/sapi": "0.1.0",
|
|
45
45
|
"@talismn/scale": "0.3.0",
|
|
46
|
-
"@talismn/token-rates": "3.0.14",
|
|
47
46
|
"@talismn/solana": "0.0.5",
|
|
47
|
+
"@talismn/token-rates": "3.0.15",
|
|
48
48
|
"@talismn/util": "0.5.6"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"jest": "^29.7.0",
|
|
61
61
|
"ts-jest": "^29.2.5",
|
|
62
62
|
"typescript": "^5.6.3",
|
|
63
|
-
"@talismn/
|
|
64
|
-
"@talismn/
|
|
63
|
+
"@talismn/eslint-config": "0.0.3",
|
|
64
|
+
"@talismn/tsconfig": "0.0.3"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"@polkadot/api-contract": "*",
|