@talismn/balances 0.0.0-pr2043-20250626090303 → 0.0.0-pr2043-20250626133240
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/talismn-balances.cjs.dev.js +496 -483
- package/dist/talismn-balances.cjs.prod.js +496 -483
- package/dist/talismn-balances.esm.js +496 -483
- package/package.json +7 -7
@@ -126,7 +126,7 @@ class EvmTokenFetcher {
|
|
126
126
|
|
127
127
|
var pkg = {
|
128
128
|
name: "@talismn/balances",
|
129
|
-
version: "0.0.0-pr2043-
|
129
|
+
version: "0.0.0-pr2043-20250626133240"};
|
130
130
|
|
131
131
|
var log = anylogger__default.default(pkg.name);
|
132
132
|
|
@@ -3659,11 +3659,18 @@ const getAddresssesByTokenByNetwork = addressesByToken => {
|
|
3659
3659
|
};
|
3660
3660
|
|
3661
3661
|
async function subscribeBase(queries, chainConnector, callback) {
|
3662
|
-
|
3663
|
-
|
3664
|
-
|
3665
|
-
|
3666
|
-
|
3662
|
+
try {
|
3663
|
+
const unsubscribe = await new RpcStateQueryHelper(chainConnector, queries).subscribe((error, result) => {
|
3664
|
+
if (error) callback(error);
|
3665
|
+
if (result && result.length > 0) callback(null, result);
|
3666
|
+
});
|
3667
|
+
return unsubscribe;
|
3668
|
+
} catch (err) {
|
3669
|
+
if (!util.isAbortError(err)) log.error("Error subscribing to base queries", {
|
3670
|
+
err
|
3671
|
+
});
|
3672
|
+
return () => {};
|
3673
|
+
}
|
3667
3674
|
}
|
3668
3675
|
|
3669
3676
|
/**
|
@@ -3700,284 +3707,291 @@ const nompoolStashAccountId = (palletId, poolId) => nompoolAccountId(palletId, p
|
|
3700
3707
|
|
3701
3708
|
// TODO make this method chain-specific
|
3702
3709
|
async function subscribeNompoolStaking(chaindataProvider$1, chainConnector, addressesByToken, callback, signal) {
|
3703
|
-
|
3704
|
-
|
3705
|
-
|
3706
|
-
|
3707
|
-
|
3708
|
-
|
3709
|
-
|
3710
|
-
|
3711
|
-
|
3712
|
-
|
3713
|
-
signal?.throwIfAborted();
|
3714
|
-
const nomPoolTokenIds = Object.entries(tokens).filter(([, token]) => {
|
3715
|
-
// ignore non-native tokens
|
3716
|
-
if (token.type !== "substrate-native") return false;
|
3717
|
-
|
3718
|
-
// ignore tokens on chains with no nompools pallet
|
3719
|
-
const miniMetadata = miniMetadatas.get(token.networkId);
|
3720
|
-
return typeof miniMetadata?.extra?.nominationPoolsPalletId === "string";
|
3721
|
-
}).map(([tokenId]) => tokenId);
|
3722
|
-
|
3723
|
-
// staking can only be done by the native token on chains with the staking pallet
|
3724
|
-
const addressesByNomPoolToken = Object.fromEntries(Object.entries(addressesByToken)
|
3725
|
-
// remove ethereum addresses
|
3726
|
-
.map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !util.isEthereumAddress(address))])
|
3727
|
-
// remove tokens which aren't nom pool tokens
|
3728
|
-
.filter(([tokenId]) => nomPoolTokenIds.includes(tokenId)));
|
3729
|
-
const uniqueChainIds = getUniqueChainIds(addressesByNomPoolToken, tokens);
|
3730
|
-
const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
|
3731
|
-
const chainStorageCoders = buildStorageCoders({
|
3732
|
-
chainIds: uniqueChainIds,
|
3733
|
-
chains,
|
3734
|
-
miniMetadatas,
|
3735
|
-
coders: {
|
3736
|
-
poolMembers: ["NominationPools", "PoolMembers"],
|
3737
|
-
bondedPools: ["NominationPools", "BondedPools"],
|
3738
|
-
ledger: ["Staking", "Ledger"],
|
3739
|
-
metadata: ["NominationPools", "Metadata"]
|
3740
|
-
}
|
3741
|
-
});
|
3742
|
-
const resultUnsubscribes = [];
|
3743
|
-
for (const [tokenId, addresses] of Object.entries(addressesByNomPoolToken)) {
|
3744
|
-
const token = tokens[tokenId];
|
3745
|
-
if (!token) {
|
3746
|
-
log.warn(`Token ${tokenId} not found`);
|
3747
|
-
continue;
|
3748
|
-
}
|
3749
|
-
if (token.type !== "substrate-native") {
|
3750
|
-
log.debug(`This module doesn't handle tokens of type ${token.type}`);
|
3751
|
-
continue;
|
3752
|
-
}
|
3753
|
-
const chainId = token.networkId;
|
3754
|
-
if (!chainId) {
|
3755
|
-
log.warn(`Token ${tokenId} has no chain`);
|
3756
|
-
continue;
|
3757
|
-
}
|
3758
|
-
const chain = chains[chainId];
|
3759
|
-
if (!chain) {
|
3760
|
-
log.warn(`Chain ${chainId} for token ${tokenId} not found`);
|
3761
|
-
continue;
|
3710
|
+
try {
|
3711
|
+
const allChains = await chaindataProvider$1.getNetworksMapById("polkadot");
|
3712
|
+
const tokens = await chaindataProvider$1.getTokensMapById();
|
3713
|
+
|
3714
|
+
// there should be only one network here when subscribing to balances, we've split it up by network at the top level
|
3715
|
+
const networkIds = lodash.keys(addressesByToken).map(tokenId => chaindataProvider.parseTokenId(tokenId).networkId);
|
3716
|
+
const miniMetadatas = new Map();
|
3717
|
+
for (const networkId of networkIds) {
|
3718
|
+
const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native");
|
3719
|
+
miniMetadatas.set(networkId, miniMetadata);
|
3762
3720
|
}
|
3763
|
-
|
3764
|
-
const {
|
3765
|
-
|
3766
|
-
|
3767
|
-
|
3768
|
-
|
3769
|
-
const
|
3770
|
-
|
3771
|
-
|
3772
|
-
|
3773
|
-
|
3774
|
-
|
3775
|
-
|
3776
|
-
|
3777
|
-
|
3778
|
-
|
3779
|
-
|
3780
|
-
|
3781
|
-
|
3782
|
-
|
3783
|
-
|
3721
|
+
signal?.throwIfAborted();
|
3722
|
+
const nomPoolTokenIds = Object.entries(tokens).filter(([, token]) => {
|
3723
|
+
// ignore non-native tokens
|
3724
|
+
if (token.type !== "substrate-native") return false;
|
3725
|
+
|
3726
|
+
// ignore tokens on chains with no nompools pallet
|
3727
|
+
const miniMetadata = miniMetadatas.get(token.networkId);
|
3728
|
+
return typeof miniMetadata?.extra?.nominationPoolsPalletId === "string";
|
3729
|
+
}).map(([tokenId]) => tokenId);
|
3730
|
+
|
3731
|
+
// staking can only be done by the native token on chains with the staking pallet
|
3732
|
+
const addressesByNomPoolToken = Object.fromEntries(Object.entries(addressesByToken)
|
3733
|
+
// remove ethereum addresses
|
3734
|
+
.map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !util.isEthereumAddress(address))])
|
3735
|
+
// remove tokens which aren't nom pool tokens
|
3736
|
+
.filter(([tokenId]) => nomPoolTokenIds.includes(tokenId)));
|
3737
|
+
const uniqueChainIds = getUniqueChainIds(addressesByNomPoolToken, tokens);
|
3738
|
+
const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
|
3739
|
+
const chainStorageCoders = buildStorageCoders({
|
3740
|
+
chainIds: uniqueChainIds,
|
3741
|
+
chains,
|
3742
|
+
miniMetadatas,
|
3743
|
+
coders: {
|
3744
|
+
poolMembers: ["NominationPools", "PoolMembers"],
|
3745
|
+
bondedPools: ["NominationPools", "BondedPools"],
|
3746
|
+
ledger: ["Staking", "Ledger"],
|
3747
|
+
metadata: ["NominationPools", "Metadata"]
|
3748
|
+
}
|
3749
|
+
});
|
3750
|
+
const resultUnsubscribes = [];
|
3751
|
+
for (const [tokenId, addresses] of Object.entries(addressesByNomPoolToken)) {
|
3752
|
+
const token = tokens[tokenId];
|
3753
|
+
if (!token) {
|
3754
|
+
log.warn(`Token ${tokenId} not found`);
|
3755
|
+
continue;
|
3756
|
+
}
|
3757
|
+
if (token.type !== "substrate-native") {
|
3758
|
+
log.debug(`This module doesn't handle tokens of type ${token.type}`);
|
3759
|
+
continue;
|
3760
|
+
}
|
3761
|
+
const chainId = token.networkId;
|
3762
|
+
if (!chainId) {
|
3763
|
+
log.warn(`Token ${tokenId} has no chain`);
|
3764
|
+
continue;
|
3765
|
+
}
|
3766
|
+
const chain = chains[chainId];
|
3767
|
+
if (!chain) {
|
3768
|
+
log.warn(`Chain ${chainId} for token ${tokenId} not found`);
|
3769
|
+
continue;
|
3770
|
+
}
|
3771
|
+
const miniMetadata = miniMetadatas.get(chainId);
|
3772
|
+
const {
|
3773
|
+
nominationPoolsPalletId
|
3774
|
+
} = miniMetadata?.extra ?? {};
|
3775
|
+
const subscribePoolMembers = (addresses, callback) => {
|
3776
|
+
const scaleCoder = chainStorageCoders.get(chainId)?.poolMembers;
|
3777
|
+
const queries = addresses.flatMap(address => {
|
3778
|
+
const stateKey = scale.encodeStateKey(scaleCoder, `Invalid address in ${chainId} poolMembers query ${address}`, address);
|
3779
|
+
if (!stateKey) return [];
|
3780
|
+
const decodeResult = change => {
|
3781
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3782
|
+
|
3783
|
+
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode poolMembers on chain ${chainId}`);
|
3784
|
+
const poolId = decoded?.pool_id?.toString?.();
|
3785
|
+
const points = decoded?.points?.toString?.();
|
3786
|
+
const unbondingEras = Array.from(decoded?.unbonding_eras ?? []).flatMap(entry => {
|
3787
|
+
if (entry === undefined) return [];
|
3788
|
+
const [key, value] = Array.from(entry);
|
3789
|
+
const era = key?.toString?.();
|
3790
|
+
const amount = value?.toString?.();
|
3791
|
+
if (typeof era !== "string" || typeof amount !== "string") return [];
|
3792
|
+
return {
|
3793
|
+
era,
|
3794
|
+
amount
|
3795
|
+
};
|
3796
|
+
});
|
3784
3797
|
return {
|
3785
|
-
|
3786
|
-
|
3798
|
+
tokenId,
|
3799
|
+
address,
|
3800
|
+
poolId,
|
3801
|
+
points,
|
3802
|
+
unbondingEras
|
3787
3803
|
};
|
3788
|
-
}
|
3804
|
+
};
|
3789
3805
|
return {
|
3790
|
-
|
3791
|
-
|
3806
|
+
chainId,
|
3807
|
+
stateKey,
|
3808
|
+
decodeResult
|
3809
|
+
};
|
3810
|
+
});
|
3811
|
+
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3812
|
+
return () => subscription.then(unsubscribe => unsubscribe());
|
3813
|
+
};
|
3814
|
+
const subscribePoolPoints = (poolIds, callback) => {
|
3815
|
+
if (poolIds.length === 0) callback(null, []);
|
3816
|
+
const scaleCoder = chainStorageCoders.get(chainId)?.bondedPools;
|
3817
|
+
const queries = poolIds.flatMap(poolId => {
|
3818
|
+
const stateKey = scale.encodeStateKey(scaleCoder, `Invalid poolId in ${chainId} bondedPools query ${poolId}`, poolId);
|
3819
|
+
if (!stateKey) return [];
|
3820
|
+
const decodeResult = change => {
|
3821
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3822
|
+
|
3823
|
+
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode bondedPools on chain ${chainId}`);
|
3824
|
+
const points = decoded?.points?.toString?.();
|
3825
|
+
return {
|
3826
|
+
poolId,
|
3827
|
+
points
|
3828
|
+
};
|
3829
|
+
};
|
3830
|
+
return {
|
3831
|
+
chainId,
|
3832
|
+
stateKey,
|
3833
|
+
decodeResult
|
3834
|
+
};
|
3835
|
+
});
|
3836
|
+
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3837
|
+
return () => subscription.then(unsubscribe => unsubscribe());
|
3838
|
+
};
|
3839
|
+
const subscribePoolStake = (poolIds, callback) => {
|
3840
|
+
if (poolIds.length === 0) callback(null, []);
|
3841
|
+
const scaleCoder = chainStorageCoders.get(chainId)?.ledger;
|
3842
|
+
const queries = poolIds.flatMap(poolId => {
|
3843
|
+
if (!nominationPoolsPalletId) return [];
|
3844
|
+
const stashAddress = nompoolStashAccountId(nominationPoolsPalletId, poolId);
|
3845
|
+
const stateKey = scale.encodeStateKey(scaleCoder, `Invalid address in ${chainId} ledger query ${stashAddress}`, stashAddress);
|
3846
|
+
if (!stateKey) return [];
|
3847
|
+
const decodeResult = change => {
|
3848
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3849
|
+
|
3850
|
+
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode ledger on chain ${chainId}`);
|
3851
|
+
const activeStake = decoded?.active?.toString?.();
|
3852
|
+
return {
|
3853
|
+
poolId,
|
3854
|
+
activeStake
|
3855
|
+
};
|
3856
|
+
};
|
3857
|
+
return {
|
3858
|
+
chainId,
|
3859
|
+
stateKey,
|
3860
|
+
decodeResult
|
3861
|
+
};
|
3862
|
+
});
|
3863
|
+
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3864
|
+
return () => subscription.then(unsubscribe => unsubscribe());
|
3865
|
+
};
|
3866
|
+
const subscribePoolMetadata = (poolIds, callback) => {
|
3867
|
+
if (poolIds.length === 0) callback(null, []);
|
3868
|
+
const scaleCoder = chainStorageCoders.get(chainId)?.metadata;
|
3869
|
+
const queries = poolIds.flatMap(poolId => {
|
3870
|
+
if (!nominationPoolsPalletId) return [];
|
3871
|
+
const stateKey = scale.encodeStateKey(scaleCoder, `Invalid poolId in ${chainId} metadata query ${poolId}`, poolId);
|
3872
|
+
if (!stateKey) return [];
|
3873
|
+
const decodeResult = change => {
|
3874
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3875
|
+
|
3876
|
+
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode metadata on chain ${chainId}`);
|
3877
|
+
const metadata = decoded?.asText?.();
|
3878
|
+
return {
|
3879
|
+
poolId,
|
3880
|
+
metadata
|
3881
|
+
};
|
3882
|
+
};
|
3883
|
+
return {
|
3884
|
+
chainId,
|
3885
|
+
stateKey,
|
3886
|
+
decodeResult
|
3887
|
+
};
|
3888
|
+
});
|
3889
|
+
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3890
|
+
return () => subscription.then(unsubscribe => unsubscribe());
|
3891
|
+
};
|
3892
|
+
const poolMembersByAddress$ = asObservable(subscribePoolMembers)(addresses).pipe(rxjs.scan((state, next) => {
|
3893
|
+
for (const poolMembers of next) {
|
3894
|
+
const {
|
3792
3895
|
poolId,
|
3793
3896
|
points,
|
3794
3897
|
unbondingEras
|
3795
|
-
};
|
3796
|
-
|
3797
|
-
|
3798
|
-
|
3799
|
-
|
3800
|
-
|
3801
|
-
}
|
3802
|
-
|
3803
|
-
|
3804
|
-
|
3805
|
-
|
3806
|
-
|
3807
|
-
|
3808
|
-
const
|
3809
|
-
|
3810
|
-
|
3811
|
-
if (!stateKey) return [];
|
3812
|
-
const decodeResult = change => {
|
3813
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3814
|
-
|
3815
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode bondedPools on chain ${chainId}`);
|
3816
|
-
const points = decoded?.points?.toString?.();
|
3817
|
-
return {
|
3898
|
+
} = poolMembers;
|
3899
|
+
if (typeof poolId === "string" && typeof points === "string") state.set(poolMembers.address, {
|
3900
|
+
poolId,
|
3901
|
+
points,
|
3902
|
+
unbondingEras
|
3903
|
+
});else state.set(poolMembers.address, null);
|
3904
|
+
}
|
3905
|
+
return state;
|
3906
|
+
}, new Map()), rxjs.share());
|
3907
|
+
const poolIdByAddress$ = poolMembersByAddress$.pipe(rxjs.map(pm => new Map(Array.from(pm).map(([address, pm]) => [address, pm?.poolId ?? null]))));
|
3908
|
+
const pointsByAddress$ = poolMembersByAddress$.pipe(rxjs.map(pm => new Map(Array.from(pm).map(([address, pm]) => [address, pm?.points ?? null]))));
|
3909
|
+
const unbondingErasByAddress$ = poolMembersByAddress$.pipe(rxjs.map(pm => new Map(Array.from(pm).map(([address, pm]) => [address, pm?.unbondingEras ?? null]))));
|
3910
|
+
const poolIds$ = poolIdByAddress$.pipe(rxjs.map(byAddress => [...new Set(Array.from(byAddress.values()).flatMap(poolId => poolId ?? []))]));
|
3911
|
+
const pointsByPool$ = poolIds$.pipe(rxjs.map(poolIds => asObservable(subscribePoolPoints)(poolIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
|
3912
|
+
for (const poolPoints of next) {
|
3913
|
+
const {
|
3818
3914
|
poolId,
|
3819
3915
|
points
|
3820
|
-
};
|
3821
|
-
|
3822
|
-
|
3823
|
-
|
3824
|
-
|
3825
|
-
|
3826
|
-
|
3827
|
-
|
3828
|
-
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3829
|
-
return () => subscription.then(unsubscribe => unsubscribe());
|
3830
|
-
};
|
3831
|
-
const subscribePoolStake = (poolIds, callback) => {
|
3832
|
-
if (poolIds.length === 0) callback(null, []);
|
3833
|
-
const scaleCoder = chainStorageCoders.get(chainId)?.ledger;
|
3834
|
-
const queries = poolIds.flatMap(poolId => {
|
3835
|
-
if (!nominationPoolsPalletId) return [];
|
3836
|
-
const stashAddress = nompoolStashAccountId(nominationPoolsPalletId, poolId);
|
3837
|
-
const stateKey = scale.encodeStateKey(scaleCoder, `Invalid address in ${chainId} ledger query ${stashAddress}`, stashAddress);
|
3838
|
-
if (!stateKey) return [];
|
3839
|
-
const decodeResult = change => {
|
3840
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3841
|
-
|
3842
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode ledger on chain ${chainId}`);
|
3843
|
-
const activeStake = decoded?.active?.toString?.();
|
3844
|
-
return {
|
3916
|
+
} = poolPoints;
|
3917
|
+
if (typeof points === "string") state.set(poolId, points);else state.delete(poolId);
|
3918
|
+
}
|
3919
|
+
return state;
|
3920
|
+
}, new Map()));
|
3921
|
+
const stakeByPool$ = poolIds$.pipe(rxjs.map(poolIds => asObservable(subscribePoolStake)(poolIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
|
3922
|
+
for (const poolStake of next) {
|
3923
|
+
const {
|
3845
3924
|
poolId,
|
3846
3925
|
activeStake
|
3847
|
-
};
|
3848
|
-
|
3849
|
-
|
3850
|
-
|
3851
|
-
|
3852
|
-
|
3853
|
-
|
3854
|
-
|
3855
|
-
const subscription = new RpcStateQueryHelper(chainConnector, queries).subscribe(callback);
|
3856
|
-
return () => subscription.then(unsubscribe => unsubscribe());
|
3857
|
-
};
|
3858
|
-
const subscribePoolMetadata = (poolIds, callback) => {
|
3859
|
-
if (poolIds.length === 0) callback(null, []);
|
3860
|
-
const scaleCoder = chainStorageCoders.get(chainId)?.metadata;
|
3861
|
-
const queries = poolIds.flatMap(poolId => {
|
3862
|
-
if (!nominationPoolsPalletId) return [];
|
3863
|
-
const stateKey = scale.encodeStateKey(scaleCoder, `Invalid poolId in ${chainId} metadata query ${poolId}`, poolId);
|
3864
|
-
if (!stateKey) return [];
|
3865
|
-
const decodeResult = change => {
|
3866
|
-
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
3867
|
-
|
3868
|
-
const decoded = scale.decodeScale(scaleCoder, change, `Failed to decode metadata on chain ${chainId}`);
|
3869
|
-
const metadata = decoded?.asText?.();
|
3870
|
-
return {
|
3926
|
+
} = poolStake;
|
3927
|
+
if (typeof activeStake === "string") state.set(poolId, activeStake);else state.delete(poolId);
|
3928
|
+
}
|
3929
|
+
return state;
|
3930
|
+
}, new Map()));
|
3931
|
+
const metadataByPool$ = poolIds$.pipe(rxjs.map(poolIds => asObservable(subscribePoolMetadata)(poolIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
|
3932
|
+
for (const poolMetadata of next) {
|
3933
|
+
const {
|
3871
3934
|
poolId,
|
3872
3935
|
metadata
|
3873
|
-
};
|
3874
|
-
|
3875
|
-
|
3876
|
-
|
3877
|
-
|
3878
|
-
|
3879
|
-
|
3880
|
-
|
3881
|
-
|
3882
|
-
|
3883
|
-
|
3884
|
-
|
3885
|
-
|
3886
|
-
|
3887
|
-
|
3888
|
-
|
3889
|
-
|
3890
|
-
|
3891
|
-
|
3892
|
-
|
3893
|
-
|
3894
|
-
|
3895
|
-
|
3896
|
-
|
3897
|
-
|
3898
|
-
}, new Map()), rxjs.share());
|
3899
|
-
const poolIdByAddress$ = poolMembersByAddress$.pipe(rxjs.map(pm => new Map(Array.from(pm).map(([address, pm]) => [address, pm?.poolId ?? null]))));
|
3900
|
-
const pointsByAddress$ = poolMembersByAddress$.pipe(rxjs.map(pm => new Map(Array.from(pm).map(([address, pm]) => [address, pm?.points ?? null]))));
|
3901
|
-
const unbondingErasByAddress$ = poolMembersByAddress$.pipe(rxjs.map(pm => new Map(Array.from(pm).map(([address, pm]) => [address, pm?.unbondingEras ?? null]))));
|
3902
|
-
const poolIds$ = poolIdByAddress$.pipe(rxjs.map(byAddress => [...new Set(Array.from(byAddress.values()).flatMap(poolId => poolId ?? []))]));
|
3903
|
-
const pointsByPool$ = poolIds$.pipe(rxjs.map(poolIds => asObservable(subscribePoolPoints)(poolIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
|
3904
|
-
for (const poolPoints of next) {
|
3905
|
-
const {
|
3906
|
-
poolId,
|
3907
|
-
points
|
3908
|
-
} = poolPoints;
|
3909
|
-
if (typeof points === "string") state.set(poolId, points);else state.delete(poolId);
|
3910
|
-
}
|
3911
|
-
return state;
|
3912
|
-
}, new Map()));
|
3913
|
-
const stakeByPool$ = poolIds$.pipe(rxjs.map(poolIds => asObservable(subscribePoolStake)(poolIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
|
3914
|
-
for (const poolStake of next) {
|
3915
|
-
const {
|
3916
|
-
poolId,
|
3917
|
-
activeStake
|
3918
|
-
} = poolStake;
|
3919
|
-
if (typeof activeStake === "string") state.set(poolId, activeStake);else state.delete(poolId);
|
3920
|
-
}
|
3921
|
-
return state;
|
3922
|
-
}, new Map()));
|
3923
|
-
const metadataByPool$ = poolIds$.pipe(rxjs.map(poolIds => asObservable(subscribePoolMetadata)(poolIds)), rxjs.switchAll(), rxjs.scan((state, next) => {
|
3924
|
-
for (const poolMetadata of next) {
|
3925
|
-
const {
|
3926
|
-
poolId,
|
3927
|
-
metadata
|
3928
|
-
} = poolMetadata;
|
3929
|
-
if (typeof metadata === "string") state.set(poolId, metadata);else state.delete(poolId);
|
3930
|
-
}
|
3931
|
-
return state;
|
3932
|
-
}, new Map()));
|
3933
|
-
const subscription = rxjs.combineLatest([poolIdByAddress$, pointsByAddress$, unbondingErasByAddress$, pointsByPool$, stakeByPool$, metadataByPool$]).subscribe({
|
3934
|
-
next: ([poolIdByAddress, pointsByAddress, unbondingErasByAddress, pointsByPool, stakeByPool, metadataByPool]) => {
|
3935
|
-
const balances = Array.from(poolIdByAddress).map(([address, poolId]) => {
|
3936
|
-
const parsedPoolId = poolId === null ? undefined : parseInt(poolId);
|
3937
|
-
const points = pointsByAddress.get(address) ?? "0";
|
3938
|
-
const poolPoints = pointsByPool.get(poolId ?? "") ?? "0";
|
3939
|
-
const poolStake = stakeByPool.get(poolId ?? "") ?? "0";
|
3940
|
-
const poolMetadata = poolId ? metadataByPool.get(poolId) ?? `Pool ${poolId}` : undefined;
|
3941
|
-
const amount = points === "0" || poolPoints === "0" || poolStake === "0" ? 0n : BigInt(poolStake) * BigInt(points) / BigInt(poolPoints);
|
3942
|
-
const unbondingAmount = (unbondingErasByAddress.get(address) ?? []).reduce((total, {
|
3943
|
-
amount
|
3944
|
-
}) => total + BigInt(amount ?? "0"), 0n);
|
3945
|
-
return {
|
3946
|
-
source: "substrate-native",
|
3947
|
-
status: "live",
|
3948
|
-
address,
|
3949
|
-
networkId: chainId,
|
3950
|
-
tokenId,
|
3951
|
-
values: [{
|
3952
|
-
source: "nompools-staking",
|
3953
|
-
type: "nompool",
|
3954
|
-
label: "nompools-staking",
|
3955
|
-
amount: amount.toString(),
|
3956
|
-
meta: {
|
3936
|
+
} = poolMetadata;
|
3937
|
+
if (typeof metadata === "string") state.set(poolId, metadata);else state.delete(poolId);
|
3938
|
+
}
|
3939
|
+
return state;
|
3940
|
+
}, new Map()));
|
3941
|
+
const subscription = rxjs.combineLatest([poolIdByAddress$, pointsByAddress$, unbondingErasByAddress$, pointsByPool$, stakeByPool$, metadataByPool$]).subscribe({
|
3942
|
+
next: ([poolIdByAddress, pointsByAddress, unbondingErasByAddress, pointsByPool, stakeByPool, metadataByPool]) => {
|
3943
|
+
const balances = Array.from(poolIdByAddress).map(([address, poolId]) => {
|
3944
|
+
const parsedPoolId = poolId === null ? undefined : parseInt(poolId);
|
3945
|
+
const points = pointsByAddress.get(address) ?? "0";
|
3946
|
+
const poolPoints = pointsByPool.get(poolId ?? "") ?? "0";
|
3947
|
+
const poolStake = stakeByPool.get(poolId ?? "") ?? "0";
|
3948
|
+
const poolMetadata = poolId ? metadataByPool.get(poolId) ?? `Pool ${poolId}` : undefined;
|
3949
|
+
const amount = points === "0" || poolPoints === "0" || poolStake === "0" ? 0n : BigInt(poolStake) * BigInt(points) / BigInt(poolPoints);
|
3950
|
+
const unbondingAmount = (unbondingErasByAddress.get(address) ?? []).reduce((total, {
|
3951
|
+
amount
|
3952
|
+
}) => total + BigInt(amount ?? "0"), 0n);
|
3953
|
+
return {
|
3954
|
+
source: "substrate-native",
|
3955
|
+
status: "live",
|
3956
|
+
address,
|
3957
|
+
networkId: chainId,
|
3958
|
+
tokenId,
|
3959
|
+
values: [{
|
3960
|
+
source: "nompools-staking",
|
3957
3961
|
type: "nompool",
|
3958
|
-
|
3959
|
-
|
3960
|
-
|
3961
|
-
|
3962
|
-
|
3963
|
-
|
3964
|
-
|
3965
|
-
|
3966
|
-
|
3967
|
-
|
3968
|
-
|
3969
|
-
|
3970
|
-
|
3971
|
-
|
3972
|
-
|
3973
|
-
|
3974
|
-
|
3975
|
-
|
3976
|
-
|
3962
|
+
label: "nompools-staking",
|
3963
|
+
amount: amount.toString(),
|
3964
|
+
meta: {
|
3965
|
+
type: "nompool",
|
3966
|
+
poolId: parsedPoolId,
|
3967
|
+
description: poolMetadata
|
3968
|
+
}
|
3969
|
+
}, {
|
3970
|
+
source: "nompools-staking",
|
3971
|
+
type: "nompool",
|
3972
|
+
label: "nompools-unbonding",
|
3973
|
+
amount: unbondingAmount.toString(),
|
3974
|
+
meta: {
|
3975
|
+
poolId: parsedPoolId,
|
3976
|
+
description: poolMetadata,
|
3977
|
+
unbonding: true
|
3978
|
+
}
|
3979
|
+
}]
|
3980
|
+
};
|
3981
|
+
}).filter(util.isNotNil);
|
3982
|
+
if (balances.length > 0) callback(null, balances);
|
3983
|
+
},
|
3984
|
+
error: error => callback(error)
|
3985
|
+
});
|
3986
|
+
resultUnsubscribes.push(() => subscription.unsubscribe());
|
3987
|
+
}
|
3988
|
+
return () => resultUnsubscribes.forEach(unsub => unsub());
|
3989
|
+
} catch (err) {
|
3990
|
+
if (!util.isAbortError(err)) log.error("Error subscribing to nom pool staking", {
|
3991
|
+
err
|
3977
3992
|
});
|
3978
|
-
|
3993
|
+
return () => {};
|
3979
3994
|
}
|
3980
|
-
return () => resultUnsubscribes.forEach(unsub => unsub());
|
3981
3995
|
}
|
3982
3996
|
|
3983
3997
|
const SUBTENSOR_ROOT_NETUID = 0;
|
@@ -4021,227 +4035,234 @@ const calculateTaoFromDynamicInfo = ({
|
|
4021
4035
|
|
4022
4036
|
// TODO make this method chain-specific
|
4023
4037
|
async function subscribeSubtensorStaking(chaindataProvider$1, chainConnector, addressesByToken, callback, signal) {
|
4024
|
-
|
4025
|
-
|
4026
|
-
|
4027
|
-
|
4028
|
-
|
4029
|
-
|
4030
|
-
|
4031
|
-
|
4032
|
-
|
4033
|
-
|
4034
|
-
signal?.throwIfAborted();
|
4035
|
-
const subtensorTokenIds = Object.entries(tokens).filter(([, token]) => {
|
4036
|
-
// ignore non-native tokens
|
4037
|
-
if (token.type !== "substrate-native") return false;
|
4038
|
-
// ignore tokens on chains with no subtensor pallet
|
4039
|
-
const miniMetadata = miniMetadatas.get(token.networkId);
|
4040
|
-
return miniMetadata?.extra?.hasSubtensorPallet === true;
|
4041
|
-
}).map(([tokenId]) => tokenId);
|
4042
|
-
|
4043
|
-
// staking can only be done by the native token on chains with the subtensor pallet
|
4044
|
-
const addressesBySubtensorToken = Object.fromEntries(Object.entries(addressesByToken)
|
4045
|
-
// remove ethereum addresses
|
4046
|
-
.map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !util.isEthereumAddress(address))])
|
4047
|
-
// remove tokens which aren't subtensor staking tokens
|
4048
|
-
.filter(([tokenId]) => subtensorTokenIds.includes(tokenId)));
|
4049
|
-
const uniqueChainIds = getUniqueChainIds(addressesBySubtensorToken, tokens);
|
4050
|
-
const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
|
4051
|
-
const abortController = new AbortController();
|
4052
|
-
for (const [tokenId, addresses] of Object.entries(addressesBySubtensorToken)) {
|
4053
|
-
const token = tokens[tokenId];
|
4054
|
-
if (!token) {
|
4055
|
-
log.warn(`Token ${tokenId} not found`);
|
4056
|
-
continue;
|
4057
|
-
}
|
4058
|
-
if (token.type !== "substrate-native") {
|
4059
|
-
log.debug(`This module doesn't handle tokens of type ${token.type}`);
|
4060
|
-
continue;
|
4061
|
-
}
|
4062
|
-
const chainId = token.networkId;
|
4063
|
-
const chain = chains[chainId];
|
4064
|
-
if (!chain) {
|
4065
|
-
log.warn(`Chain ${chainId} for token ${tokenId} not found`);
|
4066
|
-
continue;
|
4067
|
-
}
|
4068
|
-
const miniMetadata = miniMetadatas.get(token.networkId);
|
4069
|
-
if (!miniMetadata?.data) {
|
4070
|
-
log.warn(`MiniMetadata for chain ${chainId} not found`);
|
4071
|
-
continue;
|
4038
|
+
try {
|
4039
|
+
const allChains = await chaindataProvider$1.getNetworksMapById("polkadot");
|
4040
|
+
const tokens = await chaindataProvider$1.getTokensMapById();
|
4041
|
+
|
4042
|
+
// there should be only one network here when subscribing to balances, we've split it up by network at the top level
|
4043
|
+
const networkIds = lodash.keys(addressesByToken).map(tokenId => chaindataProvider.parseTokenId(tokenId).networkId);
|
4044
|
+
const miniMetadatas = new Map();
|
4045
|
+
for (const networkId of networkIds) {
|
4046
|
+
const miniMetadata = await getMiniMetadata(chaindataProvider$1, chainConnector, networkId, "substrate-native", signal);
|
4047
|
+
miniMetadatas.set(networkId, miniMetadata);
|
4072
4048
|
}
|
4073
|
-
|
4074
|
-
|
4075
|
-
|
4076
|
-
|
4077
|
-
|
4078
|
-
)
|
4079
|
-
|
4080
|
-
|
4081
|
-
|
4082
|
-
|
4083
|
-
|
4084
|
-
|
4085
|
-
|
4086
|
-
|
4087
|
-
|
4088
|
-
|
4089
|
-
|
4090
|
-
|
4091
|
-
|
4092
|
-
|
4093
|
-
|
4094
|
-
|
4095
|
-
|
4096
|
-
|
4097
|
-
|
4098
|
-
|
4099
|
-
|
4100
|
-
|
4101
|
-
|
4102
|
-
|
4049
|
+
signal?.throwIfAborted();
|
4050
|
+
const subtensorTokenIds = Object.entries(tokens).filter(([, token]) => {
|
4051
|
+
// ignore non-native tokens
|
4052
|
+
if (token.type !== "substrate-native") return false;
|
4053
|
+
// ignore tokens on chains with no subtensor pallet
|
4054
|
+
const miniMetadata = miniMetadatas.get(token.networkId);
|
4055
|
+
return miniMetadata?.extra?.hasSubtensorPallet === true;
|
4056
|
+
}).map(([tokenId]) => tokenId);
|
4057
|
+
|
4058
|
+
// staking can only be done by the native token on chains with the subtensor pallet
|
4059
|
+
const addressesBySubtensorToken = Object.fromEntries(Object.entries(addressesByToken)
|
4060
|
+
// remove ethereum addresses
|
4061
|
+
.map(([tokenId, addresses]) => [tokenId, addresses.filter(address => !util.isEthereumAddress(address))])
|
4062
|
+
// remove tokens which aren't subtensor staking tokens
|
4063
|
+
.filter(([tokenId]) => subtensorTokenIds.includes(tokenId)));
|
4064
|
+
const uniqueChainIds = getUniqueChainIds(addressesBySubtensorToken, tokens);
|
4065
|
+
const chains = Object.fromEntries(Object.entries(allChains).filter(([chainId]) => uniqueChainIds.includes(chainId)));
|
4066
|
+
const abortController = new AbortController();
|
4067
|
+
for (const [tokenId, addresses] of Object.entries(addressesBySubtensorToken)) {
|
4068
|
+
const token = tokens[tokenId];
|
4069
|
+
if (!token) {
|
4070
|
+
log.warn(`Token ${tokenId} not found`);
|
4071
|
+
continue;
|
4072
|
+
}
|
4073
|
+
if (token.type !== "substrate-native") {
|
4074
|
+
log.debug(`This module doesn't handle tokens of type ${token.type}`);
|
4075
|
+
continue;
|
4076
|
+
}
|
4077
|
+
const chainId = token.networkId;
|
4078
|
+
const chain = chains[chainId];
|
4079
|
+
if (!chain) {
|
4080
|
+
log.warn(`Chain ${chainId} for token ${tokenId} not found`);
|
4081
|
+
continue;
|
4082
|
+
}
|
4083
|
+
const miniMetadata = miniMetadatas.get(token.networkId);
|
4084
|
+
if (!miniMetadata?.data) {
|
4085
|
+
log.warn(`MiniMetadata for chain ${chainId} not found`);
|
4086
|
+
continue;
|
4087
|
+
}
|
4088
|
+
const scaleApi = sapi.getScaleApi({
|
4089
|
+
chainId,
|
4090
|
+
send: (...args) => chainConnector.send(chainId, ...args, {
|
4091
|
+
expectErrors: true
|
4092
|
+
} // don't pollute the wallet logs when this request fails
|
4093
|
+
)
|
4094
|
+
}, miniMetadata.data, token, chain.hasCheckMetadataHash, chain.signedExtensions, chain.registryTypes);
|
4095
|
+
|
4096
|
+
// sets the number of addresses to query in parallel (per chain, since each chain runs in parallel to the others)
|
4097
|
+
const concurrency = 4;
|
4098
|
+
// In-memory cache for successful dynamic info results
|
4099
|
+
const dynamicInfoCache = new Map();
|
4100
|
+
const fetchDynamicInfoForNetuids = async uniqueNetuids => {
|
4101
|
+
const MAX_RETRIES = 3;
|
4102
|
+
const RETRY_DELAY_MS = 500;
|
4103
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
4104
|
+
const fetchInfo = async netuid => {
|
4105
|
+
if (netuid === 0) return null;
|
4106
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
4107
|
+
try {
|
4108
|
+
const params = [netuid];
|
4109
|
+
const result = await scaleApi.getRuntimeCallValue("SubnetInfoRuntimeApi", "get_dynamic_info", params);
|
4110
|
+
dynamicInfoCache.set(netuid, result); // Cache successful response
|
4111
|
+
return result;
|
4112
|
+
} catch (error) {
|
4113
|
+
log.trace(`Attempt ${attempt} failed for netuid ${netuid}:`, error);
|
4114
|
+
if (attempt < MAX_RETRIES) {
|
4115
|
+
const backoffTime = RETRY_DELAY_MS * 2 ** (attempt - 1);
|
4116
|
+
log.trace(`Retrying in ${backoffTime}ms...`);
|
4117
|
+
await delay(backoffTime);
|
4118
|
+
}
|
4103
4119
|
}
|
4104
4120
|
}
|
4105
|
-
|
4106
|
-
|
4107
|
-
|
4108
|
-
|
4109
|
-
|
4110
|
-
|
4121
|
+
if (dynamicInfoCache.has(netuid)) {
|
4122
|
+
return dynamicInfoCache.get(netuid); // Use cached value on failure
|
4123
|
+
}
|
4124
|
+
log.trace(`Failed to fetch dynamic info for netuid ${netuid} after ${MAX_RETRIES} attempts.`);
|
4125
|
+
return null;
|
4126
|
+
};
|
4127
|
+
return Promise.all(uniqueNetuids.map(fetchInfo));
|
4111
4128
|
};
|
4112
|
-
|
4113
|
-
|
4114
|
-
|
4115
|
-
|
4116
|
-
|
4117
|
-
|
4118
|
-
|
4119
|
-
|
4120
|
-
|
4121
|
-
|
4122
|
-
|
4123
|
-
|
4124
|
-
const stakes = result?.map(({
|
4125
|
-
coldkey,
|
4126
|
-
hotkey,
|
4127
|
-
netuid,
|
4128
|
-
stake
|
4129
|
-
}) => {
|
4130
|
-
return {
|
4131
|
-
address: coldkey,
|
4129
|
+
const subtensorQueries = rxjs.from(addresses).pipe(
|
4130
|
+
// mergeMap lets us run N concurrent queries, where N is the value of `concurrency`
|
4131
|
+
rxjs.mergeMap(async address => {
|
4132
|
+
const queryMethods = [async () => {
|
4133
|
+
if (chain.isTestnet) return [];
|
4134
|
+
const params = [address];
|
4135
|
+
const result = await scaleApi.getRuntimeCallValue("StakeInfoRuntimeApi", "get_stake_info_for_coldkey", params);
|
4136
|
+
if (!Array.isArray(result)) return [];
|
4137
|
+
const uniqueNetuids = Array.from(new Set(result.map(item => Number(item.netuid)).filter(netuid => netuid !== SUBTENSOR_ROOT_NETUID)));
|
4138
|
+
await fetchDynamicInfoForNetuids(uniqueNetuids);
|
4139
|
+
const stakes = result?.map(({
|
4140
|
+
coldkey,
|
4132
4141
|
hotkey,
|
4133
|
-
netuid
|
4134
|
-
stake
|
4135
|
-
|
4136
|
-
|
4137
|
-
|
4138
|
-
|
4139
|
-
|
4140
|
-
|
4141
|
-
|
4142
|
-
|
4143
|
-
|
4144
|
-
|
4145
|
-
|
4146
|
-
return
|
4147
|
-
}
|
4148
|
-
|
4149
|
-
|
4142
|
+
netuid,
|
4143
|
+
stake
|
4144
|
+
}) => {
|
4145
|
+
return {
|
4146
|
+
address: coldkey,
|
4147
|
+
hotkey,
|
4148
|
+
netuid: Number(netuid),
|
4149
|
+
stake: BigInt(stake),
|
4150
|
+
dynamicInfo: dynamicInfoCache.get(Number(netuid))
|
4151
|
+
};
|
4152
|
+
}).filter(({
|
4153
|
+
stake
|
4154
|
+
}) => stake >= SUBTENSOR_MIN_STAKE_AMOUNT_PLANK);
|
4155
|
+
return stakes;
|
4156
|
+
}];
|
4157
|
+
const errors = [];
|
4158
|
+
for (const queryMethod of queryMethods) {
|
4159
|
+
try {
|
4160
|
+
// try each query method
|
4161
|
+
return await queryMethod();
|
4162
|
+
} catch (cause) {
|
4163
|
+
// if it fails, keep track of the error and try the next one
|
4164
|
+
errors.push(cause);
|
4165
|
+
}
|
4150
4166
|
}
|
4151
|
-
}
|
4152
4167
|
|
4153
|
-
|
4154
|
-
|
4155
|
-
|
4156
|
-
|
4157
|
-
|
4158
|
-
|
4159
|
-
|
4160
|
-
|
4161
|
-
|
4162
|
-
|
4163
|
-
address,
|
4164
|
-
hotkey,
|
4165
|
-
stake,
|
4166
|
-
netuid,
|
4167
|
-
dynamicInfo
|
4168
|
-
}) => {
|
4169
|
-
const {
|
4170
|
-
token_symbol,
|
4171
|
-
subnet_name,
|
4172
|
-
subnet_identity
|
4173
|
-
} = dynamicInfo ?? {};
|
4174
|
-
const tokenSymbol = new TextDecoder().decode(Uint8Array.from(token_symbol ?? []));
|
4175
|
-
const subnetName = new TextDecoder().decode(Uint8Array.from(subnet_name ?? []));
|
4176
|
-
|
4177
|
-
/** Map from Record<string, Binary> to Record<string, string> */
|
4178
|
-
const binaryToText = input => Object.entries(input).reduce((acc, [key, value]) => {
|
4179
|
-
acc[key] = value.asText();
|
4180
|
-
return acc;
|
4181
|
-
}, {});
|
4182
|
-
const subnetIdentity = subnet_identity ? binaryToText(subnet_identity) : undefined;
|
4183
|
-
|
4184
|
-
// Add 1n balance if failed to fetch dynamic info, so the position is not ignored by Balance lib and is displayed in the UI.
|
4185
|
-
const alphaStakedInTao = dynamicInfo ? calculateTaoFromDynamicInfo({
|
4186
|
-
dynamicInfo,
|
4187
|
-
alphaStaked: stake
|
4188
|
-
}) : 1n;
|
4189
|
-
const alphaToTaoRate = calculateTaoFromDynamicInfo({
|
4190
|
-
dynamicInfo: dynamicInfo ?? null,
|
4191
|
-
alphaStaked: ONE_ALPHA_TOKEN
|
4192
|
-
}).toString();
|
4193
|
-
const stakeByNetuid = Number(netuid) === SUBTENSOR_ROOT_NETUID ? stake : alphaStakedInTao;
|
4194
|
-
return {
|
4195
|
-
source: "substrate-native",
|
4196
|
-
status: "live",
|
4168
|
+
// if we get to here, that means that all query methods failed
|
4169
|
+
// let's throw the errors back to the native balance module
|
4170
|
+
throw new Error([`Failed to fetch ${tokenId} subtensor staked balance for ${address}:`, ...errors.map(error => String(error))].join("\n\t"));
|
4171
|
+
}, concurrency),
|
4172
|
+
// instead of emitting each balance as it's fetched, toArray waits for them all to fetch and then it collects them into an array
|
4173
|
+
rxjs.toArray(),
|
4174
|
+
// this mergeMap flattens our Array<Array<Stakes>> into just an Array<Stakes>
|
4175
|
+
rxjs.mergeMap(stakes => stakes),
|
4176
|
+
// convert our Array<Stakes> into Array<Balances>, which we can then return to the native balance module
|
4177
|
+
rxjs.map(stakes => stakes.map(({
|
4197
4178
|
address,
|
4198
|
-
|
4199
|
-
|
4200
|
-
|
4201
|
-
|
4202
|
-
|
4203
|
-
|
4204
|
-
|
4205
|
-
|
4206
|
-
|
4207
|
-
|
4208
|
-
|
4209
|
-
|
4210
|
-
|
4211
|
-
|
4212
|
-
|
4213
|
-
|
4214
|
-
|
4215
|
-
|
4216
|
-
|
4179
|
+
hotkey,
|
4180
|
+
stake,
|
4181
|
+
netuid,
|
4182
|
+
dynamicInfo
|
4183
|
+
}) => {
|
4184
|
+
const {
|
4185
|
+
token_symbol,
|
4186
|
+
subnet_name,
|
4187
|
+
subnet_identity
|
4188
|
+
} = dynamicInfo ?? {};
|
4189
|
+
const tokenSymbol = new TextDecoder().decode(Uint8Array.from(token_symbol ?? []));
|
4190
|
+
const subnetName = new TextDecoder().decode(Uint8Array.from(subnet_name ?? []));
|
4191
|
+
|
4192
|
+
/** Map from Record<string, Binary> to Record<string, string> */
|
4193
|
+
const binaryToText = input => Object.entries(input).reduce((acc, [key, value]) => {
|
4194
|
+
acc[key] = value.asText();
|
4195
|
+
return acc;
|
4196
|
+
}, {});
|
4197
|
+
const subnetIdentity = subnet_identity ? binaryToText(subnet_identity) : undefined;
|
4198
|
+
|
4199
|
+
// Add 1n balance if failed to fetch dynamic info, so the position is not ignored by Balance lib and is displayed in the UI.
|
4200
|
+
const alphaStakedInTao = dynamicInfo ? calculateTaoFromDynamicInfo({
|
4201
|
+
dynamicInfo,
|
4202
|
+
alphaStaked: stake
|
4203
|
+
}) : 1n;
|
4204
|
+
const alphaToTaoRate = calculateTaoFromDynamicInfo({
|
4205
|
+
dynamicInfo: dynamicInfo ?? null,
|
4206
|
+
alphaStaked: ONE_ALPHA_TOKEN
|
4207
|
+
}).toString();
|
4208
|
+
const stakeByNetuid = Number(netuid) === SUBTENSOR_ROOT_NETUID ? stake : alphaStakedInTao;
|
4209
|
+
return {
|
4210
|
+
source: "substrate-native",
|
4211
|
+
status: "live",
|
4212
|
+
address,
|
4213
|
+
networkId: chainId,
|
4214
|
+
tokenId,
|
4215
|
+
values: [{
|
4216
|
+
source: "subtensor-staking",
|
4217
|
+
type: "subtensor",
|
4218
|
+
label: "subtensor-staking",
|
4219
|
+
amount: stakeByNetuid.toString(),
|
4220
|
+
meta: {
|
4221
|
+
type: "subtensor-staking",
|
4222
|
+
hotkey,
|
4223
|
+
netuid,
|
4224
|
+
amountStaked: stake.toString(),
|
4225
|
+
alphaToTaoRate,
|
4226
|
+
dynamicInfo: {
|
4227
|
+
tokenSymbol,
|
4228
|
+
subnetName,
|
4229
|
+
subnetIdentity: {
|
4230
|
+
...subnetIdentity,
|
4231
|
+
subnetName: subnetIdentity?.subnet_name || subnetName
|
4232
|
+
}
|
4217
4233
|
}
|
4218
4234
|
}
|
4219
|
-
}
|
4220
|
-
}
|
4221
|
-
};
|
4222
|
-
|
4223
|
-
|
4224
|
-
|
4225
|
-
|
4226
|
-
|
4227
|
-
|
4228
|
-
|
4229
|
-
|
4230
|
-
|
4231
|
-
}));
|
4235
|
+
}]
|
4236
|
+
};
|
4237
|
+
})));
|
4238
|
+
|
4239
|
+
// This observable will run the subtensorQueries on a 30s (30_000ms) interval.
|
4240
|
+
// However, if the last run has not yet completed (e.g. its been 30s but we're still fetching some balances),
|
4241
|
+
// then exhaustMap will wait until the next interval (so T: 60s, T: 90s, T: 120s, etc) before re-executing the subtensorQueries.
|
4242
|
+
const subtensorQueriesInterval = rxjs.interval(30_000).pipe(rxjs.startWith(0),
|
4243
|
+
// start immediately
|
4244
|
+
rxjs.exhaustMap(() => {
|
4245
|
+
return subtensorQueries;
|
4246
|
+
}));
|
4232
4247
|
|
4233
|
-
|
4234
|
-
|
4235
|
-
|
4236
|
-
|
4237
|
-
|
4248
|
+
// subscribe to the balances
|
4249
|
+
const subscription = subtensorQueriesInterval.subscribe({
|
4250
|
+
next: balances => callback(null, balances),
|
4251
|
+
error: error => callback(error)
|
4252
|
+
});
|
4238
4253
|
|
4239
|
-
|
4240
|
-
|
4241
|
-
|
4254
|
+
// use the abortController to tear the subscription down when we don't need it anymore
|
4255
|
+
abortController.signal.addEventListener("abort", () => {
|
4256
|
+
subscription.unsubscribe();
|
4257
|
+
});
|
4258
|
+
}
|
4259
|
+
return () => abortController.abort();
|
4260
|
+
} catch (err) {
|
4261
|
+
if (!util.isAbortError(err)) log.error("Error subscribing to subtensor staking", {
|
4262
|
+
err
|
4242
4263
|
});
|
4264
|
+
return () => {};
|
4243
4265
|
}
|
4244
|
-
return () => abortController.abort();
|
4245
4266
|
}
|
4246
4267
|
|
4247
4268
|
const getOtherType = input => `other-${input}`;
|
@@ -4920,17 +4941,9 @@ const SubNativeModule = hydrate => {
|
|
4920
4941
|
if (!chainConnectors.substrate) return;
|
4921
4942
|
const unsubSubtensorStaking = subscribeSubtensorStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("subtensor-staking"), signal);
|
4922
4943
|
const unsubNompoolStaking = subscribeNompoolStaking(chaindataProvider$1, chainConnectors.substrate, newAddressesByToken, handleUpdateForSource("nompools-staking"), signal);
|
4923
|
-
// const unsubCrowdloans = subscribeCrowdloans(
|
4924
|
-
// chaindataProvider,
|
4925
|
-
// chainConnectors.substrate,
|
4926
|
-
// newAddressesByToken,
|
4927
|
-
// handleUpdateForSource("crowdloan"),
|
4928
|
-
// signal,
|
4929
|
-
// )
|
4930
4944
|
const unsubBase = subscribeBase(baseQueries, chainConnectors.substrate, handleUpdateForSource("base"));
|
4931
4945
|
subscriber.add(async () => (await unsubSubtensorStaking)());
|
4932
4946
|
subscriber.add(async () => (await unsubNompoolStaking)());
|
4933
|
-
// subscriber.add(async () => (await unsubCrowdloans)())
|
4934
4947
|
subscriber.add(async () => (await unsubBase)());
|
4935
4948
|
});
|
4936
4949
|
}));
|