Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2f7ea4b
Add root claim infrastructure
shamil-gadelshin Sep 9, 2025
c579597
Alter coinbase.rs
shamil-gadelshin Sep 12, 2025
6c614fe
Alter tests
shamil-gadelshin Sep 12, 2025
e04c8e9
Remove comments
shamil-gadelshin Sep 12, 2025
f607993
Change TaoDividendsPerSubnet -> AlphaDividendsPerSubnet
shamil-gadelshin Sep 12, 2025
8877c40
Fix: PendingAlphaSwapped only needs to increase for non-subsidized su…
gztensor Sep 12, 2025
e7605df
Update tests
shamil-gadelshin Sep 16, 2025
c48a9c4
Update coinbase.rs
shamil-gadelshin Sep 16, 2025
9fa484a
Cargo fmt
shamil-gadelshin Sep 16, 2025
7f5dba4
Remove TaoDividendsPerSubnet
shamil-gadelshin Sep 16, 2025
28e2f74
Remove PendingAlphaSwapped
shamil-gadelshin Sep 16, 2025
eb32237
Rename RootDebt -> RootClaimed
shamil-gadelshin Sep 16, 2025
2e613b0
Fix RootClaimable map
shamil-gadelshin Sep 17, 2025
b125673
Add root claim tests.
shamil-gadelshin Sep 17, 2025
0151f49
Add basic test with drain_emissions
shamil-gadelshin Sep 17, 2025
83b2b36
Update tests.
shamil-gadelshin Sep 17, 2025
c482f2f
Update tests
shamil-gadelshin Sep 18, 2025
55c1b1d
Simplify tests
shamil-gadelshin Sep 18, 2025
a11d7a9
Add disproportional stake test
shamil-gadelshin Sep 19, 2025
e3cc8e1
Add test with removed stake.
shamil-gadelshin Sep 19, 2025
dd44dd8
Fix existing tests
shamil-gadelshin Sep 19, 2025
0b13d2e
Update tests
shamil-gadelshin Sep 22, 2025
8d1140d
Fix swap claim root
shamil-gadelshin Sep 22, 2025
65bb4f0
Add basic run_coinbase test
shamil-gadelshin Sep 24, 2025
1accdfa
Fix block indices to claim
shamil-gadelshin Sep 25, 2025
c09bcb3
Optimize RootClaimable
shamil-gadelshin Sep 26, 2025
fc161b3
Update benchmarks
shamil-gadelshin Sep 29, 2025
b39d51c
Add coldkey to the new map.
shamil-gadelshin Sep 30, 2025
57bcae3
Add root staking maps migration.
shamil-gadelshin Sep 30, 2025
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
16 changes: 16 additions & 0 deletions pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1597,4 +1597,20 @@ mod pallet_benchmarks {
#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), hot.clone());
}
#[benchmark]
fn set_root_claim_type() {
let coldkey: T::AccountId = whitelisted_caller();

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

// TODO: rework after subnets argument
#[benchmark]
fn claim_root() {
let coldkey: T::AccountId = whitelisted_caller();

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()));
}
}
8 changes: 7 additions & 1 deletion pallets/subtensor/src/coinbase/block_step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ 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();
log::debug!("block_step for block: {block_number:?} ");
let last_block_hash: T::Hash = <frame_system::Pallet<T>>::parent_hash();

// --- 1. Adjust difficulties.
Self::adjust_registration_terms_for_networks();
// --- 2. Get the current coinbase emission.
Expand All @@ -21,6 +22,11 @@ impl<T: Config + pallet_drand::Config> Pallet<T> {
Self::run_coinbase(block_emission);
// --- 4. Set pending children on the epoch; but only after the coinbase has been run.
Self::try_set_pending_children(block_number);
// --- 5. Run auto-claim root divs.
Self::run_auto_claim_root_divs(last_block_hash);
// --- 6. Populate root coldkey maps.
Self::populate_root_coldkey_staking_maps();

// Return ok.
Ok(())
}
Expand Down
128 changes: 49 additions & 79 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,27 +214,14 @@ impl<T: Config> Pallet<T> {
// Get pending alpha as original alpha_out - root_alpha.
let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha);
log::debug!("pending_alpha: {pending_alpha:?}");
// Sell root emission through the pool (do not pay fees)

