Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 115 additions & 2 deletions pallets/subtensor/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use subtensor_runtime_common::{MechId, NetUid, TaoBalance};
use sp_api::ProvideRuntimeApi;

pub use subtensor_custom_rpc_runtime_api::{
DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, StakeInfoRuntimeApi, SubnetInfoRuntimeApi,
SubnetRegistrationRuntimeApi,
BetaBasketRuntimeApi, DelegateInfoRuntimeApi, NeuronInfoRuntimeApi, StakeInfoRuntimeApi,
SubnetInfoRuntimeApi, SubnetRegistrationRuntimeApi,
};

#[rpc(client, server)]
Expand Down Expand Up @@ -118,6 +118,38 @@ pub trait SubtensorCustomApi<BlockHash> {
netuid: NetUid,
at: Option<BlockHash>,
) -> RpcResult<Vec<u8>>;

/// Total TAO a staker (coldkey) would realize by redeeming all its root beta baskets.
#[method(name = "betaBasket_getStakerOwed")]
fn get_root_basket_owed(
&self,
coldkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<TaoBalance>;
/// A validator's beta basket net asset value, in TAO.
#[method(name = "betaBasket_getValidatorNav")]
fn get_validator_basket_nav(
&self,
hotkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<TaoBalance>;
/// A validator's full basket breakdown: SCALE-encoded `Vec<(NetUid, AlphaBalance, TaoBalance)>`.
#[method(name = "betaBasket_getValidatorBasket")]
fn get_validator_basket(
&self,
hotkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<Vec<u8>>;
/// Network-wide total beta basket NAV across all validators, in TAO.
#[method(name = "betaBasket_getTotalNav")]
fn get_root_basket_total_nav(&self, at: Option<BlockHash>) -> RpcResult<TaoBalance>;
/// A validator's basket weight vector: SCALE-encoded `Vec<(NetUid, u16)>` (its strategy).
#[method(name = "betaBasket_getValidatorWeights")]
fn get_validator_weights(
&self,
hotkey: AccountId32,
at: Option<BlockHash>,
) -> RpcResult<Vec<u8>>;
}

pub struct SubtensorCustom<C, P> {
Expand Down Expand Up @@ -167,6 +199,7 @@ where
C::Api: SubnetInfoRuntimeApi<Block>,
C::Api: StakeInfoRuntimeApi<Block>,
C::Api: SubnetRegistrationRuntimeApi<Block>,
C::Api: BetaBasketRuntimeApi<Block>,
{
fn get_delegates(&self, at: Option<<Block as BlockT>::Hash>) -> RpcResult<Vec<u8>> {
let api = self.client.runtime_api();
Expand Down Expand Up @@ -572,4 +605,84 @@ where
Err(e) => Err(Error::RuntimeError(format!("Unable to get coldkey lock: {e:?}")).into()),
}
}

fn get_root_basket_owed(
&self,
coldkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<TaoBalance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_root_basket_owed(at, coldkey) {
Ok(result) => Ok(result),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get root basket owed: {e:?}")).into())
}
}
}

fn get_validator_basket_nav(
&self,
hotkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<TaoBalance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_validator_basket_nav(at, hotkey) {
Ok(result) => Ok(result),
Err(e) => Err(Error::RuntimeError(format!(
"Unable to get validator basket NAV: {e:?}"
))
.into()),
}
}

fn get_validator_basket(
&self,
hotkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<u8>> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_validator_basket(at, hotkey) {
Ok(result) => Ok(result.encode()),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get validator basket: {e:?}")).into())
}
}
}

fn get_root_basket_total_nav(
&self,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<TaoBalance> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_root_basket_total_nav(at) {
Ok(result) => Ok(result),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get total basket NAV: {e:?}")).into())
}
}
}

fn get_validator_weights(
&self,
hotkey: AccountId32,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<u8>> {
let api = self.client.runtime_api();
let at = at.unwrap_or_else(|| self.client.info().best_hash);

match api.get_validator_weights(at, hotkey) {
Ok(result) => Ok(result.encode()),
Err(e) => {
Err(Error::RuntimeError(format!("Unable to get validator weights: {e:?}")).into())
}
}
}
}
13 changes: 13 additions & 0 deletions pallets/subtensor/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,17 @@ sp_api::decl_runtime_apis! {
fn get_proxy_types() -> Vec<ProxyTypeInfo>;
fn get_proxy_filter(proxy_type: Option<u8>) -> Vec<ProxyFilterInfo>;
}

pub trait BetaBasketRuntimeApi {
/// Total TAO a coldkey would realize by redeeming all its root beta baskets (marked).
fn get_root_basket_owed(coldkey: AccountId32) -> TaoBalance;
/// A validator's beta basket net asset value, in TAO (marked).
fn get_validator_basket_nav(hotkey: AccountId32) -> TaoBalance;
/// A validator's basket breakdown: (subnet, alpha held, TAO value) per subnet.
fn get_validator_basket(hotkey: AccountId32) -> Vec<(NetUid, AlphaBalance, TaoBalance)>;
/// Network-wide total beta basket NAV across all validators, in TAO (marked).
fn get_root_basket_total_nav() -> TaoBalance;
/// A validator's basket weight vector `w`: (subnet, weight) it deploys dividends into.
fn get_validator_weights(hotkey: AccountId32) -> Vec<(NetUid, u16)>;
}
}
49 changes: 23 additions & 26 deletions pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ use sp_runtime::{
BoundedVec, Percent,
traits::{BlakeTwo256, Hash},
};
use sp_std::collections::btree_set::BTreeSet;
use sp_std::vec;
use substrate_fixed::types::U64F64;
use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance};
use subtensor_runtime_common::{AlphaBalance, NetUid, NetUidStorageIndex, TaoBalance};
use subtensor_swap_interface::SwapHandler;

