From 033e7022dd6a640c13d9de2e46138b6e18bb0603 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 10:26:04 -0500 Subject: [PATCH 01/18] allow to set min burn and max burn --- pallets/admin-utils/src/lib.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 03a20a5f21..0b1498fd44 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -107,6 +107,8 @@ pub mod pallet { BondsMovingAverageMaxReached, /// Only root can set negative sigmoid steepness values NegativeSigmoidSteepness, + /// Value not in allowed bounds. + ValueNotInBounds, } /// Enum for specifying the type of precompile operation. #[derive( @@ -665,12 +667,22 @@ pub mod pallet { netuid: NetUid, min_burn: TaoCurrency, ) -> DispatchResult { - ensure_root(origin)?; - + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; + // Allow set min_burn but only up to 1 TAO for spamming. ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); + // Min burn must be less than 1 TAO. + ensure!( + min_burn < TaoCurrency::from(1_000_000_000), + Error::::ValueNotInBounds + ) + // Min burn must be less than max burn + ensure!( + min_burn > pallet_subtensor::Pallet::::MaxBurn(netuid), + Error::::ValueNotInBounds + ) pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); log::debug!("MinBurnSet( netuid: {netuid:?} min_burn: {min_burn:?} ) "); Ok(()) @@ -688,12 +700,21 @@ pub mod pallet { netuid: NetUid, max_burn: TaoCurrency, ) -> DispatchResult { - ensure_root(origin)?; - + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); + // Max burn must be greater than 0.1 TAO. + ensure!( + max_burn > TaoCurrency::from(100_000_000), + Error::::ValueNotInBounds + ) + // Max burn must be greater than min burn + ensure!( + max_burn > pallet_subtensor::Pallet::::MinBurn(netuid), + Error::::ValueNotInBounds + ) pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); log::debug!("MaxBurnSet( netuid: {netuid:?} max_burn: {max_burn:?} ) "); Ok(()) From 53a2d104b8fe7ac9a5368804f91ac7b5dd70c9a3 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 10:59:34 -0500 Subject: [PATCH 02/18] ckburn added --- pallets/admin-utils/src/lib.rs | 18 ++++++++++ .../subtensor/src/coinbase/run_coinbase.rs | 33 ++++++++----------- pallets/subtensor/src/lib.rs | 3 ++ pallets/subtensor/src/staking/stake_utils.rs | 10 ++++++ precompiles/src/alpha.rs | 7 ++++ 5 files changed, 52 insertions(+), 19 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 0b1498fd44..cb0dec173e 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1703,6 +1703,24 @@ pub mod pallet { pallet_subtensor::Pallet::::set_owner_immune_neuron_limit(netuid, immune_neurons)?; Ok(()) } + + /// The extrinsic sets the difficulty for a subnet. + /// It is only callable by the root account or subnet owner. + /// The extrinsic will call the Subtensor pallet to set the difficulty. + #[pallet::call_index(73)] + #[pallet::weight(Weight::from_parts(15_650_000, 0) + .saturating_add(::DbWeight::get().reads(1_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] + pub fn sudo_set_ck_burn( + origin: OriginFor, + netuid: NetUid, + burn: u64, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_ck_burn( burn ); + log::debug!("CKBurnSet( burn: {burn:?} ) "); + Ok(()) + } } } diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 73fc928e90..cc20a72219 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -743,10 +743,11 @@ impl Pallet { // Calculate the hotkey's share of the validator emission based on its childkey take let validating_emission: U96F32 = U96F32::saturating_from_num(dividends); let mut remaining_emission: U96F32 = validating_emission; - let childkey_take_proportion: U96F32 = + let burn_take_proportion: U96F32 = Self::get_ck_burn_proportion(); + let child_take_proportion: U96F32 = U96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid)) .safe_div(U96F32::saturating_from_num(u16::MAX)); - log::debug!("Childkey take proportion: {childkey_take_proportion:?} for hotkey {hotkey:?}"); + log::debug!("Childkey take proportion: {child_take_proportion:?} for hotkey {hotkey:?}"); // NOTE: Only the validation emission should be split amongst parents. // Grab the owner of the childkey. @@ -754,7 +755,7 @@ impl Pallet { // Initialize variables to track emission distribution let mut to_parents: u64 = 0; - let mut total_child_emission_take: U96F32 = U96F32::saturating_from_num(0); + let mut total_child_take: U96F32 = U96F32::saturating_from_num(0); // Initialize variables to calculate total stakes from parents let mut total_contribution: U96F32 = U96F32::saturating_from_num(0); @@ -818,21 +819,16 @@ impl Pallet { remaining_emission = remaining_emission.saturating_sub(parent_emission); // Get the childkey take for this parent. - let child_emission_take: U96F32 = if parent_owner == childkey_owner { - // The parent is from the same coldkey, so we don't remove any childkey take. - U96F32::saturating_from_num(0) - } else { - childkey_take_proportion - .saturating_mul(U96F32::saturating_from_num(parent_emission)) + let mut burn_take: U96F32 = U96F32::saturating_from_num(0); + let mut child_take: U96F32 = U96F32::saturating_from_num(0); + if parent_owner != childkey_owner { + // The parent is from a different coldkey, we burn some proportion + burn_take = burn_take_proportion * parent_emission; + child_take = child_take_proportion * parent_emission; + parent_emission = parent_emission.saturating_sub(burn_take); + parent_emission = parent_emission.saturating_sub(child_take); + total_child_take = total_child_take.saturating_add(child_take); }; - - // Remove the childkey take from the parent's emission. - parent_emission = parent_emission.saturating_sub(child_emission_take); - - // Add the childkey take to the total childkey take tracker. - total_child_emission_take = - total_child_emission_take.saturating_add(child_emission_take); - log::debug!("Child emission take: {child_emission_take:?} for hotkey {hotkey:?}"); log::debug!("Parent emission: {parent_emission:?} for hotkey {hotkey:?}"); log::debug!("remaining emission: {remaining_emission:?}"); @@ -852,13 +848,12 @@ impl Pallet { // Calculate the final emission for the hotkey itself. // This includes the take left from the parents and the self contribution. let child_emission = remaining_emission - .saturating_add(total_child_emission_take) + .saturating_add(total_child_take) .saturating_to_num::() .into(); // Add the hotkey's own emission to the distribution list dividend_tuples.push((hotkey.clone(), child_emission)); - dividend_tuples } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ef41b07c78..35435c8a83 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -920,6 +920,9 @@ pub mod pallet { /// --- ITEM --> Global weight pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; #[pallet::storage] + /// --- ITEM --> CK burn + pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; + #[pallet::storage] /// --- ITEM ( default_delegate_take ) pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; #[pallet::storage] diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 8dff984099..84485b74a8 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -96,6 +96,11 @@ impl Pallet { // This ensures the result is always between 0 and 1 weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX)) } + pub fn get_ck_burn() -> U96F32 { + let stored_weight = CKBurn::::get(); + let weight_fixed = U96F32::saturating_from_num(stored_weight); + weight_fixed.safe_div(U96F32::saturating_from_num(u64::MAX)) + } /// Sets the global global weight in storage. /// @@ -117,6 +122,11 @@ impl Pallet { // Update the TaoWeight storage with the new weight value TaoWeight::::set(weight); } + // Set the amount burned on non owned CK + pub fn set_ck_burn(weight: u64) { + // Update the ck burn value. + CKBurn::::set(weight); + } /// Calculates the weighted combination of alpha and global tao for a single hotkey onet a subnet. /// diff --git a/precompiles/src/alpha.rs b/precompiles/src/alpha.rs index 42261674d9..29f7cab568 100644 --- a/precompiles/src/alpha.rs +++ b/precompiles/src/alpha.rs @@ -90,6 +90,13 @@ where Ok(U256::from(tao_weight)) } + #[precompile::public("getCKBurn()")] + #[precompile::view] + fn get_ck_burn(_handle: &mut impl PrecompileHandle) -> EvmResult { + let ck_burn = pallet_subtensor::CKBurn::::get(); + Ok(U256::from(ck_burn)) + } + #[precompile::public("simSwapTaoForAlpha(uint16,uint64)")] #[precompile::view] fn sim_swap_tao_for_alpha( From b42a765184d6dd2a8596f981d0ebb88e6be35eba Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 11:51:39 -0500 Subject: [PATCH 03/18] burn take subtracts fro alpha out --- pallets/subtensor/src/coinbase/run_coinbase.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index cc20a72219..60caf9a3db 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -743,7 +743,7 @@ impl Pallet { // Calculate the hotkey's share of the validator emission based on its childkey take let validating_emission: U96F32 = U96F32::saturating_from_num(dividends); let mut remaining_emission: U96F32 = validating_emission; - let burn_take_proportion: U96F32 = Self::get_ck_burn_proportion(); + let burn_take_proportion: U96F32 = Self::get_ck_burn(); let child_take_proportion: U96F32 = U96F32::saturating_from_num(Self::get_childkey_take(hotkey, netuid)) .safe_div(U96F32::saturating_from_num(u16::MAX)); @@ -828,6 +828,7 @@ impl Pallet { parent_emission = parent_emission.saturating_sub(burn_take); parent_emission = parent_emission.saturating_sub(child_take); total_child_take = total_child_take.saturating_add(child_take); + SubnetAlphaOut::::mutate( |total| *total = total.saturating_sub(burn_take) ); }; log::debug!("Child emission take: {child_emission_take:?} for hotkey {hotkey:?}"); log::debug!("Parent emission: {parent_emission:?} for hotkey {hotkey:?}"); From cdc7a22f9f3d3cccfd6b9bc18af4036b825002a5 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 12:17:10 -0500 Subject: [PATCH 04/18] commit Cargo.lock --- pallets/admin-utils/src/lib.rs | 13 ++++++------- pallets/subtensor/src/coinbase/run_coinbase.rs | 9 ++++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index cb0dec173e..d363490f16 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -677,12 +677,12 @@ pub mod pallet { ensure!( min_burn < TaoCurrency::from(1_000_000_000), Error::::ValueNotInBounds - ) + ); // Min burn must be less than max burn ensure!( - min_burn > pallet_subtensor::Pallet::::MaxBurn(netuid), + min_burn > pallet_subtensor::Pallet::::get_max_burn(netuid), Error::::ValueNotInBounds - ) + ); pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); log::debug!("MinBurnSet( netuid: {netuid:?} min_burn: {min_burn:?} ) "); Ok(()) @@ -709,12 +709,12 @@ pub mod pallet { ensure!( max_burn > TaoCurrency::from(100_000_000), Error::::ValueNotInBounds - ) + ); // Max burn must be greater than min burn ensure!( - max_burn > pallet_subtensor::Pallet::::MinBurn(netuid), + max_burn > pallet_subtensor::Pallet::::get_min_burn(netuid), Error::::ValueNotInBounds - ) + ); pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); log::debug!("MaxBurnSet( netuid: {netuid:?} max_burn: {max_burn:?} ) "); Ok(()) @@ -1713,7 +1713,6 @@ pub mod pallet { .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_ck_burn( origin: OriginFor, - netuid: NetUid, burn: u64, ) -> DispatchResult { ensure_root(origin)?; diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 60caf9a3db..ec3a6ff287 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -828,10 +828,13 @@ impl Pallet { parent_emission = parent_emission.saturating_sub(burn_take); parent_emission = parent_emission.saturating_sub(child_take); total_child_take = total_child_take.saturating_add(child_take); - SubnetAlphaOut::::mutate( |total| *total = total.saturating_sub(burn_take) ); + SubnetAlphaOut::::mutate( netuid, |total| { *total = total.saturating_sub( AlphaCurrency::from(burn_take.saturating_to_num::()) ); }); }; - log::debug!("Child emission take: {child_emission_take:?} for hotkey {hotkey:?}"); - log::debug!("Parent emission: {parent_emission:?} for hotkey {hotkey:?}"); + log::debug!("burn_takee: {burn_take:?} for hotkey {hotkey:?}"); + log::debug!("child_take: {child_take:?} for hotkey {hotkey:?}"); + log::debug!("parent_emission: {parent_emission:?} for hotkey {hotkey:?}"); + log::debug!("total_child_take: {total_child_take:?} for hotkey {hotkey:?}"); + log::debug!("remaining emission: {remaining_emission:?}"); // Add the parent's emission to the distribution list From 167b1cce083142f8231fe2f473fb1c263c091832 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 12:18:23 -0500 Subject: [PATCH 05/18] commit Cargo.lock --- pallets/subtensor/src/coinbase/run_coinbase.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index ec3a6ff287..a84c2b29fb 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -823,8 +823,8 @@ impl Pallet { let mut child_take: U96F32 = U96F32::saturating_from_num(0); if parent_owner != childkey_owner { // The parent is from a different coldkey, we burn some proportion - burn_take = burn_take_proportion * parent_emission; - child_take = child_take_proportion * parent_emission; + burn_take = burn_take_proportion.saturating_mul(parent_emission); + child_take = child_take_proportion.saturating_mul(parent_emission); parent_emission = parent_emission.saturating_sub(burn_take); parent_emission = parent_emission.saturating_sub(child_take); total_child_take = total_child_take.saturating_add(child_take); From 505b3580d374315eb79da4e388a9f308bfba3951 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 12:20:31 -0500 Subject: [PATCH 06/18] cargo fmt --- pallets/admin-utils/src/lib.rs | 7 ++----- pallets/subtensor/src/coinbase/run_coinbase.rs | 5 ++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index d363490f16..babcfb1225 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1711,12 +1711,9 @@ pub mod pallet { #[pallet::weight(Weight::from_parts(15_650_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] - pub fn sudo_set_ck_burn( - origin: OriginFor, - burn: u64, - ) -> DispatchResult { + pub fn sudo_set_ck_burn(origin: OriginFor, burn: u64) -> DispatchResult { ensure_root(origin)?; - pallet_subtensor::Pallet::::set_ck_burn( burn ); + pallet_subtensor::Pallet::::set_ck_burn(burn); log::debug!("CKBurnSet( burn: {burn:?} ) "); Ok(()) } diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a84c2b29fb..c029f0ed13 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -828,7 +828,10 @@ impl Pallet { parent_emission = parent_emission.saturating_sub(burn_take); parent_emission = parent_emission.saturating_sub(child_take); total_child_take = total_child_take.saturating_add(child_take); - SubnetAlphaOut::::mutate( netuid, |total| { *total = total.saturating_sub( AlphaCurrency::from(burn_take.saturating_to_num::()) ); }); + SubnetAlphaOut::::mutate(netuid, |total| { + *total = total + .saturating_sub(AlphaCurrency::from(burn_take.saturating_to_num::())); + }); }; log::debug!("burn_takee: {burn_take:?} for hotkey {hotkey:?}"); log::debug!("child_take: {child_take:?} for hotkey {hotkey:?}"); From 4763b867cdc797a9212792b7526f25617ac1cdd7 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 2 Sep 2025 10:49:00 -0400 Subject: [PATCH 07/18] Fix wrong way ensure for min value --- pallets/admin-utils/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index babcfb1225..2587eada62 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -680,7 +680,7 @@ pub mod pallet { ); // Min burn must be less than max burn ensure!( - min_burn > pallet_subtensor::Pallet::::get_max_burn(netuid), + min_burn <= pallet_subtensor::Pallet::::get_max_burn(netuid), Error::::ValueNotInBounds ); pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); @@ -712,7 +712,7 @@ pub mod pallet { ); // Max burn must be greater than min burn ensure!( - max_burn > pallet_subtensor::Pallet::::get_min_burn(netuid), + max_burn >= pallet_subtensor::Pallet::::get_min_burn(netuid), Error::::ValueNotInBounds ); pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); From b10087d3303e0f1a5ccb2661c9c988b493029564 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 2 Sep 2025 22:52:20 +0800 Subject: [PATCH 08/18] fix conflict --- pallets/admin-utils/src/lib.rs | 35 ++++--------------- .../subtensor/src/coinbase/run_coinbase.rs | 9 ++--- pallets/subtensor/src/staking/helpers.rs | 6 ++++ 3 files changed, 18 insertions(+), 32 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 2587eada62..0a183fcea1 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -107,8 +107,6 @@ pub mod pallet { BondsMovingAverageMaxReached, /// Only root can set negative sigmoid steepness values NegativeSigmoidSteepness, - /// Value not in allowed bounds. - ValueNotInBounds, } /// Enum for specifying the type of precompile operation. #[derive( @@ -667,22 +665,12 @@ pub mod pallet { netuid: NetUid, min_burn: TaoCurrency, ) -> DispatchResult { - pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; - // Allow set min_burn but only up to 1 TAO for spamming. + ensure_root(origin)?; + ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - // Min burn must be less than 1 TAO. - ensure!( - min_burn < TaoCurrency::from(1_000_000_000), - Error::::ValueNotInBounds - ); - // Min burn must be less than max burn - ensure!( - min_burn <= pallet_subtensor::Pallet::::get_max_burn(netuid), - Error::::ValueNotInBounds - ); pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); log::debug!("MinBurnSet( netuid: {netuid:?} min_burn: {min_burn:?} ) "); Ok(()) @@ -700,21 +688,12 @@ pub mod pallet { netuid: NetUid, max_burn: TaoCurrency, ) -> DispatchResult { - pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; + ensure_root(origin)?; + ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - // Max burn must be greater than 0.1 TAO. - ensure!( - max_burn > TaoCurrency::from(100_000_000), - Error::::ValueNotInBounds - ); - // Max burn must be greater than min burn - ensure!( - max_burn >= pallet_subtensor::Pallet::::get_min_burn(netuid), - Error::::ValueNotInBounds - ); pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); log::debug!("MaxBurnSet( netuid: {netuid:?} max_burn: {max_burn:?} ) "); Ok(()) @@ -1704,9 +1683,9 @@ pub mod pallet { Ok(()) } - /// The extrinsic sets the difficulty for a subnet. - /// It is only callable by the root account or subnet owner. - /// The extrinsic will call the Subtensor pallet to set the difficulty. + /// Sets the childkey burn for a subnet. + /// It is only callable by the root account. + /// The extrinsic will call the Subtensor pallet to set the childkey burn. #[pallet::call_index(73)] #[pallet::weight(Weight::from_parts(15_650_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index c029f0ed13..0379494710 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -828,10 +828,11 @@ impl Pallet { parent_emission = parent_emission.saturating_sub(burn_take); parent_emission = parent_emission.saturating_sub(child_take); total_child_take = total_child_take.saturating_add(child_take); - SubnetAlphaOut::::mutate(netuid, |total| { - *total = total - .saturating_sub(AlphaCurrency::from(burn_take.saturating_to_num::())); - }); + + Self::burn_subnet_alpha( + netuid, + AlphaCurrency::from(burn_take.saturating_to_num::()), + ); }; log::debug!("burn_takee: {burn_take:?} for hotkey {hotkey:?}"); log::debug!("child_take: {child_take:?} for hotkey {hotkey:?}"); diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 3f07115b7b..c89a762f91 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -321,4 +321,10 @@ impl Pallet { pub fn is_user_liquidity_enabled(netuid: NetUid) -> bool { T::SwapInterface::is_user_liquidity_enabled(netuid) } + + pub fn burn_subnet_alpha(netuid: NetUid, amount: AlphaCurrency) { + SubnetAlphaOut::::mutate(netuid, |total| { + *total = total.saturating_sub(amount); + }); + } } From 17acaae18c172bb199d015e8d369e4126e3ef20d Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 2 Sep 2025 23:09:07 +0800 Subject: [PATCH 09/18] default ck burn --- pallets/subtensor/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 35435c8a83..057126e13e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -867,6 +867,12 @@ pub mod pallet { 50400 } + #[pallet::type_value] + /// Default value for ck burn, 18%. + pub fn DefaultCKBurn() -> u64 { + u64::MAX / 18 + } + #[pallet::storage] pub type MinActivityCutoff = StorageValue<_, u16, ValueQuery, DefaultMinActivityCutoff>; @@ -921,7 +927,7 @@ pub mod pallet { pub type TaoWeight = StorageValue<_, u64, ValueQuery, DefaultTaoWeight>; #[pallet::storage] /// --- ITEM --> CK burn - pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; + pub type CKBurn = StorageValue<_, u64, ValueQuery, DefaultCKBurn>; #[pallet::storage] /// --- ITEM ( default_delegate_take ) pub type MaxDelegateTake = StorageValue<_, u16, ValueQuery, DefaultDelegateTake>; From ae25b5f53d11cdf6db6e8a2c1c16eb805886568b Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 2 Sep 2025 23:19:10 +0800 Subject: [PATCH 10/18] bump version --- pallets/subtensor/src/coinbase/run_coinbase.rs | 1 + runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 0379494710..b026f73988 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -862,6 +862,7 @@ impl Pallet { // Add the hotkey's own emission to the distribution list dividend_tuples.push((hotkey.clone(), child_emission)); + dividend_tuples } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 00cc24b411..679e5e55b2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 307, + spec_version: 308, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From f08bc1ea5d448493b1161afc808423e07c5a11ed Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 00:15:46 +0800 Subject: [PATCH 11/18] fix unit tests --- pallets/subtensor/src/tests/children.rs | 5 +++++ pallets/subtensor/src/tests/coinbase.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 2ffc21ea1d..a0505fa9f3 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -2861,6 +2861,7 @@ fn test_childkey_take_drain() { // Add network, register hotkeys, and setup network parameters add_network(netuid, subnet_tempo, 0); + SubtensorModule::set_ck_burn(0); mock::setup_reserves(netuid, (stake * 10_000).into(), (stake * 10_000).into()); register_ok_neuron(netuid, child_hotkey, child_coldkey, 0); register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 1); @@ -2980,6 +2981,7 @@ fn test_parent_child_chain_emission() { let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + SubtensorModule::set_ck_burn(0); Tempo::::insert(netuid, 1); // Setup large LPs to prevent slippage @@ -3192,6 +3194,7 @@ fn test_parent_child_chain_epoch() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); + SubtensorModule::set_ck_burn(0); // Set owner cut to 0 SubtensorModule::set_subnet_owner_cut(0_u16); @@ -3336,6 +3339,7 @@ fn test_dividend_distribution_with_children() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); + SubtensorModule::set_ck_burn(0); mock::setup_reserves( netuid, 1_000_000_000_000_000.into(), @@ -3570,6 +3574,7 @@ fn test_dividend_distribution_with_children() { fn test_dynamic_parent_child_relationships() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); + SubtensorModule::set_ck_burn(0); add_network_disable_commit_reveal(netuid, 1, 0); // Define hotkeys and coldkeys diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 927f98e2fa..5b0f7316b0 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -1063,6 +1063,7 @@ fn test_drain_alpha_childkey_parentkey() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); + SubtensorModule::set_ck_burn(0); let parent = U256::from(1); let child = U256::from(2); let coldkey = U256::from(3); @@ -1238,6 +1239,7 @@ fn test_get_root_children_drain() { let alpha = NetUid::from(1); add_network(NetUid::ROOT, 1, 0); add_network(alpha, 1, 0); + SubtensorModule::set_ck_burn(0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. // Create keys. @@ -1399,6 +1401,7 @@ fn test_get_root_children_drain_half_proportion() { let alpha = NetUid::from(1); add_network(NetUid::ROOT, 1, 0); add_network(alpha, 1, 0); + SubtensorModule::set_ck_burn(0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. // Create keys. @@ -1576,6 +1579,7 @@ fn test_get_root_children_drain_with_half_take() { add_network(alpha, 1, 0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. + SubtensorModule::set_ck_burn(0); // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); From 2451f18b36e7cc38a62ab5038b670344280fb80c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 2 Sep 2025 18:12:34 +0000 Subject: [PATCH 12/18] auto-update benchmark weights --- pallets/subtensor/src/macros/dispatches.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index aed0c150de..6c89dfcfca 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -120,9 +120,9 @@ mod dispatches { /// - On failure for each failed item in the batch. /// #[pallet::call_index(80)] - #[pallet::weight((Weight::from_parts(95_160_000, 0) - .saturating_add(T::DbWeight::get().reads(14)) - .saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))] + #[pallet::weight((Weight::from_parts(19_010_000, 0) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(0_u64)), DispatchClass::Normal, Pays::No))] pub fn batch_set_weights( origin: OriginFor, netuids: Vec>, From df5e183e21d0dd42dafdfae51a9d6e3ca1e83093 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 08:07:43 +0800 Subject: [PATCH 13/18] bump version --- pallets/subtensor/src/lib.rs | 2 +- runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 057126e13e..e606144170 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -870,7 +870,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for ck burn, 18%. pub fn DefaultCKBurn() -> u64 { - u64::MAX / 18 + u64::MAX / 100 * 18 } #[pallet::storage] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f0dce12ca0..55dd9fb115 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 308, + spec_version: 309, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From addca32e0ddf0e41891f2694c23c97bca0c0f751 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 10:00:02 +0800 Subject: [PATCH 14/18] add e2e case --- evm-tests/src/contracts/alpha.ts | 13 +++++++++++++ evm-tests/test/alpha.precompile.test.ts | 15 +++++++++++++++ precompiles/src/solidity/alpha.abi | 13 +++++++++++++ precompiles/src/solidity/alpha.sol | 4 ++++ 4 files changed, 45 insertions(+) diff --git a/evm-tests/src/contracts/alpha.ts b/evm-tests/src/contracts/alpha.ts index a87702eb80..ae24298048 100644 --- a/evm-tests/src/contracts/alpha.ts +++ b/evm-tests/src/contracts/alpha.ts @@ -315,5 +315,18 @@ export const IAlphaABI = [ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [], + "name": "getCKBurn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" } ] \ No newline at end of file diff --git a/evm-tests/test/alpha.precompile.test.ts b/evm-tests/test/alpha.precompile.test.ts index 31b31da135..1982d1ea87 100644 --- a/evm-tests/test/alpha.precompile.test.ts +++ b/evm-tests/test/alpha.precompile.test.ts @@ -209,6 +209,21 @@ describe("Test Alpha Precompile", () => { assert.ok(typeof alphaIssuance === 'bigint', "Alpha issuance should be a bigint"); assert.ok(alphaIssuance >= BigInt(0), "Alpha issuance should be non-negative"); }); + + it("getCKBurn returns valid CK burn rate", async () => { + const ckBurn = await publicClient.readContract({ + abi: IAlphaABI, + address: toViemAddress(IALPHA_ADDRESS), + functionName: "getCKBurn", + args: [] + }) + + const ckBurnOnChain = await api.query.SubtensorModule.CKBurn.getValue() + + assert.strictEqual(ckBurn, ckBurnOnChain, "CK burn should match on chain"); + assert.ok(ckBurn !== undefined, "CK burn should be defined"); + assert.ok(typeof ckBurn === 'bigint', "CK burn should be a bigint"); + }); }); describe("Global Functions", () => { diff --git a/precompiles/src/solidity/alpha.abi b/precompiles/src/solidity/alpha.abi index 06975d5e61..14d6eb66dc 100644 --- a/precompiles/src/solidity/alpha.abi +++ b/precompiles/src/solidity/alpha.abi @@ -313,5 +313,18 @@ ], "stateMutability": "view", "type": "function" + }, + { + "inputs": [], + "name": "getCKBurn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" } ] \ No newline at end of file diff --git a/precompiles/src/solidity/alpha.sol b/precompiles/src/solidity/alpha.sol index e66dcf00d9..c99252ff48 100644 --- a/precompiles/src/solidity/alpha.sol +++ b/precompiles/src/solidity/alpha.sol @@ -94,4 +94,8 @@ interface IAlpha { /// @dev Returns the sum of alpha prices for all subnets. /// @return The sum of alpha prices. function getSumAlphaPrice() external view returns (uint256); + + /// @dev Returns the CK burn rate. + /// @return The CK burn rate. + function getCKBurn() external view returns (uint256); } From 3fd2f16c3d5f01bd09d7bc9b9eb2f12fbc2d95d2 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 10:05:22 +0800 Subject: [PATCH 15/18] check burn rate scope --- evm-tests/test/alpha.precompile.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/evm-tests/test/alpha.precompile.test.ts b/evm-tests/test/alpha.precompile.test.ts index 1982d1ea87..1ca3c755af 100644 --- a/evm-tests/test/alpha.precompile.test.ts +++ b/evm-tests/test/alpha.precompile.test.ts @@ -8,6 +8,7 @@ import { PublicClient } from "viem"; import { PolkadotSigner, TypedApi } from "polkadot-api"; import { toViemAddress, convertPublicKeyToSs58 } from "../src/address-utils" import { IAlphaABI, IALPHA_ADDRESS } from "../src/contracts/alpha" +import { u64 } from "@polkadot-api/substrate-bindings"; describe("Test Alpha Precompile", () => { // init substrate part @@ -222,6 +223,9 @@ describe("Test Alpha Precompile", () => { assert.strictEqual(ckBurn, ckBurnOnChain, "CK burn should match on chain"); assert.ok(ckBurn !== undefined, "CK burn should be defined"); + const ckBurnPercentage = BigInt(ckBurn) * BigInt(100) / BigInt(2 ** 64 - 1) + assert.ok(ckBurnPercentage >= BigInt(0), "CK burn percentage should be non-negative"); + assert.ok(ckBurnPercentage <= BigInt(100), "CK burn percentage should be less than or equal to 100"); assert.ok(typeof ckBurn === 'bigint', "CK burn should be a bigint"); }); }); From 6722dca93fb591fb877a612ab2d6fb55459a61a9 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 11:53:19 +0800 Subject: [PATCH 16/18] add unit test --- pallets/subtensor/src/tests/coinbase.rs | 73 +++++++++++++++++++++++++ precompiles/src/solidity/subnet.abi | 3 + 2 files changed, 76 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 5b0f7316b0..6759f5c11b 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -1080,6 +1080,9 @@ fn test_drain_alpha_childkey_parentkey() { // Childkey take is 10% ChildkeyTake::::insert(child, netuid, u16::MAX / 10); + let parent_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); + let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::drain_pending_emission( netuid, @@ -1099,6 +1102,7 @@ fn test_drain_alpha_childkey_parentkey() { expected.to_num::(), parent_stake_after ); + close(expected.to_num::(), parent_stake_after.into(), 10_000); let expected = I96F32::from_num(u64::from(pending_alpha)) / I96F32::from_num(10); close(expected.to_num::(), child_stake_after.into(), 10_000); @@ -2753,3 +2757,72 @@ fn test_coinbase_v3_liquidity_update() { assert!(liquidity_before < liquidity_after); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_alpha_childkey_parentkey_with_burn --exact --show-output --nocapture +#[test] +fn test_drain_alpha_childkey_parentkey_with_burn() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + add_network(netuid, 1, 0); + let parent = U256::from(1); + let child = U256::from(2); + let coldkey = U256::from(3); + let stake_before = AlphaCurrency::from(1_000_000_000); + register_ok_neuron(netuid, child, coldkey, 0); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &parent, + &coldkey, + netuid, + stake_before, + ); + mock_set_children_no_epochs(netuid, &parent, &[(u64::MAX, child)]); + + // Childkey take is 10% + ChildkeyTake::::insert(child, netuid, u16::MAX / 10); + + let burn_rate = SubtensorModule::get_ck_burn(); + let parent_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); + + let pending_alpha = AlphaCurrency::from(1_000_000_000); + SubtensorModule::drain_pending_emission( + netuid, + pending_alpha, + TaoCurrency::ZERO, + AlphaCurrency::ZERO, + AlphaCurrency::ZERO, + ); + let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); + let child_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); + + let expected_ck_burn = I96F32::from_num(pending_alpha) + * I96F32::from_num(9.0 / 10.0) + * I96F32::from_num(burn_rate); + + let expected_total = I96F32::from_num(pending_alpha) - expected_ck_burn; + let parent_ratio = (I96F32::from_num(pending_alpha) * I96F32::from_num(9.0 / 10.0) + - expected_ck_burn) + / expected_total; + let child_ratio = (I96F32::from_num(pending_alpha) / I96F32::from_num(10)) / expected_total; + + let expected = + I96F32::from_num(stake_before) + I96F32::from_num(pending_alpha) * parent_ratio; + log::info!( + "expected: {:?}, parent_stake_after: {:?}", + expected.to_num::(), + parent_stake_after + ); + + close( + expected.to_num::(), + parent_stake_after.into(), + 3_000_000, + ); + let expected = I96F32::from_num(u64::from(pending_alpha)) * child_ratio; + close( + expected.to_num::(), + child_stake_after.into(), + 3_000_000, + ); + }); +} diff --git a/precompiles/src/solidity/subnet.abi b/precompiles/src/solidity/subnet.abi index 805c0057d9..f2d97e1f90 100644 --- a/precompiles/src/solidity/subnet.abi +++ b/precompiles/src/solidity/subnet.abi @@ -1046,5 +1046,8 @@ "outputs": [], "stateMutability": "payable", "type": "function" + }, + { + "inputs" } ] From ccb23ccfc4a062403e6c81a870ce1e8f444eb7d5 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 11:55:07 +0800 Subject: [PATCH 17/18] remove unused variable --- pallets/subtensor/src/tests/coinbase.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 6759f5c11b..dce2f7b6ad 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -1080,9 +1080,6 @@ fn test_drain_alpha_childkey_parentkey() { // Childkey take is 10% ChildkeyTake::::insert(child, netuid, u16::MAX / 10); - let parent_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); - let child_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&child, netuid); - let pending_alpha = AlphaCurrency::from(1_000_000_000); SubtensorModule::drain_pending_emission( netuid, From 682cb0d157128c5bfc95308ca41e880548a4cee3 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 3 Sep 2025 11:56:57 +0800 Subject: [PATCH 18/18] unnecessary code change --- pallets/subtensor/src/tests/coinbase.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index dce2f7b6ad..fc1712d161 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -1099,7 +1099,6 @@ fn test_drain_alpha_childkey_parentkey() { expected.to_num::(), parent_stake_after ); - close(expected.to_num::(), parent_stake_after.into(), 10_000); let expected = I96F32::from_num(u64::from(pending_alpha)) / I96F32::from_num(10); close(expected.to_num::(), child_stake_after.into(), 10_000);