diff --git a/pallets/subtensor/src/staking/remove_stake.rs b/pallets/subtensor/src/staking/remove_stake.rs index 5771029f74..7fdd335556 100644 --- a/pallets/subtensor/src/staking/remove_stake.rs +++ b/pallets/subtensor/src/staking/remove_stake.rs @@ -447,19 +447,13 @@ impl Pallet { let should_refund_owner: bool = reg_at < start_block; // 3) Compute owner's received emission in TAO at current price (ONLY if we may refund). - // Emission:: is Vec. We: - // - sum emitted α, + // We: + // - get the current alpha issuance, // - apply owner fraction to get owner α, // - price that α using a *simulated* AMM swap. let mut owner_emission_tao = TaoCurrency::ZERO; if should_refund_owner && !lock_cost.is_zero() { - let total_emitted_alpha_u128: u128 = - Emission::::get(netuid) - .into_iter() - .fold(0u128, |acc, e_alpha| { - let e_u64: u64 = Into::::into(e_alpha); - acc.saturating_add(e_u64 as u128) - }); + let total_emitted_alpha_u128: u128 = Self::get_alpha_issuance(netuid).to_u64() as u128; if total_emitted_alpha_u128 > 0 { let owner_fraction: U96F32 = Self::get_float_subnet_owner_cut(); @@ -469,22 +463,12 @@ impl Pallet { .saturating_to_num::(); owner_emission_tao = if owner_alpha_u64 > 0 { - let order = GetTaoForAlpha::with_amount(owner_alpha_u64); - match T::SwapInterface::sim_swap(netuid.into(), order) { - Ok(sim) => TaoCurrency::from(sim.amount_paid_out), - Err(e) => { - log::debug!( - "destroy_alpha_in_out_stakes: sim_swap owner α→τ failed (netuid={netuid:?}, alpha={owner_alpha_u64}, err={e:?}); falling back to price multiply.", - ); - let cur_price: U96F32 = - T::SwapInterface::current_alpha_price(netuid.into()); - let val_u64 = U96F32::from_num(owner_alpha_u64) - .saturating_mul(cur_price) - .floor() - .saturating_to_num::(); - TaoCurrency::from(val_u64) - } - } + let cur_price: U96F32 = T::SwapInterface::current_alpha_price(netuid.into()); + let val_u64 = U96F32::from_num(owner_alpha_u64) + .saturating_mul(cur_price) + .floor() + .saturating_to_num::(); + TaoCurrency::from(val_u64) } else { TaoCurrency::ZERO }; diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 0449c67f86..3706878bb6 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -216,43 +216,49 @@ fn dissolve_owner_cut_refund_logic() { // One staker and a TAO pot (not relevant to refund amount). let sh = U256::from(77); let sc = U256::from(88); - Alpha::::insert((sh, sc, net), U64F64::from_num(100u128)); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &sh, + &sc, + net, + AlphaCurrency::from(800u64), + ); SubnetTAO::::insert(net, TaoCurrency::from(1_000)); // Lock & emissions: total emitted α = 800. let lock: TaoCurrency = TaoCurrency::from(2_000); SubtensorModule::set_subnet_locked_balance(net, lock); - Emission::::insert( - net, - vec![AlphaCurrency::from(200), AlphaCurrency::from(600)], - ); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(net).to_u64() > 0); // Owner cut = 11796 / 65535 (about 18%). SubnetOwnerCut::::put(11_796u16); // Compute expected refund with the SAME math as the pallet. let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 800; + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(net).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - // Current α→τ price for this subnet. - let price: U96F32 = - ::SwapInterface::current_alpha_price(net.into()); - let owner_emission_tao_u64: u64 = U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::(); + // Use the current alpha price to estimate the TAO equivalent. + let owner_emission_tao = { + let price: U96F32 = + ::SwapInterface::current_alpha_price(net.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + .into() + }; - let expected_refund: TaoCurrency = - lock.saturating_sub(TaoCurrency::from(owner_emission_tao_u64)); + let expected_refund: TaoCurrency = lock.saturating_sub(owner_emission_tao); let before = SubtensorModule::get_coldkey_balance(&oc); assert_ok!(SubtensorModule::do_dissolve_network(net)); let after = SubtensorModule::get_coldkey_balance(&oc); + assert!(after > before); // some refund is expected assert_eq!( TaoCurrency::from(after), TaoCurrency::from(before) + expected_refund @@ -841,15 +847,10 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { SubnetTAO::::insert(netuid, TaoCurrency::from(tao_pot)); SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock)); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); + // Owner already earned some emission; owner-cut = 50 % - Emission::::insert( - netuid, - vec![ - AlphaCurrency::from(1_000), - AlphaCurrency::from(2_000), - AlphaCurrency::from(1_500), - ], - ); SubnetOwnerCut::::put(32_768u16); // ~ 0.5 in fixed-point // ── 4) balances before ────────────────────────────────────────────── @@ -879,28 +880,23 @@ fn destroy_alpha_out_many_stakers_complex_distribution() { // ── 5b) expected owner refund with price-aware emission deduction ─── let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 1_000 + 2_000 + 1_500; // 4500 α + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(netuid).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - let order = GetTaoForAlpha::::with_amount(owner_alpha_u64); - let owner_emission_tao = - ::SwapInterface::sim_swap(netuid.into(), order) - .map(|res| res.amount_paid_out) - .unwrap_or_else(|_| { - // Fallback matches the pallet's fallback - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); - U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::() - .into() - }); - - let expected_refund = lock.saturating_sub(owner_emission_tao.to_u64()); + let owner_emission_tao: u64 = { + // Fallback matches the pallet's fallback + let price: U96F32 = + ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + }; + + let expected_refund = lock.saturating_sub(owner_emission_tao); // ── 6) run distribution (credits τ to coldkeys, wipes α state) ───── assert_ok!(SubtensorModule::destroy_alpha_in_out_stakes(netuid)); @@ -947,34 +943,38 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // Lock and (nonzero) emissions let lock_u64: u64 = 50_000; SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock_u64)); - Emission::::insert( - netuid, - vec![AlphaCurrency::from(1_500u64), AlphaCurrency::from(3_000u64)], // total 4_500 α - ); // Owner cut ≈ 50% SubnetOwnerCut::::put(32_768u16); + // give some stake to other key + let other_cold = U256::from(1_234); + let other_hot = U256::from(2_345); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_hot, + &other_cold, + netuid, + AlphaCurrency::from(30u64), // not nearly enough to cover the lock + ); + + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); + // Compute expected refund using the same math as the pallet let frac: U96F32 = SubtensorModule::get_float_subnet_owner_cut(); - let total_emitted_alpha: u64 = 1_500 + 3_000; // 4_500 α + let total_emitted_alpha: u64 = SubtensorModule::get_alpha_issuance(netuid).to_u64(); let owner_alpha_u64: u64 = U96F32::from_num(total_emitted_alpha) .saturating_mul(frac) .floor() .saturating_to_num::(); - // Prefer sim_swap; fall back to current price if unavailable. - let order = GetTaoForAlpha::::with_amount(owner_alpha_u64); - let owner_emission_tao_u64 = - ::SwapInterface::sim_swap(netuid.into(), order) - .map(|res| res.amount_paid_out.to_u64()) - .unwrap_or_else(|_| { - let price: U96F32 = - ::SwapInterface::current_alpha_price(netuid.into()); - U96F32::from_num(owner_alpha_u64) - .saturating_mul(price) - .floor() - .saturating_to_num::() - }); + let owner_emission_tao_u64 = { + let price: U96F32 = + ::SwapInterface::current_alpha_price(netuid.into()); + U96F32::from_num(owner_alpha_u64) + .saturating_mul(price) + .floor() + .saturating_to_num::() + }; let expected_refund: u64 = lock_u64.saturating_sub(owner_emission_tao_u64); @@ -1011,7 +1011,17 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // Lock and emissions present (should be ignored for refund) let lock_u64: u64 = 42_000; SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(lock_u64)); - Emission::::insert(netuid, vec![AlphaCurrency::from(5_000u64)]); + // give some stake to other key + let other_cold = U256::from(1_234); + let other_hot = U256::from(2_345); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_hot, + &other_cold, + netuid, + AlphaCurrency::from(300u64), // not nearly enough to cover the lock + ); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); SubnetOwnerCut::::put(32_768u16); // ~50% // Balances before @@ -1046,7 +1056,9 @@ fn destroy_alpha_out_refund_gating_by_registration_block() { // lock = 0; emissions present (must not matter) SubtensorModule::set_subnet_locked_balance(netuid, TaoCurrency::from(0u64)); - Emission::::insert(netuid, vec![AlphaCurrency::from(10_000u64)]); + SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(10_000)); + // ensure there was some Alpha issued + assert!(SubtensorModule::get_alpha_issuance(netuid).to_u64() > 0); SubnetOwnerCut::::put(32_768u16); // ~50% let owner_before = SubtensorModule::get_coldkey_balance(&owner_cold);