From b6cb0884891a6848003eec2d86e42590481de46d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:13:49 -0500 Subject: [PATCH 01/13] impl tests benchmarks --- pallets/subtensor/src/benchmarks.rs | 1 + .../subtensor/src/coinbase/run_coinbase.rs | 14 ++++++++++++-- pallets/subtensor/src/tests/claim_root.rs | 14 ++++++++++++++ pallets/subtensor/src/tests/coinbase.rs | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 2a13a18aa0..0e53facabc 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1619,6 +1619,7 @@ mod pallet_benchmarks { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 53ac3c6ac5..9ced81fc5b 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -229,6 +229,9 @@ impl Pallet { if Self::should_run_epoch(netuid, current_block) && Self::is_epoch_input_state_consistent(netuid) { + let alpha_out_i: AlphaCurrency = + tou64!(*alpha_out.get(&netuid).unwrap_or(&asfloat!(0.0))).into(); + // Restart counters. BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); @@ -246,7 +249,13 @@ impl Pallet { PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); // Drain pending root alpha divs, alpha emission, and owner cut. - Self::drain_pending_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); + Self::drain_pending_emission( + netuid, + pending_alpha, + pending_root_alpha, + alpha_out_i, + owner_cut, + ); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -604,6 +613,7 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, + alpha_out: AlphaCurrency, // total alpha out for the subnet owner_cut: AlphaCurrency, ) { log::debug!( @@ -614,7 +624,7 @@ impl Pallet { // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, pending_alpha.saturating_add(pending_root_alpha)); + Self::epoch_with_mechanisms(netuid, alpha_out); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index e73417a326..c75b4f5784 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -76,6 +76,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -144,6 +145,7 @@ fn test_claim_root_with_drain_emissions() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -245,6 +247,7 @@ fn test_claim_root_adding_stake_proportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -346,6 +349,7 @@ fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -437,6 +441,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -489,6 +494,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -542,6 +548,7 @@ fn test_claim_root_with_changed_stake() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -634,6 +641,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -678,6 +686,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -714,6 +723,7 @@ fn test_claim_root_with_drain_emissions_and_swap_claim_type() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1100,6 +1110,7 @@ fn test_claim_root_with_swap_coldkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1190,6 +1201,7 @@ fn test_claim_root_with_swap_hotkey() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1306,6 +1318,7 @@ fn test_claim_root_on_network_deregistration() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); @@ -1446,6 +1459,7 @@ fn test_claim_root_with_unrelated_subnets() { netuid, AlphaCurrency::ZERO, pending_root_alpha.into(), + pending_root_alpha.into(), // alpha out AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 60644b2a28..bbb7061e27 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -689,6 +689,7 @@ fn test_drain_base() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -704,6 +705,7 @@ fn test_drain_base_with_subnet() { netuid, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -729,6 +731,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { netuid, pending_alpha.into(), AlphaCurrency::ZERO, + pending_alpha.into(), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -758,6 +761,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -802,6 +806,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -849,6 +854,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -914,6 +920,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -989,6 +996,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, 0.into(), ); let stake_after1 = @@ -1069,6 +1077,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1130,6 +1139,7 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1355,6 +1365,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1379,6 +1390,7 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1402,6 +1414,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1490,6 +1503,7 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1576,6 +1590,7 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1663,6 +1678,7 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -2378,6 +2394,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, + emission, AlphaCurrency::ZERO, ); @@ -2451,6 +2468,7 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2743,6 +2761,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From 3118bb7c53b78b94cd0b49f8277403f348134ea0 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:13:59 -0500 Subject: [PATCH 02/13] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 266a755708..240c490736 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: 338, + spec_version: 340, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 843bc884453a9aac28912f72a2acaf5f838a7bc8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:22:39 -0500 Subject: [PATCH 03/13] remove duplicate remove --- pallets/subtensor/src/coinbase/root.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 642a7f18ac..80965f0bbb 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -348,7 +348,6 @@ impl Pallet { RAORecycledForRegistration::::remove(netuid); MaxRegistrationsPerBlock::::remove(netuid); WeightsVersionKey::::remove(netuid); - PendingRootAlphaDivs::::remove(netuid); // --- 17. Subtoken / feature flags. LiquidAlphaOn::::remove(netuid); From fb053c863b84c062551a1feccf0d14a27f64d9f3 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 5 Nov 2025 15:41:11 -0500 Subject: [PATCH 04/13] use alpha out here --- pallets/subtensor/src/coinbase/run_coinbase.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 9ced81fc5b..8ab4361353 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -640,8 +640,7 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let pending_validator_alpha = if !incentive_sum.is_zero() { - pending_alpha - .saturating_add(pending_root_alpha) + alpha_out .saturating_div(2.into()) .saturating_sub(pending_root_alpha) } else { From cb2085e031253123e21908a341c6422e5f89cce6 Mon Sep 17 00:00:00 2001 From: 0xcacti <97139981+0xcacti@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:53:34 -0500 Subject: [PATCH 05/13] Hotfix/vune/epoch sub fix (#2186) * fix: injection logic * fix: fmt' --- .../subtensor/src/coinbase/run_coinbase.rs | 81 +++++++++++-------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 8ab4361353..6e759b9243 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,13 +34,19 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); + let total_ema_price: U96F32 = subnets_to_emit_to + .iter() + .map(|netuid| Self::get_moving_alpha_price(*netuid)) + .sum(); + let subsidy_mode = total_ema_price <= U96F32::saturating_from_num(1); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut is_subsidized: BTreeMap = BTreeMap::new(); + let mut subsidy_amount: BTreeMap = BTreeMap::new(); + // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -62,32 +68,16 @@ impl Pallet { // Get initial alpha_in let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - let tao_in_ratio: U96F32 = default_tao_in_i.safe_div_or( - U96F32::saturating_from_num(block_emission), - U96F32::saturating_from_num(0.0), - ); - if price_i < tao_in_ratio { - tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); - alpha_in_i = block_emission; + + if default_alpha_in_i > alpha_emission_i { + alpha_in_i = alpha_emission_i; + tao_in_i = alpha_in_i.saturating_mul(price_i); let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - // Difference becomes buy. - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(difference_tao).into(), - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); - } - is_subsidized.insert(*netuid_i, true); + subsidy_amount.insert(*netuid_i, difference_tao); } else { tao_in_i = default_tao_in_i; - alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); - is_subsidized.insert(*netuid_i, false); + alpha_in_i = alpha_in_default_i; + subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -110,9 +100,33 @@ impl Pallet { log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - // --- 4. Injection. - // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. - // This operation changes the pool liquidity each block. + // --- 4. Inject and subsidize + for netuid_i in subnets_to_emit_to.iter() { + let tao_in_i: TaoCurrency = + tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let alpha_in_i: AlphaCurrency = + AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); + let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); + + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); + + if difference_tao > asfloat!(0) { + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(difference_tao).into(), + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_sub(bought_alpha); + }); + } + } + } + + // --- 5. Update counters for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = @@ -138,14 +152,15 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); + + let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(tou64!(difference_tao).into()); }); - // Adjust protocol liquidity based on new reserves - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } - // --- 5. Compute owner cuts and remove them from alpha_out remaining. + // --- 6. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -174,7 +189,7 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 6. Seperate out root dividends in alpha and keep them. + // --- 7. Seperate out root dividends in alpha and keep them. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. @@ -211,14 +226,14 @@ impl Pallet { }); } - // --- 7. Update moving prices after using them in the emission calculation. + // --- 8. Update moving prices after using them in the emission calculation. // Only update price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Update moving prices after using them above. Self::update_moving_price(*netuid_i); } - // --- 8. Drain pending emission through the subnet based on tempo. + // --- 9. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { // Reveal matured weights. From b068d0247311858dc22a198162ae9767df22c7a9 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 15:55:14 -0500 Subject: [PATCH 06/13] emit full alpha if subsidized --- .../subtensor/src/coinbase/run_coinbase.rs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6e759b9243..e1635afb4e 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -204,21 +204,23 @@ impl Pallet { .unwrap_or(asfloat!(0.0)); log::debug!("root_proportion: {root_proportion:?}"); // Get root proportion of alpha_out dividends. - let root_alpha: U96F32 = root_proportion - .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. - .saturating_mul(asfloat!(0.5)); // 50% to validators. - // Remove root alpha from alpha_out. - log::debug!("root_alpha: {root_alpha:?}"); - // 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:?}"); - + let mut root_alpha: U96F32 = asfloat!(0.0); let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); if !subsidized { + // Only give root alpha if not being subsidized. + root_alpha = root_proportion + .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. + .saturating_mul(asfloat!(0.5)); // 50% to validators. PendingRootAlphaDivs::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(root_alpha).into()); }); } + // Remove root alpha from alpha_out. + log::debug!("root_alpha: {root_alpha:?}"); + + // 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:?}"); // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid_i, |total| { From 7d77cf0bea7da09a12ae72761a5e7330310b1784 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 15:59:43 -0500 Subject: [PATCH 07/13] fix coinbase test --- pallets/subtensor/src/tests/coinbase.rs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index bbb7061e27..29fbfacf48 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -245,7 +245,7 @@ fn test_coinbase_tao_issuance_different_prices() { ); // Prices are low => we limit tao issued (buy alpha with it) - let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); + let tao_issued = TaoCurrency::from(((1.0) * emission as f64) as u64); assert_abs_diff_eq!( TotalIssuance::::get(), tao_issued, @@ -689,7 +689,6 @@ fn test_drain_base() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -705,7 +704,6 @@ fn test_drain_base_with_subnet() { netuid, AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, // alpha out AlphaCurrency::ZERO, ) }); @@ -731,7 +729,6 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { netuid, pending_alpha.into(), AlphaCurrency::ZERO, - pending_alpha.into(), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -761,7 +758,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -806,7 +802,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, - pending_alpha.saturating_add(pending_root_alpha), // alpha out AlphaCurrency::ZERO, ); let stake_after = @@ -854,7 +849,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -920,7 +914,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -996,7 +989,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, 0.into(), ); let stake_after1 = @@ -1077,7 +1069,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1139,7 +1130,6 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1365,7 +1355,6 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1390,7 +1379,6 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1414,7 +1402,6 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1503,7 +1490,6 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1590,7 +1576,6 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -1678,7 +1663,6 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); @@ -2394,7 +2378,6 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, - emission, AlphaCurrency::ZERO, ); @@ -2468,7 +2451,6 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, - AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2761,7 +2743,6 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, - pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From e792dd19d9d355d03a4bc0bc4de50326b494db82 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 16:08:07 -0500 Subject: [PATCH 08/13] fix tests --- pallets/subtensor/src/tests/coinbase.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 29fbfacf48..0fc1ea8c98 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -690,6 +690,7 @@ fn test_drain_base() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -705,6 +706,7 @@ fn test_drain_base_with_subnet() { AlphaCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ) }); } @@ -730,6 +732,7 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { pending_alpha.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); let stake_after = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); @@ -758,6 +761,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -802,6 +806,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, pending_alpha, pending_root_alpha, + pending_alpha.saturating_add(pending_root_alpha), AlphaCurrency::ZERO, ); let stake_after = @@ -849,6 +854,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -914,6 +920,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -989,6 +996,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, 0.into(), ); let stake_after1 = @@ -1069,6 +1077,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let stake_after1 = @@ -1130,6 +1139,7 @@ fn test_drain_alpha_childkey_parentkey() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); @@ -1355,6 +1365,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1379,6 +1390,7 @@ fn test_get_root_children_drain() { pending_alpha, // pending_root1, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1402,6 +1414,7 @@ fn test_get_root_children_drain() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1490,6 +1503,7 @@ fn test_get_root_children_drain_half_proportion() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1576,6 +1590,7 @@ fn test_get_root_children_drain_with_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -1663,6 +1678,7 @@ fn test_get_root_children_drain_with_half_take() { alpha, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); @@ -2378,6 +2394,7 @@ fn test_drain_pending_emission_no_miners_all_drained() { netuid, emission, AlphaCurrency::ZERO, + emission, AlphaCurrency::ZERO, ); @@ -2451,6 +2468,7 @@ fn test_drain_pending_emission_zero_emission() { 0.into(), AlphaCurrency::ZERO, AlphaCurrency::ZERO, + AlphaCurrency::ZERO, ); // Get the new stake of the hotkey. @@ -2743,6 +2761,7 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { netuid, pending_alpha, AlphaCurrency::ZERO, + pending_alpha, AlphaCurrency::ZERO, ); let parent_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&parent, netuid); From d7a28ba22961771d4f44222799354abd9c29b70d Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 17:54:01 -0500 Subject: [PATCH 09/13] Revert "Hotfix/vune/epoch sub fix (#2186)" This reverts commit ccc6bba7bdfa7501a461d0adfef80dd6488f9dfb. --- .../subtensor/src/coinbase/run_coinbase.rs | 81 ++++++++----------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index e1635afb4e..2bc985a7b1 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,19 +34,13 @@ impl Pallet { // 2. Get subnets to emit to and emissions let subnet_emissions = Self::get_subnet_block_emissions(&subnets, block_emission); let subnets_to_emit_to: Vec = subnet_emissions.keys().copied().collect(); - let total_ema_price: U96F32 = subnets_to_emit_to - .iter() - .map(|netuid| Self::get_moving_alpha_price(*netuid)) - .sum(); - let subsidy_mode = total_ema_price <= U96F32::saturating_from_num(1); // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) // Computation is described in detail in the dtao whitepaper. let mut tao_in: BTreeMap = BTreeMap::new(); let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); - let mut subsidy_amount: BTreeMap = BTreeMap::new(); - + let mut is_subsidized: BTreeMap = BTreeMap::new(); // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -68,16 +62,32 @@ impl Pallet { // Get initial alpha_in let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - - if default_alpha_in_i > alpha_emission_i { - alpha_in_i = alpha_emission_i; - tao_in_i = alpha_in_i.saturating_mul(price_i); + let tao_in_ratio: U96F32 = default_tao_in_i.safe_div_or( + U96F32::saturating_from_num(block_emission), + U96F32::saturating_from_num(0.0), + ); + if price_i < tao_in_ratio { + tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); + alpha_in_i = block_emission; let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - subsidy_amount.insert(*netuid_i, difference_tao); + // Difference becomes buy. + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(difference_tao).into(), + T::SwapInterface::max_price(), + true, + ); + if let Ok(buy_swap_result_ok) = buy_swap_result { + let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_sub(bought_alpha); + }); + } + is_subsidized.insert(*netuid_i, true); } else { tao_in_i = default_tao_in_i; - alpha_in_i = alpha_in_default_i; - subsidy_amount.insert(*netuid_i, U96F32::from_num(0.0)); + alpha_in_i = tao_in_i.safe_div_or(price_i, alpha_emission_i); + is_subsidized.insert(*netuid_i, false); } log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -100,33 +110,9 @@ impl Pallet { log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - // --- 4. Inject and subsidize - for netuid_i in subnets_to_emit_to.iter() { - let tao_in_i: TaoCurrency = - tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - let alpha_in_i: AlphaCurrency = - AlphaCurrency::from(tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0)))); - let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); - - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); - - if difference_tao > asfloat!(0) { - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(difference_tao).into(), - T::SwapInterface::max_price(), - true, - ); - if let Ok(buy_swap_result_ok) = buy_swap_result { - let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); - } - } - } - - // --- 5. Update counters + // --- 4. Injection. + // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. + // This operation changes the pool liquidity each block. for netuid_i in subnets_to_emit_to.iter() { // Inject Alpha in. let alpha_in_i = @@ -152,15 +138,14 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); - - let difference_tao: U96F32 = *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0)); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(tou64!(difference_tao).into()); }); + // Adjust protocol liquidity based on new reserves + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } - // --- 6. Compute owner cuts and remove them from alpha_out remaining. + // --- 5. Compute owner cuts and remove them from alpha_out remaining. // Remove owner cuts here so that we can properly seperate root dividends in the next step. // Owner cuts are accumulated and then fed to the drain at the end of this func. let cut_percent: U96F32 = Self::get_float_subnet_owner_cut(); @@ -189,7 +174,7 @@ impl Pallet { let tao_weight: U96F32 = root_tao.saturating_mul(Self::get_tao_weight()); log::debug!("tao_weight: {tao_weight:?}"); - // --- 7. Seperate out root dividends in alpha and keep them. + // --- 6. Seperate out root dividends in alpha and keep them. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. @@ -228,14 +213,14 @@ impl Pallet { }); } - // --- 8. Update moving prices after using them in the emission calculation. + // --- 7. Update moving prices after using them in the emission calculation. // Only update price EMA for subnets that we emit to. for netuid_i in subnets_to_emit_to.iter() { // Update moving prices after using them above. Self::update_moving_price(*netuid_i); } - // --- 9. Drain pending emission through the subnet based on tempo. + // --- 8. Drain pending emission through the subnet based on tempo. // Run the epoch for *all* subnets, even if we don't emit anything. for &netuid in subnets.iter() { // Reveal matured weights. From a837632fdefc9e0528adcce86fe0c3a6e941e4c7 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 17:59:28 -0500 Subject: [PATCH 10/13] add total issuance bump --- pallets/subtensor/src/coinbase/run_coinbase.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 2bc985a7b1..c1f7052d84 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -69,11 +69,15 @@ impl Pallet { if price_i < tao_in_ratio { tao_in_i = price_i.saturating_mul(U96F32::saturating_from_num(block_emission)); alpha_in_i = block_emission; - let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); + let difference_tao: TaoCurrency = + tou64!(default_tao_in_i.saturating_sub(tao_in_i)).into(); + TotalIssuance::::mutate(|total| { + *total = total.saturating_add(difference_tao); + }); // Difference becomes buy. let buy_swap_result = Self::swap_tao_for_alpha( *netuid_i, - tou64!(difference_tao).into(), + difference_tao, T::SwapInterface::max_price(), true, ); From 6bddb56301974770eeb58f92844e2f4f359821de Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 21:34:14 -0500 Subject: [PATCH 11/13] no alpha out thing --- .../subtensor/src/coinbase/run_coinbase.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index c1f7052d84..9b1ad984af 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -83,9 +83,7 @@ impl Pallet { ); if let Ok(buy_swap_result_ok) = buy_swap_result { let bought_alpha = AlphaCurrency::from(buy_swap_result_ok.amount_paid_out); - SubnetAlphaOut::::mutate(*netuid_i, |total| { - *total = total.saturating_sub(bought_alpha); - }); + Self::recycle_subnet_alpha(*netuid_i, bought_alpha); } is_subsidized.insert(*netuid_i, true); } else { @@ -110,6 +108,7 @@ impl Pallet { alpha_in.insert(*netuid_i, alpha_in_i); alpha_out.insert(*netuid_i, alpha_out_i); } + log::debug!("is_subsidized: {is_subsidized:?}"); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); @@ -235,9 +234,6 @@ impl Pallet { if Self::should_run_epoch(netuid, current_block) && Self::is_epoch_input_state_consistent(netuid) { - let alpha_out_i: AlphaCurrency = - tou64!(*alpha_out.get(&netuid).unwrap_or(&asfloat!(0.0))).into(); - // Restart counters. BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); @@ -259,7 +255,7 @@ impl Pallet { netuid, pending_alpha, pending_root_alpha, - alpha_out_i, + pending_alpha.saturating_add(pending_root_alpha), owner_cut, ); } else { @@ -497,6 +493,7 @@ impl Pallet { let destination = maybe_dest.clone().unwrap_or(hotkey.clone()); if let Some(dest) = maybe_dest { + log::debug!("incentives: auto staking {incentive:?} to {dest:?}"); Self::deposit_event(Event::::AutoStakeAdded { netuid, destination: dest, @@ -505,6 +502,9 @@ impl Pallet { incentive, }); } + log::debug!( + "incentives: increasing stake for {hotkey:?} to {incentive:?} with owner {owner:?}" + ); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &destination, &owner, @@ -619,7 +619,7 @@ impl Pallet { netuid: NetUid, pending_alpha: AlphaCurrency, pending_root_alpha: AlphaCurrency, - alpha_out: AlphaCurrency, // total alpha out for the subnet + total_alpha: AlphaCurrency, owner_cut: AlphaCurrency, ) { log::debug!( @@ -630,7 +630,7 @@ impl Pallet { // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, alpha_out); + Self::epoch_with_mechanisms(netuid, total_alpha); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. @@ -646,7 +646,7 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let pending_validator_alpha = if !incentive_sum.is_zero() { - alpha_out + total_alpha .saturating_div(2.into()) .saturating_sub(pending_root_alpha) } else { From b857d4d4bc8cc2fb0702af4c6d6aba64ef304a4c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 21:34:29 -0500 Subject: [PATCH 12/13] add tests for emissions w/ subs --- pallets/subtensor/src/tests/coinbase.rs | 350 ++++++++++++++++++++++++ 1 file changed, 350 insertions(+) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 0fc1ea8c98..56f7d2171f 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -2928,3 +2928,353 @@ fn test_zero_shares_zero_emission() { assert_eq!(SubnetAlphaIn::::get(netuid2), initial.into()); }); } + +#[test] +fn test_mining_emission_distribution_with_subsidy() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make subsidy happen + // set price very low, e.g. a lot of alpha in + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(0.01), + ); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + log::info!("is_sub: Running epoch with subsidy"); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are indeed being subsidized, i.e. that root alpha divs are NOT increasing + assert_eq!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should not increase" + ); + // Check root divs are zero + assert_eq!( + new_root_alpha_divs, + AlphaCurrency::ZERO, + "Root alpha divs should be zero" + ); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + // Run again but with some root stake + step_block(subnet_tempo - 2); + assert_abs_diff_eq!( + PendingEmission::::get(netuid).to_u64(), + U96F32::saturating_from_num(per_block_emission) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo as u64)) + .saturating_mul(U96F32::saturating_from_num(0.90)) + .saturating_to_num::(), + epsilon = 100_000_u64.into() + ); + step_block(1); + assert!( + BlocksSinceLastStep::::get(netuid) == 0, + "Blocks since last step should be 0" + ); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + log::info!("Miner uid: {miner_uid:?}"); + let miner_incentive: AlphaCurrency = + (*Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .expect("Miner uid should be present") as u64) + .into(); + log::info!("Miner incentive: {miner_incentive:?}"); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + Incentive::::get(NetUidStorageIndex::from(netuid)) + .iter() + .sum::(), + u16::MAX, + epsilon = 10 + ); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_mining_emission_distribution_with_no_subsidy --exact --show-output --nocapture +#[test] +fn test_mining_emission_distribution_with_no_subsidy() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake: u64 = 100_000_000_000; + let root_stake: u64 = 200_000_000_000; // 200 TAO + + // Create root network + SubtensorModule::set_tao_weight(0); // Start tao weight at 0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + SubnetMechanism::::insert(netuid, 1); // Set mechanism to 1 + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, TaoCurrency::from(1_000_000_000_000_000)); + SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(1_000_000_000_000_000)); + + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(u16::MAX / 10); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + // Add stake to validator so it has root stake + SubtensorModule::add_balance_to_coldkey_account(&validator_coldkey, root_stake.into()); + // init root + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + NetUid::ROOT, + root_stake.into() + )); + // Set tao weight non zero + SubtensorModule::set_tao_weight(u64::MAX / 10); + + // Make subsidy not happen + // set price very high + // e.g. very little alpha in pool + //SubnetAlphaIn::::insert(netuid, AlphaCurrency::from(5)); + pallet_subtensor_swap::AlphaSqrtPrice::::insert( + netuid, + U64F64::saturating_from_num(10.0), + ); + + // Run run_coinbase until emissions are drained + step_block(subnet_tempo); + + log::info!("is_sub: Running epoch with no subsidy"); + + let old_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + let miner_stake_before_epoch = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ); + + // step by one block + step_block(1); + // Verify that root alpha divs + let new_root_alpha_divs = PendingRootAlphaDivs::::get(netuid); + // Check that we are NOT being subsidized, i.e. that root alpha divs are are changing + assert_ne!( + new_root_alpha_divs, old_root_alpha_divs, + "Root alpha divs should be changing" + ); + assert!( + new_root_alpha_divs > AlphaCurrency::ZERO, + "Root alpha divs should be greater than 0" + ); + + // Run again but with some root stake + step_block(subnet_tempo - 1); + + let miner_uid = Uids::::get(netuid, miner_hotkey).unwrap_or(0); + let miner_incentive: AlphaCurrency = + (*Incentive::::get(NetUidStorageIndex::from(netuid)) + .get(miner_uid as usize) + .expect("Miner uid should be present") as u64) + .into(); + log::info!("Miner incentive: {miner_incentive:?}"); + + let per_block_emission = SubtensorModule::get_block_emission_for_issuance( + SubtensorModule::get_alpha_issuance(netuid).into(), + ) + .unwrap_or(0); + + // Miner emissions + let miner_emission_1: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &miner_hotkey, + &miner_coldkey, + netuid, + ) + .to_u64() + - miner_stake_before_epoch.to_u64(); + + assert_abs_diff_eq!( + miner_emission_1, + U96F32::saturating_from_num(miner_incentive) + .saturating_div(u16::MAX.into()) + .saturating_mul(U96F32::saturating_from_num(per_block_emission)) + .saturating_mul(U96F32::saturating_from_num(subnet_tempo + 1)) + .saturating_mul(U96F32::saturating_from_num(0.45)) // miner cut + .saturating_to_num::(), + epsilon = 1_000_000_u64 + ); + }); +} From e1feb05c3936b010a15a1c64844245b144c8e659 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Wed, 5 Nov 2025 21:41:14 -0500 Subject: [PATCH 13/13] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 240c490736..42f35cbc80 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: 340, + spec_version: 341, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,