let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false);
if !subsidized {
let swap_result = Self::swap_alpha_for_tao(
*netuid_i,
tou64!(root_alpha).into(),
T::SwapInterface::min_price().into(),
true,
);
if let Ok(ok_result) = swap_result {
let root_tao: u64 = ok_result.amount_paid_out;
// Accumulate root divs for subnet.
PendingRootDivs::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(root_tao.into());
});
}
PendingRootAlphaDivs::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(tou64!(root_alpha).into());
});
}
// Accumulate alpha emission in pending.
PendingAlphaSwapped::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(tou64!(root_alpha).into());
});

// Accumulate alpha emission in pending.
PendingEmission::<T>::mutate(*netuid_i, |total| {
*total = total.saturating_add(tou64!(pending_alpha).into());
Expand Down Expand Up @@ -266,25 +253,15 @@ impl<T: Config> Pallet<T> {
PendingEmission::<T>::insert(netuid, AlphaCurrency::ZERO);

// Get and drain the subnet pending root divs.
let pending_tao = PendingRootDivs::<T>::get(netuid);
PendingRootDivs::<T>::insert(netuid, TaoCurrency::ZERO);

// Get this amount as alpha that was swapped for pending root divs.
let pending_swapped = PendingAlphaSwapped::<T>::get(netuid);
PendingAlphaSwapped::<T>::insert(netuid, AlphaCurrency::ZERO);
let pending_root_alpha = PendingRootAlphaDivs::<T>::get(netuid);
PendingRootAlphaDivs::<T>::insert(netuid, AlphaCurrency::ZERO);

// Get owner cut and drain.
let owner_cut = PendingOwnerCut::<T>::get(netuid);
PendingOwnerCut::<T>::insert(netuid, AlphaCurrency::ZERO);

// Drain pending root divs, alpha emission, and owner cut.
Self::drain_pending_emission(
netuid,
pending_alpha,
pending_tao,
pending_swapped,
owner_cut,
);
Self::drain_pending_emission(netuid, pending_alpha, pending_root_alpha, owner_cut);
} else {
// Increment
BlocksSinceLastStep::<T>::mutate(netuid, |total| *total = total.saturating_add(1));
Expand Down Expand Up @@ -327,7 +304,7 @@ impl<T: Config> Pallet<T> {

pub fn calculate_dividend_distribution(
pending_alpha: AlphaCurrency,
pending_tao: TaoCurrency,
pending_root_alpha: AlphaCurrency,
tao_weight: U96F32,
stake_map: BTreeMap<T::AccountId, (AlphaCurrency, AlphaCurrency)>,
dividends: BTreeMap<T::AccountId, U96F32>,
Expand All @@ -338,7 +315,7 @@ impl<T: Config> Pallet<T> {
log::debug!("dividends: {dividends:?}");
log::debug!("stake_map: {stake_map:?}");
log::debug!("pending_alpha: {pending_alpha:?}");
log::debug!("pending_tao: {pending_tao:?}");
log::debug!("pending_root_alpha: {pending_root_alpha:?}");
log::debug!("tao_weight: {tao_weight:?}");

// Setup.
Expand Down Expand Up @@ -390,22 +367,22 @@ impl<T: Config> Pallet<T> {
log::debug!("total_root_divs: {total_root_divs:?}");
log::debug!("total_alpha_divs: {total_alpha_divs:?}");

// Compute root divs as TAO. Here we take
let mut tao_dividends: BTreeMap<T::AccountId, U96F32> = BTreeMap::new();
// Compute root alpha divs. Here we take
let mut root_alpha_dividends: BTreeMap<T::AccountId, U96F32> = BTreeMap::new();
for (hotkey, root_divs) in root_dividends {
// Root proportion.
let root_share: U96F32 = root_divs.checked_div(total_root_divs).unwrap_or(zero);
log::debug!("hotkey: {hotkey:?}, root_share: {root_share:?}");
// Root proportion in TAO
let root_tao: U96F32 = asfloat!(pending_tao).saturating_mul(root_share);
log::debug!("hotkey: {hotkey:?}, root_tao: {root_tao:?}");
// Root proportion in alpha
let root_alpha: U96F32 = asfloat!(pending_root_alpha).saturating_mul(root_share);
log::debug!("hotkey: {hotkey:?}, root_alpha: {root_alpha:?}");
// Record root dividends as TAO.
tao_dividends
root_alpha_dividends
.entry(hotkey)
.and_modify(|e| *e = root_tao)
.or_insert(root_tao);
.and_modify(|e| *e = root_alpha)
.or_insert(root_alpha);
}
log::debug!("tao_dividends: {tao_dividends:?}");
log::debug!("root_alpha_dividends: {root_alpha_dividends:?}");

// Compute proportional alpha divs using the pending alpha and total alpha divs from the epoch.
let mut prop_alpha_dividends: BTreeMap<T::AccountId, U96F32> = BTreeMap::new();
Expand All @@ -425,7 +402,7 @@ impl<T: Config> Pallet<T> {
}
log::debug!("prop_alpha_dividends: {prop_alpha_dividends:?}");

(prop_alpha_dividends, tao_dividends)
(prop_alpha_dividends, root_alpha_dividends)
}

fn get_immune_owner_hotkeys(netuid: NetUid, coldkey: &T::AccountId) -> Vec<T::AccountId> {
Expand Down Expand Up @@ -474,7 +451,7 @@ impl<T: Config> Pallet<T> {
owner_cut: AlphaCurrency,
incentives: BTreeMap<T::AccountId, AlphaCurrency>,
alpha_dividends: BTreeMap<T::AccountId, U96F32>,
tao_dividends: BTreeMap<T::AccountId, U96F32>,
root_alpha_dividends: BTreeMap<T::AccountId, U96F32>,
) {
// Distribute the owner cut.
if let Ok(owner_coldkey) = SubnetOwner::<T>::try_get(netuid) {
Expand Down Expand Up @@ -550,37 +527,31 @@ impl<T: Config> Pallet<T> {
TotalHotkeyAlphaLastEpoch::<T>::insert(hotkey, netuid, total_hotkey_alpha);
}

// Distribute root tao divs.
let _ = TaoDividendsPerSubnet::<T>::clear_prefix(netuid, u32::MAX, None);
for (hotkey, mut root_tao) in tao_dividends {
// Distribute root alpha divs.
for (hotkey, mut root_alpha) in root_alpha_dividends {
// Get take prop
let tao_take: U96F32 = Self::get_hotkey_take_float(&hotkey).saturating_mul(root_tao);
let alpha_take: U96F32 =
Self::get_hotkey_take_float(&hotkey).saturating_mul(root_alpha);
// Remove take prop from root_tao
root_tao = root_tao.saturating_sub(tao_take);
root_alpha = root_alpha.saturating_sub(alpha_take);
// Give the validator their take.
log::debug!("hotkey: {hotkey:?} tao_take: {tao_take:?}");
let validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
log::debug!("hotkey: {hotkey:?} alpha_take: {alpha_take:?}");
let _validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&Owner::<T>::get(hotkey.clone()),
NetUid::ROOT,
tou64!(tao_take).into(),
netuid,
tou64!(alpha_take).into(),
);
// Give rest to nominators.
log::debug!("hotkey: {hotkey:?} root_tao: {root_tao:?}");
Self::increase_stake_for_hotkey_on_subnet(

Self::increase_root_claimable_for_hotkey_and_subnet(
&hotkey,
NetUid::ROOT,
tou64!(root_tao).into(),
netuid,
tou64!(root_alpha).into(),
);

// Record root dividends for this validator on this subnet.
TaoDividendsPerSubnet::<T>::mutate(netuid, hotkey.clone(), |divs| {
*divs = divs.saturating_add(tou64!(root_tao).into());
});
// Update the total TAO on the subnet with root tao dividends.
SubnetTAO::<T>::mutate(NetUid::ROOT, |total| {
*total = total
.saturating_add(validator_stake.to_u64().into())
.saturating_add(tou64!(root_tao).into());
AlphaDividendsPerSubnet::<T>::mutate(netuid, hotkey.clone(), |divs| {
*divs = divs.saturating_add(tou64!(root_alpha).into());
});
}
}
Expand All @@ -602,7 +573,7 @@ impl<T: Config> Pallet<T> {

pub fn calculate_dividend_and_incentive_distribution(
netuid: NetUid,
pending_tao: TaoCurrency,
pending_root_alpha: AlphaCurrency,
pending_validator_alpha: AlphaCurrency,
hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)>,
tao_weight: U96F32,
Expand All @@ -618,33 +589,32 @@ impl<T: Config> Pallet<T> {

let stake_map = Self::get_stake_map(netuid, dividends.keys().collect::<Vec<_>>());

let (alpha_dividends, tao_dividends) = Self::calculate_dividend_distribution(
let (alpha_dividends, root_alpha_dividends) = Self::calculate_dividend_distribution(
pending_validator_alpha,
pending_tao,
pending_root_alpha,
tao_weight,
stake_map,
dividends,
);

(incentives, (alpha_dividends, tao_dividends))
(incentives, (alpha_dividends, root_alpha_dividends))
}

pub fn drain_pending_emission(
netuid: NetUid,
pending_alpha: AlphaCurrency,
pending_tao: TaoCurrency,
pending_swapped: AlphaCurrency,
pending_root_alpha: AlphaCurrency,
owner_cut: AlphaCurrency,
) {
log::debug!(
"Draining pending alpha emission for netuid {netuid:?}, pending_alpha: {pending_alpha:?}, pending_tao: {pending_tao:?}, pending_swapped: {pending_swapped:?}, owner_cut: {owner_cut:?}"
"Draining pending alpha emission for netuid {netuid:?}, pending_alpha: {pending_alpha:?}, pending_root_alpha: {pending_root_alpha:?}, owner_cut: {owner_cut:?}"
);

let tao_weight = Self::get_tao_weight();

// Run the epoch.
let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> =
Self::epoch(netuid, pending_alpha.saturating_add(pending_swapped));
Self::epoch(netuid, pending_alpha.saturating_add(pending_root_alpha));
log::debug!("hotkey_emission: {hotkey_emission:?}");

// Compute the pending validator alpha.
Expand All @@ -661,18 +631,18 @@ impl<T: Config> Pallet<T> {

let pending_validator_alpha = if !incentive_sum.is_zero() {
pending_alpha
.saturating_add(pending_swapped)
.saturating_add(pending_root_alpha)
.saturating_div(2.into())
.saturating_sub(pending_swapped)
.saturating_sub(pending_root_alpha)
} else {
// If the incentive is 0, then Validators get 100% of the alpha.
pending_alpha
};

let (incentives, (alpha_dividends, tao_dividends)) =
let (incentives, (alpha_dividends, root_alpha_dividends)) =
Self::calculate_dividend_and_incentive_distribution(
netuid,
pending_tao,
pending_root_alpha,
pending_validator_alpha,
hotkey_emission,
tao_weight,
Expand All @@ -683,7 +653,7 @@ impl<T: Config> Pallet<T> {
owner_cut,
incentives,
alpha_dividends,
tao_dividends,
root_alpha_dividends,
);
}

Expand Down
Loading