#[benchmarks(
Expand Down Expand Up @@ -1907,14 +1906,6 @@ mod pallet_benchmarks {
_(RawOrigin::Signed(coldkey.clone()), netuid, hotkey.clone());
}

#[benchmark]
fn set_root_claim_type() {
let coldkey: T::AccountId = whitelisted_caller();

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), RootClaimTypeEnum::Keep);
}

#[benchmark]
fn claim_root() {
let coldkey: T::AccountId = whitelisted_caller();
Expand Down Expand Up @@ -1957,6 +1948,16 @@ mod pallet_benchmarks {
initial_total_hotkey_alpha.into(),
);

// Point the validator's basket weight vector at the subnet so the distributed root
// dividend is deposited into its fund (instead of being recycled for lack of weights).
if let Ok(root_uid) = Uids::<T>::try_get(NetUid::ROOT, &hotkey) {
Weights::<T>::insert(
NetUidStorageIndex::ROOT,
root_uid,
vec![(u16::from(netuid), 1u16)],
);
}

let pending_root_alpha = 10_000_000u64;
Subtensor::<T>::distribute_emission(
netuid,
Expand All @@ -1966,29 +1967,25 @@ mod pallet_benchmarks {
AlphaBalance::ZERO,
);

let initial_stake =
Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);

assert_ok!(Subtensor::<T>::set_root_claim_type(
RawOrigin::Signed(coldkey.clone()).into(),
RootClaimTypeEnum::Keep
));
let initial_stake = Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&coldkey,
NetUid::ROOT,
);

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), BTreeSet::from([netuid]));
_(RawOrigin::Signed(coldkey.clone()));

let new_stake =
Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
let new_stake = Subtensor::<T>::get_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&coldkey,
NetUid::ROOT,
);

// The claim must actually pay out (strict: a no-op claim is a broken benchmark).
assert!(new_stake > initial_stake);
}

#[benchmark]
fn sudo_set_num_root_claims() {
#[extrinsic_call]
_(RawOrigin::Root, 40);
}

#[benchmark]
fn sudo_set_root_claim_threshold() {
let coldkey: T::AccountId = whitelisted_caller();
Expand Down
4 changes: 1 addition & 3 deletions pallets/subtensor/src/coinbase/block_step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ impl<T: Config + pallet_drand::Config> Pallet<T> {
/// Executes the necessary operations for each block.
pub fn block_step() -> Result<(), &'static str> {
let block_number: u64 = Self::get_current_block_as_u64();
let last_block_hash: T::Hash = <frame_system::Pallet<T>>::parent_hash();

// --- 1. Update registration burn prices.
Self::update_registration_prices_for_networks();
Expand All @@ -25,8 +24,7 @@ impl<T: Config + pallet_drand::Config> Pallet<T> {
Self::update_root_prop();
// --- 7. Set pending children on the epoch; but only after the coinbase has been run.
Self::try_set_pending_children(block_number);
// --- 8. Run auto-claim root divs.
Self::run_auto_claim_root_divs(last_block_hash);
// --- 8. Beta baskets are redeemed on-demand by stakers via `claim_root`; no auto-swap.
// --- 9. Populate root coldkey maps.
Self::populate_root_coldkey_staking_maps();
Self::populate_root_coldkey_staking_maps_v2();
Expand Down
2 changes: 1 addition & 1 deletion pallets/subtensor/src/coinbase/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl<T: Config> Pallet<T> {
Error::<T>::SubnetNotExists
);

Self::finalize_all_subnet_root_dividends(netuid);
Self::convert_subnet_basket_holdings_to_root(netuid);

// --- Perform the cleanup before removing the network.
Self::destroy_alpha_in_out_stakes(netuid)?;
Expand Down
10 changes: 5 additions & 5 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,11 +705,11 @@ impl<T: Config> Pallet<T> {
tou64!(alpha_take).into(),
);

Self::increase_root_claimable_for_hotkey_and_subnet(
&hotkey,
netuid,
tou64!(root_alpha).into(),
);
// Distribute the validator's root dividend into its beta basket across subnets
// per the validator's root weight vector (set on subnet 0). The bought basket
// alpha is staked to the validator under the global escrow coldkey, so it counts
// toward the validator's stake and compounds; stakers accrue a claimable rate.
Self::distribute_root_alpha_to_basket(&hotkey, netuid, tou64!(root_alpha).into());

// Record root alpha dividends for this validator on this subnet.
RootAlphaDividendsPerSubnet::<T>::mutate(netuid, &hotkey, |divs| {
Expand Down
Loading