From f04ddc5d8eaab68ffb6a66aa73cd3dddfd3ead9c Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Thu, 2 Oct 2025 01:12:16 -0400 Subject: [PATCH 1/9] fix: make alpha_in halve with block_emission, fix subsidy condition --- .../subtensor/src/coinbase/run_coinbase.rs | 64 ++++++++++++------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 006edcb2ef..a38a058b07 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -41,6 +41,8 @@ impl Pallet { let mut alpha_in: BTreeMap = BTreeMap::new(); let mut alpha_out: BTreeMap = BTreeMap::new(); let mut is_subsidized: BTreeMap = BTreeMap::new(); + let mut tao_to_stake = U96F32::saturating_from_num(0.0); + // Only calculate for subnets that we are emitting to. for netuid_i in subnets_to_emit_to.iter() { // Get subnet price. @@ -52,6 +54,11 @@ impl Pallet { .copied() .unwrap_or(asfloat!(0)); log::debug!("default_tao_in_i: {default_tao_in_i:?}"); + + let default_alpha_in_i: U96F32 = + default_tao_in_i.safe_div_or(price_i, U96F32::saturating_from_num(0.0)); + log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); + // Get alpha_emission total let alpha_emission_i: U96F32 = asfloat!( Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) @@ -59,36 +66,18 @@ impl Pallet { ); log::debug!("alpha_emission_i: {alpha_emission_i:?}"); - // 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); + tao_to_stake = tao_to_stake.saturating_add(difference_tao); } else { + alpha_in_i = default_alpha_in_i; 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); } + log::debug!("tao_in_i: {tao_in_i:?}"); log::debug!("alpha_in_i: {alpha_in_i:?}"); // Get alpha_out. @@ -106,6 +95,29 @@ impl Pallet { alpha_in.insert(*netuid_i, alpha_in_i); alpha_out.insert(*netuid_i, alpha_out_i); } + + let amount_per_subnet: U96F32 = tao_to_stake.safe_div_or( + U96F32::saturating_from_num(subnets_to_emit_to.len()), + U96F32::saturating_from_num(0.0), + ); + + for netuid_i in subnets_to_emit_to.iter() { + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(amount_per_subnet).into(), + T::SwapInterface::max_price().into(), + 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); + } + + log::debug!("tao_to_stake: {tao_to_stake:?}"); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); @@ -134,12 +146,16 @@ impl Pallet { SubnetTaoInEmission::::insert(*netuid_i, TaoCurrency::from(tao_in_i)); SubnetTAO::::mutate(*netuid_i, |total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(amount_per_subnet.into()); }); + TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(amount_per_subnet.into()); }); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); + *total = total.saturating_add(amount_per_subnet.into()); }); // Adjust protocol liquidity based on new reserves T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); From 935f3059ac25cd3435a7624e8619dd5d92f24f31 Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Thu, 2 Oct 2025 01:30:16 -0400 Subject: [PATCH 2/9] fix: typecheck --- pallets/subtensor/src/coinbase/run_coinbase.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a38a058b07..bbe836dce0 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -60,10 +60,10 @@ impl Pallet { log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); // Get alpha_emission total - let alpha_emission_i: U96F32 = asfloat!( - Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) - .unwrap_or(0) - ); + let alpha_emission_i: U96F32 = asfloat!(Self::get_block_emission_for_issuance( + Self::get_alpha_issuance(*netuid_i).into() + ) + .unwrap_or(0)); log::debug!("alpha_emission_i: {alpha_emission_i:?}"); let mut alpha_in_i: U96F32; @@ -146,16 +146,16 @@ impl Pallet { SubnetTaoInEmission::::insert(*netuid_i, TaoCurrency::from(tao_in_i)); SubnetTAO::::mutate(*netuid_i, |total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(amount_per_subnet.into()); + *total = total.saturating_add(tou64!(amount_per_subnet).into()); }); TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(amount_per_subnet.into()); + *total = total.saturating_add(tou64!(amount_per_subnet).into()); }); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(amount_per_subnet.into()); + *total = total.saturating_add(tou64!(amount_per_subnet).into()); }); // Adjust protocol liquidity based on new reserves T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); @@ -208,7 +208,7 @@ impl Pallet { 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. + // 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); From d7641bb8735d9f1e1343f4d432ff9684aef46407 Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Thu, 2 Oct 2025 01:50:33 -0400 Subject: [PATCH 3/9] fix: fmt --- pallets/subtensor/src/coinbase/run_coinbase.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index bbe836dce0..6cc3e43c0f 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -60,10 +60,10 @@ impl Pallet { log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); // Get alpha_emission total - let alpha_emission_i: U96F32 = asfloat!(Self::get_block_emission_for_issuance( - Self::get_alpha_issuance(*netuid_i).into() - ) - .unwrap_or(0)); + let alpha_emission_i: U96F32 = asfloat!( + Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) + .unwrap_or(0) + ); log::debug!("alpha_emission_i: {alpha_emission_i:?}"); let mut alpha_in_i: U96F32; @@ -208,7 +208,7 @@ impl Pallet { 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. + // 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); From c0212a7381ed3e231a9e1a2d8ff0ec1d203a4df6 Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Mon, 6 Oct 2025 23:43:56 -0400 Subject: [PATCH 4/9] remove is_subsidized --- .../subtensor/src/coinbase/run_coinbase.rs | 62 +++++++++---------- pallets/subtensor/src/tests/coinbase.rs | 8 +-- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6cc3e43c0f..406481a279 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -40,7 +40,6 @@ impl Pallet { 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 tao_to_stake = U96F32::saturating_from_num(0.0); // Only calculate for subnets that we are emitting to. @@ -68,8 +67,9 @@ impl Pallet { 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; + let min_alpha_emission = alpha_emission_i.min(block_emission); + if default_alpha_in_i > min_alpha_emission { + alpha_in_i = min_alpha_emission; tao_in_i = alpha_in_i.saturating_mul(price_i); let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); tao_to_stake = tao_to_stake.saturating_add(difference_tao); @@ -101,20 +101,21 @@ impl Pallet { U96F32::saturating_from_num(0.0), ); - for netuid_i in subnets_to_emit_to.iter() { - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(amount_per_subnet).into(), - T::SwapInterface::max_price().into(), - 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); - }); + if amount_per_subnet > asfloat!(0.0) { + for netuid_i in subnets_to_emit_to.iter() { + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(amount_per_subnet).into(), + T::SwapInterface::max_price().into(), + 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); } log::debug!("tao_to_stake: {tao_to_stake:?}"); @@ -146,12 +147,10 @@ impl Pallet { SubnetTaoInEmission::::insert(*netuid_i, TaoCurrency::from(tao_in_i)); SubnetTAO::::mutate(*netuid_i, |total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(tou64!(amount_per_subnet).into()); }); TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(tou64!(amount_per_subnet).into()); }); TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); @@ -214,21 +213,18 @@ impl Pallet { 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(), - true, - ); - if let Ok(ok_result) = swap_result { - let root_tao = ok_result.amount_paid_out; - // Accumulate root divs for subnet. - PendingRootDivs::::mutate(*netuid_i, |total| { - *total = total.saturating_add(root_tao); - }); - } + 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::::mutate(*netuid_i, |total| { + *total = total.saturating_add(root_tao.into()); + }); } // Accumulate alpha emission in pending. PendingAlphaSwapped::::mutate(*netuid_i, |total| { diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 39a964a067..31e55502cd 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -153,7 +153,7 @@ fn test_coinbase_tao_issuance_different_prices() { new_test_ext(1).execute_with(|| { let netuid1 = NetUid::from(1); let netuid2 = NetUid::from(2); - let emission = 100_000_000; + let emission = 1_000_000_000; add_network(netuid1, 1, 0); add_network(netuid2, 1, 0); @@ -198,17 +198,17 @@ fn test_coinbase_tao_issuance_different_prices() { // Assert tao emission is split evenly. assert_abs_diff_eq!( SubnetTAO::::get(netuid1), - TaoCurrency::from(initial_tao + emission / 3), + TaoCurrency::from(initial_tao + 45 * emission / 100), epsilon = 1.into(), ); assert_abs_diff_eq!( SubnetTAO::::get(netuid2), - TaoCurrency::from(initial_tao + 2 * emission / 3), + TaoCurrency::from(initial_tao + 55 * emission / 100), epsilon = 1.into(), ); // 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((emission as f64) as u64); assert_abs_diff_eq!( TotalIssuance::::get(), tao_issued, From 9b30818a1e6d996479f76ea15e72915babbbe366 Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Mon, 13 Oct 2025 01:01:57 -0400 Subject: [PATCH 5/9] only subsidize when sum of prices < 1 --- .../subtensor/src/coinbase/run_coinbase.rs | 102 ++++++++++-------- pallets/subtensor/src/tests/coinbase.rs | 17 ++- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 406481a279..26b5d0a9e5 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -34,13 +34,23 @@ 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(); + // --- 2. Get sum of moving alpha prices + let mut acc_total_moving_prices = U96F32::saturating_from_num(0.0); + // Only get price EMA for subnets that we emit to. + for netuid_i in subnets_to_emit_to.iter() { + acc_total_moving_prices = + acc_total_moving_prices.saturating_add(Self::get_moving_alpha_price(*netuid_i)); + } + let total_moving_prices = acc_total_moving_prices; + log::debug!("total_moving_prices: {total_moving_prices:?}"); // --- 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 tao_to_stake = U96F32::saturating_from_num(0.0); + 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() { @@ -67,15 +77,37 @@ impl Pallet { let mut alpha_in_i: U96F32; let mut tao_in_i: U96F32; - let min_alpha_emission = alpha_emission_i.min(block_emission); - if default_alpha_in_i > min_alpha_emission { + if default_alpha_in_i > alpha_emission_i + || total_moving_prices < U96F32::saturating_from_num(1.0) + { + let min_alpha_emission = + default_alpha_in_i.min(alpha_emission_i).min(block_emission); alpha_in_i = min_alpha_emission; tao_in_i = alpha_in_i.saturating_mul(price_i); - let difference_tao: U96F32 = default_tao_in_i.saturating_sub(tao_in_i); - tao_to_stake = tao_to_stake.saturating_add(difference_tao); + + if total_moving_prices < U96F32::saturating_from_num(1.0) { + 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 { alpha_in_i = default_alpha_in_i; tao_in_i = default_tao_in_i; + is_subsidized.insert(*netuid_i, false); + subsidy_amount.insert(*netuid_i, asfloat!(0.0)); } log::debug!("tao_in_i: {tao_in_i:?}"); log::debug!("alpha_in_i: {alpha_in_i:?}"); @@ -96,29 +128,6 @@ impl Pallet { alpha_out.insert(*netuid_i, alpha_out_i); } - let amount_per_subnet: U96F32 = tao_to_stake.safe_div_or( - U96F32::saturating_from_num(subnets_to_emit_to.len()), - U96F32::saturating_from_num(0.0), - ); - - if amount_per_subnet > asfloat!(0.0) { - for netuid_i in subnets_to_emit_to.iter() { - let buy_swap_result = Self::swap_tao_for_alpha( - *netuid_i, - tou64!(amount_per_subnet).into(), - T::SwapInterface::max_price().into(), - 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); - }); - } - } - } - - log::debug!("tao_to_stake: {tao_to_stake:?}"); log::debug!("tao_in: {tao_in:?}"); log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); @@ -141,20 +150,25 @@ impl Pallet { SubnetAlphaOut::::mutate(*netuid_i, |total| { *total = total.saturating_add(alpha_out_i); }); + // Inject TAO in. let tao_in_i: TaoCurrency = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))).into(); + let subsidy_tao: TaoCurrency = + tou64!(*subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0))).into(); SubnetTaoInEmission::::insert(*netuid_i, TaoCurrency::from(tao_in_i)); + + // No need to add subsidy_tao here as it is captured from the swap result above. SubnetTAO::::mutate(*netuid_i, |total| { *total = total.saturating_add(tao_in_i.into()); }); - TotalStake::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); }); + // Here we add subsidy tao as it is technically as issuance TotalIssuance::::mutate(|total| { *total = total.saturating_add(tao_in_i.into()); - *total = total.saturating_add(tou64!(amount_per_subnet).into()); + *total = total.saturating_add(subsidy_tao.into()); }); // Adjust protocol liquidity based on new reserves T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); @@ -212,19 +226,21 @@ impl Pallet { // 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 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::::mutate(*netuid_i, |total| { - *total = total.saturating_add(root_tao.into()); - }); + 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(), + true, + ); + if let Ok(ok_result) = swap_result { + let root_tao = ok_result.amount_paid_out; + // Accumulate root divs for subnet. + PendingRootDivs::::mutate(*netuid_i, |total| { + *total = total.saturating_add(root_tao); + }); + } } // Accumulate alpha emission in pending. PendingAlphaSwapped::::mutate(*netuid_i, |total| { diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 31e55502cd..a67d6b5fb3 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -194,16 +194,15 @@ fn test_coinbase_tao_issuance_different_prices() { // Run the coinbase with the emission amount. SubtensorModule::run_coinbase(U96F32::from_num(emission)); - // Assert tao emission is split evenly. assert_abs_diff_eq!( SubnetTAO::::get(netuid1), - TaoCurrency::from(initial_tao + 45 * emission / 100), + TaoCurrency::from(initial_tao + emission / 3), epsilon = 1.into(), ); assert_abs_diff_eq!( SubnetTAO::::get(netuid2), - TaoCurrency::from(initial_tao + 55 * emission / 100), + TaoCurrency::from(initial_tao + 2 * emission / 3), epsilon = 1.into(), ); @@ -429,7 +428,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger() { SubnetAlphaIn::::insert(netuid1, AlphaCurrency::from(initial_alpha)); // Make price extremely low. SubnetTAO::::insert(netuid2, TaoCurrency::from(initial)); SubnetAlphaIn::::insert(netuid2, AlphaCurrency::from(initial_alpha)); // Make price extremely low. - // Set subnet prices. + // Set subnet prices. SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Run coinbase @@ -957,8 +956,8 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_amounts_half_tao_weight --exact --show-output --nocapture #[test] -fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_amounts_half_tao_weight() - { +fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_amounts_half_tao_weight( +) { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); @@ -1242,7 +1241,7 @@ fn test_get_root_children_drain() { SubtensorModule::set_ck_burn(0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. - // Create keys. + // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); let alice = U256::from(2); @@ -1404,7 +1403,7 @@ fn test_get_root_children_drain_half_proportion() { SubtensorModule::set_ck_burn(0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. - // Create keys. + // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); let alice = U256::from(2); @@ -1492,7 +1491,7 @@ fn test_get_root_children_drain_with_take() { add_network(alpha, 1, 0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. - // Create keys. + // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); let alice = U256::from(2); From 7e0cad97a81d206d0abc280b9b072fbd634012c9 Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Mon, 13 Oct 2025 01:02:11 -0400 Subject: [PATCH 6/9] fix: fmt --- pallets/subtensor/src/tests/coinbase.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index a67d6b5fb3..7fd4dab5ce 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -428,7 +428,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger() { SubnetAlphaIn::::insert(netuid1, AlphaCurrency::from(initial_alpha)); // Make price extremely low. SubnetTAO::::insert(netuid2, TaoCurrency::from(initial)); SubnetAlphaIn::::insert(netuid2, AlphaCurrency::from(initial_alpha)); // Make price extremely low. - // Set subnet prices. + // Set subnet prices. SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); // Run coinbase @@ -956,8 +956,8 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_amounts_half_tao_weight --exact --show-output --nocapture #[test] -fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_amounts_half_tao_weight( -) { +fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_amounts_half_tao_weight() + { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); add_network(netuid, 1, 0); @@ -1241,7 +1241,7 @@ fn test_get_root_children_drain() { SubtensorModule::set_ck_burn(0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. - // Create keys. + // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); let alice = U256::from(2); @@ -1403,7 +1403,7 @@ fn test_get_root_children_drain_half_proportion() { SubtensorModule::set_ck_burn(0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. - // Create keys. + // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); let alice = U256::from(2); @@ -1491,7 +1491,7 @@ fn test_get_root_children_drain_with_take() { add_network(alpha, 1, 0); // Set TAO weight to 1. SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1. - // Create keys. + // Create keys. let cold_alice = U256::from(0); let cold_bob = U256::from(1); let alice = U256::from(2); From 5626096334121e08a36f9904a97eeb4ac530869d Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Mon, 13 Oct 2025 01:22:29 -0400 Subject: [PATCH 7/9] fix: test test_coinbase_tao_issuance_different_prices --- pallets/subtensor/src/coinbase/run_coinbase.rs | 14 +++++++++----- pallets/subtensor/src/tests/coinbase.rs | 12 ++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 26b5d0a9e5..eef513973c 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -69,10 +69,10 @@ impl Pallet { log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); // Get alpha_emission total - let alpha_emission_i: U96F32 = asfloat!( - Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) - .unwrap_or(0) - ); + let alpha_emission_i: U96F32 = asfloat!(Self::get_block_emission_for_issuance( + Self::get_alpha_issuance(*netuid_i).into() + ) + .unwrap_or(0)); log::debug!("alpha_emission_i: {alpha_emission_i:?}"); let mut alpha_in_i: U96F32; @@ -85,7 +85,11 @@ impl Pallet { alpha_in_i = min_alpha_emission; tao_in_i = alpha_in_i.saturating_mul(price_i); + println!("are we in this block?"); + println!("total_moving_prices: {total_moving_prices:?}"); + if total_moving_prices < U96F32::saturating_from_num(1.0) { + println!("my favorite bar is če"); 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( @@ -221,7 +225,7 @@ impl Pallet { 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. + // 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); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 7fd4dab5ce..1a3e18f66f 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -197,27 +197,23 @@ fn test_coinbase_tao_issuance_different_prices() { // Assert tao emission is split evenly. assert_abs_diff_eq!( SubnetTAO::::get(netuid1), - TaoCurrency::from(initial_tao + emission / 3), + TaoCurrency::from(initial_tao + emission / 10), epsilon = 1.into(), ); assert_abs_diff_eq!( SubnetTAO::::get(netuid2), - TaoCurrency::from(initial_tao + 2 * emission / 3), + TaoCurrency::from(initial_tao + 2 * emission / 10), epsilon = 1.into(), ); // Prices are low => we limit tao issued (buy alpha with it) - let tao_issued = TaoCurrency::from((emission as f64) as u64); + let tao_issued = TaoCurrency::from(((0.1 + 0.2) * emission as f64) as u64); assert_abs_diff_eq!( TotalIssuance::::get(), tao_issued, epsilon = 10.into() ); - assert_abs_diff_eq!( - TotalStake::::get(), - emission.into(), - epsilon = 10.into() - ); + assert_abs_diff_eq!(TotalStake::::get(), tao_issued, epsilon = 10.into()); }); } From 63f961b69920c4e3ae9a8aaed65846abdfebc36f Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Mon, 13 Oct 2025 01:30:26 -0400 Subject: [PATCH 8/9] fix: test no longer triggers subsidy so test logic was wrong --- pallets/subtensor/src/coinbase/run_coinbase.rs | 14 +++++--------- pallets/subtensor/src/tests/coinbase.rs | 6 +++--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index eef513973c..26b5d0a9e5 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -69,10 +69,10 @@ impl Pallet { log::debug!("default_alpha_in_i: {default_alpha_in_i:?}"); // Get alpha_emission total - let alpha_emission_i: U96F32 = asfloat!(Self::get_block_emission_for_issuance( - Self::get_alpha_issuance(*netuid_i).into() - ) - .unwrap_or(0)); + let alpha_emission_i: U96F32 = asfloat!( + Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid_i).into()) + .unwrap_or(0) + ); log::debug!("alpha_emission_i: {alpha_emission_i:?}"); let mut alpha_in_i: U96F32; @@ -85,11 +85,7 @@ impl Pallet { alpha_in_i = min_alpha_emission; tao_in_i = alpha_in_i.saturating_mul(price_i); - println!("are we in this block?"); - println!("total_moving_prices: {total_moving_prices:?}"); - if total_moving_prices < U96F32::saturating_from_num(1.0) { - println!("my favorite bar is če"); 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( @@ -225,7 +221,7 @@ impl Pallet { 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. + // 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); diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 1a3e18f66f..ab9381b0ee 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -463,8 +463,8 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { // Enable emission FirstEmissionBlockNumber::::insert(netuid1, 0); FirstEmissionBlockNumber::::insert(netuid2, 0); - SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); - SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); + SubnetMovingPrice::::insert(netuid1, I96F32::from_num(0.1)); + SubnetMovingPrice::::insert(netuid2, I96F32::from_num(0.2)); // Force the swap to initialize SubtensorModule::swap_tao_for_alpha( @@ -498,7 +498,7 @@ fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { let price_2_after = ::SwapInterface::current_alpha_price(netuid2); // AlphaIn gets decreased beacuse of a buy - assert!(u64::from(SubnetAlphaIn::::get(netuid1)) < initial_alpha); + assert!(u64::from(SubnetAlphaIn::::get(netuid1)) < initial_alpha); // HERE assert_eq!( u64::from(SubnetAlphaOut::::get(netuid2)), 21_000_000_000_000_000_u64 From f3e76e733b601207a388523f845e01fe9913d99c Mon Sep 17 00:00:00 2001 From: 0xcacti <0xcacti@gmail.com> Date: Wed, 29 Oct 2025 01:15:14 -0400 Subject: [PATCH 9/9] subsidize outside the main loop to prevent price changes --- .../subtensor/src/coinbase/run_coinbase.rs | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 26b5d0a9e5..f42eb76878 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -87,19 +87,6 @@ impl Pallet { if total_moving_prices < U96F32::saturating_from_num(1.0) { 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); } @@ -132,7 +119,33 @@ impl Pallet { log::debug!("alpha_in: {alpha_in:?}"); log::debug!("alpha_out: {alpha_out:?}"); - // --- 4. Injection. + // --- 5. Subsidization + // When sum of moving prices is < 1.0, we subsidize by buying ALPHA with TAO. + if total_moving_prices < U96F32::saturating_from_num(1.0) { + for netuid_i in subnets_to_emit_to.iter() { + let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); + if !subsidized { + continue; + } + let subsidy_amount_i: U96F32 = + *subsidy_amount.get(netuid_i).unwrap_or(&asfloat!(0.0)); + + let buy_swap_result = Self::swap_tao_for_alpha( + *netuid_i, + tou64!(subsidy_amount_i).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. 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() { @@ -174,7 +187,7 @@ impl Pallet { 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(); @@ -203,7 +216,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 sell them into tao. + // --- 7. Seperate out root dividends in alpha and sell them into tao. // Then accumulate those dividends for later. for netuid_i in subnets_to_emit_to.iter() { // Get remaining alpha out. @@ -252,14 +265,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.