From 799af3847adb9947fe3a642e6b5b352c3f2e11ce Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 15 Jun 2026 07:26:08 -0400 Subject: [PATCH 1/5] Handle balancer misbalance on injection --- pallets/swap/src/pallet/impls.rs | 94 +++++++++++++++++++++++++------- pallets/swap/src/pallet/mod.rs | 9 +++ pallets/swap/src/pallet/tests.rs | 92 +++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 19 deletions(-) diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index c3e0b2f1d3..57e759f7ad 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -79,8 +79,8 @@ impl Pallet { Ok(()) } - /// Returns actually added Tao and Alpha, which may be zero in case - /// of a high disbalance + /// Returns actually added Tao and Alpha. Amounts that would push weights + /// out of range are left in per-subnet reservoirs for a later injection. pub(super) fn adjust_protocol_liquidity( netuid: NetUid, tao_delta: TaoBalance, @@ -89,31 +89,85 @@ impl Pallet { // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); - let mut balancer = SwapBalancer::::get(netuid); + let balancer = SwapBalancer::::get(netuid); + + let pending_tao = BalancerTaoReservoir::::get(netuid).saturating_add(tao_delta); + let pending_alpha = BalancerAlphaReservoir::::get(netuid).saturating_add(alpha_delta); + + if let Some(new_balancer) = Self::try_update_balancer( + &balancer, + tao_reserve, + alpha_reserve, + pending_tao, + pending_alpha, + ) { + BalancerTaoReservoir::::insert(netuid, TaoBalance::ZERO); + BalancerAlphaReservoir::::insert(netuid, AlphaBalance::ZERO); + SwapBalancer::::insert(netuid, new_balancer); + return (pending_tao, pending_alpha); + } - // Update weights and log errors if they go out of range - if balancer - .update_weights_for_added_liquidity( - u64::from(tao_reserve), - u64::from(alpha_reserve), - u64::from(tao_delta), - u64::from(alpha_delta), - ) - .is_err() - { + if let Some(new_balancer) = Self::try_update_balancer( + &balancer, + tao_reserve, + alpha_reserve, + TaoBalance::ZERO, + pending_alpha, + ) { + BalancerTaoReservoir::::insert(netuid, pending_tao); + BalancerAlphaReservoir::::insert(netuid, AlphaBalance::ZERO); + SwapBalancer::::insert(netuid, new_balancer); + return (TaoBalance::ZERO, pending_alpha); + } + + if let Some(new_balancer) = Self::try_update_balancer( + &balancer, + tao_reserve, + alpha_reserve, + pending_tao, + AlphaBalance::ZERO, + ) { + BalancerTaoReservoir::::insert(netuid, TaoBalance::ZERO); + BalancerAlphaReservoir::::insert(netuid, pending_alpha); + SwapBalancer::::insert(netuid, new_balancer); + return (pending_tao, AlphaBalance::ZERO); + } + + BalancerTaoReservoir::::insert(netuid, pending_tao); + BalancerAlphaReservoir::::insert(netuid, pending_alpha); + if pending_tao > TaoBalance::ZERO || pending_alpha > AlphaBalance::ZERO { log::warn!( - "Reserves are out of range for emission: netuid = {}, tao = {}, alpha = {}, tao_delta = {}, alpha_delta = {}", + "Reserves are out of range for emission: netuid = {}, tao = {}, alpha = {}, tao_delta = {}, alpha_delta = {}, tao_reservoir = {}, alpha_reservoir = {}", netuid, tao_reserve, alpha_reserve, tao_delta, - alpha_delta + alpha_delta, + pending_tao, + pending_alpha ); - (TaoBalance::ZERO, AlphaBalance::ZERO) - } else { - SwapBalancer::::insert(netuid, balancer); - (tao_delta, alpha_delta) } + + (TaoBalance::ZERO, AlphaBalance::ZERO) + } + + fn try_update_balancer( + balancer: &Balancer, + tao_reserve: TaoBalance, + alpha_reserve: AlphaBalance, + tao_delta: TaoBalance, + alpha_delta: AlphaBalance, + ) -> Option { + let mut new_balancer = balancer.clone(); + new_balancer + .update_weights_for_added_liquidity( + u64::from(tao_reserve), + u64::from(alpha_reserve), + u64::from(tao_delta), + u64::from(alpha_delta), + ) + .ok()?; + Some(new_balancer) } /// Executes a token swap on the specified subnet. @@ -276,6 +330,8 @@ impl Pallet { FeeRate::::remove(netuid); SwapBalancer::::remove(netuid); + BalancerTaoReservoir::::remove(netuid); + BalancerAlphaReservoir::::remove(netuid); log::debug!( "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 1d2fd07c59..630e51c49f 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -113,6 +113,15 @@ mod pallet { #[pallet::storage] pub type PalSwapInitialized = StorageMap<_, Twox64Concat, NetUid, bool, ValueQuery>; + /// TAO protocol liquidity that could not be injected without exceeding balancer weight bounds. + #[pallet::storage] + pub type BalancerTaoReservoir = StorageMap<_, Twox64Concat, NetUid, TaoBalance, ValueQuery>; + + /// Alpha protocol liquidity that could not be injected without exceeding balancer weight bounds. + #[pallet::storage] + pub type BalancerAlphaReservoir = + StorageMap<_, Twox64Concat, NetUid, AlphaBalance, ValueQuery>; + /// --- Storage for migration run status #[pallet::storage] pub type HasMigrationRun = diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index b1071294d3..84aa6524d8 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -155,6 +155,94 @@ mod dispatchables { }); } + #[test] + fn test_adjust_protocol_liquidity_injects_alpha_and_reservoirs_tao() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let tao = TaoBalance::from(1_000_u64); + let alpha = AlphaBalance::from(1_000_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + + let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(200_000_u64), + AlphaBalance::from(1_000_u64), + ); + + assert_eq!(tao_added, TaoBalance::ZERO); + assert_eq!(alpha_added, AlphaBalance::from(1_000_u64)); + assert_eq!( + BalancerTaoReservoir::::get(netuid), + TaoBalance::from(200_000_u64) + ); + assert_eq!( + BalancerAlphaReservoir::::get(netuid), + AlphaBalance::ZERO + ); + }); + } + + #[test] + fn test_adjust_protocol_liquidity_injects_tao_and_reservoirs_alpha() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let tao = TaoBalance::from(1_000_u64); + let alpha = AlphaBalance::from(1_000_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + + let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(200_000_u64), + ); + + assert_eq!(tao_added, TaoBalance::from(1_000_u64)); + assert_eq!(alpha_added, AlphaBalance::ZERO); + assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); + assert_eq!( + BalancerAlphaReservoir::::get(netuid), + AlphaBalance::from(200_000_u64) + ); + }); + } + + #[test] + fn test_adjust_protocol_liquidity_retries_reservoir_with_new_injection() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + + let mut tao = TaoBalance::from(1_000_u64); + let mut alpha = AlphaBalance::from(1_000_u64); + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + + let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(200_000_u64), + AlphaBalance::from(1_000_u64), + ); + tao += tao_added; + alpha += alpha_added; + TaoReserve::set_mock_reserve(netuid, tao); + AlphaReserve::set_mock_reserve(netuid, alpha); + + let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(200_000_u64), + ); + + assert!(tao_added > TaoBalance::ZERO); + assert!(alpha_added > AlphaBalance::ZERO); + assert!(BalancerTaoReservoir::::get(netuid) < TaoBalance::from(200_000_u64)); + assert!(BalancerAlphaReservoir::::get(netuid) < AlphaBalance::from(200_000_u64)); + }); + } + /// This test case verifies that small gradual injections (like emissions in every block) /// in the worst case /// - Do not cause price to change @@ -749,6 +837,8 @@ fn test_liquidate_pal_simple_ok_and_clears() { // Insert map values FeeRate::::insert(netuid, 1_000); PalSwapInitialized::::insert(netuid, true); + BalancerTaoReservoir::::insert(netuid, TaoBalance::from(12_345_u64)); + BalancerAlphaReservoir::::insert(netuid, AlphaBalance::from(67_890_u64)); let w_quote_pt = Perquintill::from_rational(1u128, 2u128); let bal = Balancer::new(w_quote_pt).unwrap(); SwapBalancer::::insert(netuid, bal); @@ -763,6 +853,8 @@ fn test_liquidate_pal_simple_ok_and_clears() { assert!(!FeeRate::::contains_key(netuid)); assert!(!PalSwapInitialized::::contains_key(netuid)); assert!(!SwapBalancer::::contains_key(netuid)); + assert!(!BalancerTaoReservoir::::contains_key(netuid)); + assert!(!BalancerAlphaReservoir::::contains_key(netuid)); }); } From 0da9a76bc79064e5c05f4b9dea14de532590fbc3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 15 Jun 2026 18:00:38 +0200 Subject: [PATCH 2/5] Fix adjust_protocol_liquidity in case when balancer is pushed out of weight range --- .../subtensor/src/coinbase/run_coinbase.rs | 71 +++++++----- pallets/subtensor/src/tests/coinbase.rs | 36 ++++++ pallets/subtensor/src/tests/networks.rs | 4 + pallets/swap/src/pallet/impls.rs | 33 ++++-- pallets/swap/src/pallet/tests.rs | 106 +++++++++++++----- primitives/swap-interface/src/lib.rs | 2 +- 6 files changed, 185 insertions(+), 67 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 494fc163f7..1a76984fcb 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -85,11 +85,6 @@ impl Pallet { let tao_to_swap_with: TaoBalance = tou64!(excess_tao.get(netuid_i).unwrap_or(&asfloat!(0))).into(); - // Inject tao and alpha into protocol liquidity. In theorry, it may not always - // be a success (returned values are 0s) in case of high liquidity disbalance - let (actual_injected_tao, actual_injected_alpha) = - T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); - // Clear per-block pool-side emission counters up front so a subnet // disabled this block does not display stale values from an earlier block. SubnetExcessTao::::insert(*netuid_i, TaoBalance::ZERO); @@ -130,38 +125,60 @@ impl Pallet { } } - // Inject Alpha in. - SubnetAlphaInEmission::::insert(*netuid_i, actual_injected_alpha); - - // Mint alpha and resolve to alpha reserve - Self::resolve_to_alpha_in(Self::mint_alpha(*netuid_i, actual_injected_alpha)); - - // Inject TAO in. - if !actual_injected_tao.is_zero() { - match Self::spend_tao(&subnet_account_id, remaining_credit, actual_injected_tao) - { + // Materialize this block's TAO before updating balancer reservoir + // state. If spending fails, do not let the swap pallet consume + // reservoir state as if this block's TAO arrived. + let materialized_tao_delta = if tao_in_i.is_zero() { + TaoBalance::ZERO + } else { + match Self::spend_tao(&subnet_account_id, remaining_credit, tao_in_i) { Ok(remainder) => { remaining_credit = remainder; - - SubnetTaoInEmission::::insert(*netuid_i, actual_injected_tao); - SubnetTAO::::mutate(*netuid_i, |total| { - *total = total.saturating_add(actual_injected_tao); - }); - TotalStake::::mutate(|total| { - *total = total.saturating_add(actual_injected_tao); - }); - - // Record emission injection as protocol inflow. - Self::record_protocol_inflow(*netuid_i, actual_injected_tao); + tao_in_i } Err(remainder) => { remaining_credit = remainder; let remaining_balance = remaining_credit.peek(); log::error!( - "Failed to spend credit: injected_tao = {actual_injected_tao:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" + "Failed to spend credit: tao_to_materialize = {tao_in_i:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" ); + TaoBalance::ZERO } } + }; + + // Decide which current/reservoir liquidity can become price-active + // without pushing balancer weights out of range. Only already + // materialized current TAO is offered to the swap pallet. + let ( + price_active_tao, + _tao_to_materialize, + price_active_alpha, + alpha_to_materialize, + ) = T::SwapInterface::adjust_protocol_liquidity( + *netuid_i, + materialized_tao_delta, + alpha_in_i, + ); + + // Materialize this block's alpha emission, then add only the + // price-active portion to the pool reserve. The price-active + // portion may include alpha that was materialized in an earlier + // block and held in the reservoir. + let _ = Self::mint_alpha(*netuid_i, alpha_to_materialize); + SubnetAlphaInEmission::::insert(*netuid_i, price_active_alpha); + Self::increase_provided_alpha_reserve(*netuid_i, price_active_alpha); + + // Add only the price-active TAO to the pool reserve. This may + // include TAO materialized in an earlier block and held in the + // reservoir. + if !price_active_tao.is_zero() { + SubnetTaoInEmission::::insert(*netuid_i, price_active_tao); + Self::increase_provided_tao_reserve(*netuid_i, price_active_tao); + TotalStake::::mutate(|total| { + *total = total.saturating_add(price_active_tao); + }); + Self::record_protocol_inflow(*netuid_i, price_active_tao); } } } diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 20944a03d0..abbe18e08d 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3678,6 +3678,42 @@ fn test_coinbase_inject_and_maybe_swap_does_not_skew_reserves() { }); } +#[test] +fn test_coinbase_failed_tao_materialization_does_not_activate_current_tao() { + new_test_ext(1).execute_with(|| { + let netuid = add_dynamic_network(&U256::from(1), &U256::from(2)); + let initial_reserve = TaoBalance::from(1_000_000_u64); + let reservoir_tao = TaoBalance::from(100_u64); + let current_tao = TaoBalance::from(200_u64); + let current_alpha = AlphaBalance::from(100_u64); + + mock::setup_reserves(netuid, initial_reserve, AlphaBalance::from(1_000_000_u64)); + Swap::maybe_initialize_palswap(netuid, None); + pallet_subtensor_swap::BalancerTaoReservoir::::insert(netuid, reservoir_tao); + + let tao_in = BTreeMap::from([(netuid, U96F32::saturating_from_num(current_tao))]); + let alpha_in = BTreeMap::from([(netuid, U96F32::saturating_from_num(current_alpha))]); + let excess_tao = BTreeMap::new(); + let credit = SubtensorModule::mint_tao(TaoBalance::ZERO); + + SubtensorModule::inject_and_maybe_swap(&[netuid], &tao_in, &alpha_in, &excess_tao, credit); + + assert_eq!( + SubnetTAO::::get(netuid), + initial_reserve.saturating_add(reservoir_tao) + ); + assert_eq!(SubnetTaoInEmission::::get(netuid), reservoir_tao); + assert_eq!( + SubnetProtocolFlow::::get(netuid), + reservoir_tao.to_u64() as i64 + ); + assert_eq!( + pallet_subtensor_swap::BalancerTaoReservoir::::get(netuid), + TaoBalance::ZERO + ); + }); +} + #[test] fn test_coinbase_drain_pending_increments_blockssincelaststep() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 4696507e2e..c49677bddf 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -370,6 +370,8 @@ fn dissolve_clears_all_per_subnet_storages() { SubnetAlphaOutEmission::::insert(net, AlphaBalance::from(1)); SubnetTaoInEmission::::insert(net, TaoBalance::from(1)); SubnetVolume::::insert(net, 1u128); + pallet_subtensor_swap::BalancerTaoReservoir::::insert(net, TaoBalance::from(1)); + pallet_subtensor_swap::BalancerAlphaReservoir::::insert(net, AlphaBalance::from(1)); // Items now REMOVED (not zeroed) by dissolution SubnetAlphaIn::::insert(net, AlphaBalance::from(2)); @@ -521,6 +523,8 @@ fn dissolve_clears_all_per_subnet_storages() { assert!(!SubnetAlphaOutEmission::::contains_key(net)); assert!(!SubnetTaoInEmission::::contains_key(net)); assert!(!SubnetVolume::::contains_key(net)); + assert!(!pallet_subtensor_swap::BalancerTaoReservoir::::contains_key(net)); + assert!(!pallet_subtensor_swap::BalancerAlphaReservoir::::contains_key(net)); // TAO Flow assert!(!SubnetTaoFlow::::contains_key(net)); diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 57e759f7ad..2afa9569b9 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -79,13 +79,30 @@ impl Pallet { Ok(()) } - /// Returns actually added Tao and Alpha. Amounts that would push weights - /// out of range are left in per-subnet reservoirs for a later injection. + /// Adjusts balancer weights with minted TAO and alpha liquidity to + /// maintain price. + /// + /// If weights cannot be adjusted (get pushed out of range), the excess TAO + /// and/or Alpha are added to reservoirs and an attempt to use them will be made + /// later. + /// + /// Returns: + /// 1. price-active TAO delta to add to `SubnetTAO` + /// 2. TAO delta to materialize by the caller + /// 3. price-active Alpha delta to add to `SubnetAlphaIn` + /// 4. Alpha delta to materialize by the caller + /// + /// Amounts that would push weights out of range are materialized but left in + /// per-subnet reservoirs for a later balancer update. + /// + /// Reservoir amounts may be included in the balancer update, but they are not + /// returned for materialization because they were already materialized when + /// first stored. pub(super) fn adjust_protocol_liquidity( netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) -> (TaoBalance, AlphaBalance) { + ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance) { // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); @@ -104,7 +121,7 @@ impl Pallet { BalancerTaoReservoir::::insert(netuid, TaoBalance::ZERO); BalancerAlphaReservoir::::insert(netuid, AlphaBalance::ZERO); SwapBalancer::::insert(netuid, new_balancer); - return (pending_tao, pending_alpha); + return (pending_tao, tao_delta, pending_alpha, alpha_delta); } if let Some(new_balancer) = Self::try_update_balancer( @@ -117,7 +134,7 @@ impl Pallet { BalancerTaoReservoir::::insert(netuid, pending_tao); BalancerAlphaReservoir::::insert(netuid, AlphaBalance::ZERO); SwapBalancer::::insert(netuid, new_balancer); - return (TaoBalance::ZERO, pending_alpha); + return (TaoBalance::ZERO, tao_delta, pending_alpha, alpha_delta); } if let Some(new_balancer) = Self::try_update_balancer( @@ -130,7 +147,7 @@ impl Pallet { BalancerTaoReservoir::::insert(netuid, TaoBalance::ZERO); BalancerAlphaReservoir::::insert(netuid, pending_alpha); SwapBalancer::::insert(netuid, new_balancer); - return (pending_tao, AlphaBalance::ZERO); + return (pending_tao, tao_delta, AlphaBalance::ZERO, alpha_delta); } BalancerTaoReservoir::::insert(netuid, pending_tao); @@ -148,7 +165,7 @@ impl Pallet { ); } - (TaoBalance::ZERO, AlphaBalance::ZERO) + (TaoBalance::ZERO, tao_delta, AlphaBalance::ZERO, alpha_delta) } fn try_update_balancer( @@ -446,7 +463,7 @@ impl SwapHandler for Pallet { netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) -> (TaoBalance, AlphaBalance) { + ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance) { Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 84aa6524d8..689e48e3d4 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -156,7 +156,7 @@ mod dispatchables { } #[test] - fn test_adjust_protocol_liquidity_injects_alpha_and_reservoirs_tao() { + fn test_adjust_protocol_liquidity_materializes_tao_when_reservoiring_tao() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); @@ -165,14 +165,17 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(200_000_u64), - AlphaBalance::from(1_000_u64), - ); + let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = + Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(200_000_u64), + AlphaBalance::from(1_000_u64), + ); - assert_eq!(tao_added, TaoBalance::ZERO); - assert_eq!(alpha_added, AlphaBalance::from(1_000_u64)); + assert_eq!(price_active_tao, TaoBalance::ZERO); + assert_eq!(tao_materialized, TaoBalance::from(200_000_u64)); + assert_eq!(price_active_alpha, AlphaBalance::from(1_000_u64)); + assert_eq!(alpha_materialized, AlphaBalance::from(1_000_u64)); assert_eq!( BalancerTaoReservoir::::get(netuid), TaoBalance::from(200_000_u64) @@ -185,7 +188,7 @@ mod dispatchables { } #[test] - fn test_adjust_protocol_liquidity_injects_tao_and_reservoirs_alpha() { + fn test_adjust_protocol_liquidity_materializes_alpha_when_reservoiring_alpha() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); @@ -194,14 +197,17 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(1_000_u64), - AlphaBalance::from(200_000_u64), - ); + let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = + Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(200_000_u64), + ); - assert_eq!(tao_added, TaoBalance::from(1_000_u64)); - assert_eq!(alpha_added, AlphaBalance::ZERO); + assert_eq!(price_active_tao, TaoBalance::from(1_000_u64)); + assert_eq!(tao_materialized, TaoBalance::from(1_000_u64)); + assert_eq!(price_active_alpha, AlphaBalance::ZERO); + assert_eq!(alpha_materialized, AlphaBalance::from(200_000_u64)); assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); assert_eq!( BalancerAlphaReservoir::::get(netuid), @@ -220,26 +226,64 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(200_000_u64), - AlphaBalance::from(1_000_u64), - ); - tao += tao_added; - alpha += alpha_added; + let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = + Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(200_000_u64), + AlphaBalance::from(1_000_u64), + ); + assert_eq!(price_active_tao, TaoBalance::ZERO); + assert_eq!(tao_materialized, TaoBalance::from(200_000_u64)); + assert_eq!(price_active_alpha, AlphaBalance::from(1_000_u64)); + assert_eq!(alpha_materialized, AlphaBalance::from(1_000_u64)); + tao += price_active_tao; + alpha += price_active_alpha; TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (tao_added, alpha_added) = Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(1_000_u64), - AlphaBalance::from(200_000_u64), + let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = + Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(200_000_u64), + ); + + assert!(price_active_tao >= tao_materialized); + assert!(price_active_alpha >= alpha_materialized); + assert_eq!(tao_materialized, TaoBalance::from(1_000_u64)); + assert_eq!(alpha_materialized, AlphaBalance::from(200_000_u64)); + assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); + assert_eq!( + BalancerAlphaReservoir::::get(netuid), + AlphaBalance::ZERO ); + }); + } + + #[test] + fn test_adjust_protocol_liquidity_does_not_materialize_reservoir_amounts() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); - assert!(tao_added > TaoBalance::ZERO); - assert!(alpha_added > AlphaBalance::ZERO); - assert!(BalancerTaoReservoir::::get(netuid) < TaoBalance::from(200_000_u64)); - assert!(BalancerAlphaReservoir::::get(netuid) < AlphaBalance::from(200_000_u64)); + TaoReserve::set_mock_reserve(netuid, TaoBalance::from(1_000_000_u64)); + AlphaReserve::set_mock_reserve(netuid, AlphaBalance::from(1_000_000_u64)); + BalancerTaoReservoir::::insert(netuid, TaoBalance::from(10_000_u64)); + BalancerAlphaReservoir::::insert(netuid, AlphaBalance::from(20_000_u64)); + + let tao_delta = TaoBalance::from(300_u64); + let alpha_delta = AlphaBalance::from(400_u64); + let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = + Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); + + assert_eq!(price_active_tao, TaoBalance::from(10_300_u64)); + assert_eq!(tao_materialized, tao_delta); + assert_eq!(price_active_alpha, AlphaBalance::from(20_400_u64)); + assert_eq!(alpha_materialized, alpha_delta); + assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); + assert_eq!( + BalancerAlphaReservoir::::get(netuid), + AlphaBalance::ZERO + ); }); } diff --git a/primitives/swap-interface/src/lib.rs b/primitives/swap-interface/src/lib.rs index 9980604707..85e8cacc7a 100644 --- a/primitives/swap-interface/src/lib.rs +++ b/primitives/swap-interface/src/lib.rs @@ -46,7 +46,7 @@ pub trait SwapHandler { netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) -> (TaoBalance, AlphaBalance); + ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance); fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; fn init_swap(netuid: NetUid, maybe_price: Option); fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance; From 02982dc8406645d49e9869059337b23eae318b16 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 18 Jun 2026 16:14:21 +0300 Subject: [PATCH 3/5] merge devnet-ready --- 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 5cf4d7aadb..94e25a46fb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -234,7 +234,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: 419, + spec_version: 420, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From db24fe48f10a1a8412a01a347486f45a32a88aa3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 25 Jun 2026 14:01:19 +0300 Subject: [PATCH 4/5] Address ai review: Handle reservoirs properly on network dissolution --- pallets/subtensor/src/coinbase/root.rs | 13 ++++- pallets/subtensor/src/staking/stake_utils.rs | 4 +- pallets/subtensor/src/tests/coinbase.rs | 28 ++++++++++ pallets/subtensor/src/tests/networks.rs | 54 +++++++++++++++++++- pallets/subtensor/src/tests/staking.rs | 8 +-- pallets/swap/src/pallet/impls.rs | 33 +++++++++--- pallets/swap/src/pallet/tests.rs | 15 +++--- primitives/swap-interface/src/lib.rs | 5 +- runtime/src/lib.rs | 2 +- 9 files changed, 133 insertions(+), 29 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index b4db388621..0e6bf7269a 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -210,11 +210,22 @@ impl Pallet { Error::::SubnetNotExists ); + let reservoir_tao = T::SwapInterface::protocol_tao_reservoir(netuid); + let reservoir_alpha = T::SwapInterface::protocol_alpha_reservoir(netuid); + T::SwapInterface::clear_protocol_liquidity_reservoirs(netuid); + Self::increase_provided_tao_reserve(netuid, reservoir_tao); + Self::increase_provided_alpha_reserve(netuid, reservoir_alpha); + if !reservoir_tao.is_zero() { + TotalStake::::mutate(|total| { + *total = total.saturating_add(reservoir_tao); + }); + } + Self::finalize_all_subnet_root_dividends(netuid); // --- Perform the cleanup before removing the network. Self::destroy_alpha_in_out_stakes(netuid)?; - T::SwapInterface::clear_protocol_liquidity(netuid)?; + T::SwapInterface::clear_protocol_liquidity(netuid); T::CommitmentsInterface::purge_netuid(netuid); // --- Remove the network diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 2826590c50..8327a9a72b 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -18,7 +18,9 @@ impl Pallet { /// # Returns /// * `u64` - The total alpha issuance for the specified subnet. pub fn get_alpha_issuance(netuid: NetUid) -> AlphaBalance { - SubnetAlphaIn::::get(netuid).saturating_add(SubnetAlphaOut::::get(netuid)) + SubnetAlphaIn::::get(netuid) + .saturating_add(SubnetAlphaOut::::get(netuid)) + .saturating_add(T::SwapInterface::protocol_alpha_reservoir(netuid)) } pub fn get_moving_alpha_price(netuid: NetUid) -> U64F64 { diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index cc508891e0..be23066c34 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -3737,6 +3737,34 @@ fn test_coinbase_failed_tao_materialization_does_not_activate_current_tao() { }); } +#[test] +fn test_alpha_reservoir_counts_toward_subnet_issuance_across_blocks() { + new_test_ext(1).execute_with(|| { + let netuid = add_dynamic_network(&U256::from(1), &U256::from(2)); + let alpha_in = AlphaBalance::from(10_000_u64); + let alpha_out = AlphaBalance::from(20_000_u64); + let reservoir_alpha = AlphaBalance::from(30_000_u64); + + SubnetAlphaIn::::insert(netuid, alpha_in); + SubnetAlphaOut::::insert(netuid, alpha_out); + pallet_subtensor_swap::BalancerAlphaReservoir::::insert(netuid, reservoir_alpha); + + let expected = alpha_in + .saturating_add(alpha_out) + .saturating_add(reservoir_alpha); + assert_eq!(SubtensorModule::get_alpha_issuance(netuid), expected); + + System::set_block_number(System::block_number().saturating_add(1)); + + assert_eq!(SubnetAlphaIn::::get(netuid), alpha_in); + assert_eq!( + pallet_subtensor_swap::BalancerAlphaReservoir::::get(netuid), + reservoir_alpha + ); + assert_eq!(SubtensorModule::get_alpha_issuance(netuid), expected); + }); +} + #[test] fn test_coinbase_drain_pending_increments_blockssincelaststep() { new_test_ext(1).execute_with(|| { diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 38d0d49fe1..20531e370d 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -370,8 +370,6 @@ fn dissolve_clears_all_per_subnet_storages() { SubnetAlphaOutEmission::::insert(net, AlphaBalance::from(1)); SubnetTaoInEmission::::insert(net, TaoBalance::from(1)); SubnetVolume::::insert(net, 1u128); - pallet_subtensor_swap::BalancerTaoReservoir::::insert(net, TaoBalance::from(1)); - pallet_subtensor_swap::BalancerAlphaReservoir::::insert(net, AlphaBalance::from(1)); // Items now REMOVED (not zeroed) by dissolution SubnetAlphaIn::::insert(net, AlphaBalance::from(2)); @@ -647,6 +645,58 @@ fn dissolve_clears_all_per_subnet_storages() { }); } +#[test] +fn dissolve_materializes_nonzero_protocol_reservoirs_before_cleanup() { + new_test_ext(0).execute_with(|| { + let owner_cold = U256::from(123); + let owner_hot = U256::from(456); + let net = add_dynamic_network(&owner_hot, &owner_cold); + remove_owner_registration_stake(net); + + // Force the modern dissolve branch where pool alpha participates in + // the protocol denominator. + TaoInRefundDeploymentBlock::::put(0); + NetworkRegisteredAt::::insert(net, 1); + + let reservoir_tao = TaoBalance::from(100_u64); + let reservoir_alpha = AlphaBalance::from(100_u64); + let staker_hot = U256::from(789); + let staker_cold = U256::from(987); + + let subnet_account = SubtensorModule::get_subnet_account_id(net).unwrap(); + add_balance_to_coldkey_account(&subnet_account, reservoir_tao); + + SubnetTAO::::insert(net, TaoBalance::ZERO); + SubtensorModule::set_subnet_locked_balance(net, TaoBalance::ZERO); + SubnetAlphaIn::::insert(net, AlphaBalance::ZERO); + SubnetProtocolAlpha::::insert(net, AlphaBalance::ZERO); + AlphaV2::::insert((staker_hot, staker_cold, net), sf_from_u64(100u64)); + TotalHotkeyAlpha::::insert(staker_hot, net, AlphaBalance::from(100u64)); + pallet_subtensor_swap::BalancerTaoReservoir::::insert(net, reservoir_tao); + pallet_subtensor_swap::BalancerAlphaReservoir::::insert(net, reservoir_alpha); + + let staker_before = SubtensorModule::get_coldkey_balance(&staker_cold); + let issuance_before = TotalIssuance::::get(); + + assert_ok!(SubtensorModule::do_dissolve_network(net)); + + // Reservoir alpha is treated like materialized protocol pool alpha. + // The staker owns half the denominator, so receives half the reservoir + // TAO pot; the protocol share is recycled. + assert_eq!( + SubtensorModule::get_coldkey_balance(&staker_cold), + staker_before + TaoBalance::from(50_u64) + ); + assert!(TotalIssuance::::get() < issuance_before); + assert!(!NetworksAdded::::contains_key(net)); + assert!(!SubnetOwner::::contains_key(net)); + assert!(!SubnetAlphaIn::::contains_key(net)); + assert!(!SubnetProtocolAlpha::::contains_key(net)); + assert!(!pallet_subtensor_swap::BalancerTaoReservoir::::contains_key(net)); + assert!(!pallet_subtensor_swap::BalancerAlphaReservoir::::contains_key(net)); + }); +} + #[test] fn dissolve_alpha_out_but_zero_tao_no_rewards() { new_test_ext(0).execute_with(|| { diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 660b6957d7..4ecbd9f7a8 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -4279,12 +4279,8 @@ fn test_move_stake_limit_partial() { // Registration now goes through the burn/swap path, which initializes swap V3 state. // Clear that state first so the manual reserve fixture below actually controls price. - assert_ok!( - ::SwapInterface::clear_protocol_liquidity(origin_netuid) - ); - assert_ok!( - ::SwapInterface::clear_protocol_liquidity(destination_netuid) - ); + ::SwapInterface::clear_protocol_liquidity(origin_netuid); + ::SwapInterface::clear_protocol_liquidity(destination_netuid); // Force-set alpha in and tao reserve to make price equal 1.5 on both origin and destination, // but there's much more liquidity on destination, so its price wouldn't go up when restaked. diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 2afa9569b9..ea24f7e821 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -6,7 +6,7 @@ use frame_support::{ }; use safe_math::*; use sp_arithmetic::Perquintill; -use sp_runtime::{DispatchResult, traits::AccountIdConversion}; +use sp_runtime::traits::AccountIdConversion; use substrate_fixed::types::U64F64; use subtensor_runtime_common::{AlphaBalance, NetUid, SubnetInfo, TaoBalance, Token, TokenReserve}; use subtensor_swap_interface::{ @@ -335,7 +335,15 @@ impl Pallet { } /// Clear **protocol-owned** liquidity and wipe all swap state for `netuid`. - pub fn do_clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { + pub fn do_clear_protocol_liquidity(netuid: NetUid) { + // Reservoir balances are materialized protocol liquidity that never became + // price-active. Fold them into the reserve abstraction first so cleanup + // clears them through the same path as active protocol liquidity. + let reservoir_tao = BalancerTaoReservoir::::take(netuid); + let reservoir_alpha = BalancerAlphaReservoir::::take(netuid); + T::TaoReserve::increase_provided(netuid.into(), reservoir_tao); + T::AlphaReserve::increase_provided(netuid.into(), reservoir_alpha); + // 1) Force-close protocol liquidity, burning proceeds. let burned_tao = T::TaoReserve::reserve(netuid.into()); let burned_alpha = T::AlphaReserve::reserve(netuid.into()); @@ -347,14 +355,10 @@ impl Pallet { FeeRate::::remove(netuid); SwapBalancer::::remove(netuid); - BalancerTaoReservoir::::remove(netuid); - BalancerAlphaReservoir::::remove(netuid); log::debug!( "clear_protocol_liquidity: netuid={netuid:?}, protocol_burned: τ={burned_tao:?}, α={burned_alpha:?}; state cleared" ); - - Ok(()) } } @@ -467,8 +471,21 @@ impl SwapHandler for Pallet { Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) } - fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult { - Self::do_clear_protocol_liquidity(netuid) + fn protocol_alpha_reservoir(netuid: NetUid) -> AlphaBalance { + BalancerAlphaReservoir::::get(netuid) + } + + fn protocol_tao_reservoir(netuid: NetUid) -> TaoBalance { + BalancerTaoReservoir::::get(netuid) + } + + fn clear_protocol_liquidity_reservoirs(netuid: NetUid) { + BalancerTaoReservoir::::remove(netuid); + BalancerAlphaReservoir::::remove(netuid); + } + + fn clear_protocol_liquidity(netuid: NetUid) { + Self::do_clear_protocol_liquidity(netuid); } fn init_swap(netuid: NetUid, maybe_price: Option) { diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 689e48e3d4..997dfdc2e0 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -870,11 +870,10 @@ fn print_current_price(netuid: NetUid) { log::trace!("Current price: {current_price:.6}"); } -/// Simple palswap path: PalSwap is initialized, but no positions, only protocol; function -/// must still clear any residual storages and succeed. -/// TODO: Revise when user liquidity is available +/// Reservoir liquidity is already materialized but not price-active; direct +/// cleanup materializes it into the reserve abstraction before clearing. #[test] -fn test_liquidate_pal_simple_ok_and_clears() { +fn test_clear_protocol_liquidity_clears_nonzero_reservoirs() { new_test_ext().execute_with(|| { let netuid = NetUid::from(202); @@ -890,10 +889,8 @@ fn test_liquidate_pal_simple_ok_and_clears() { // Sanity: PalSwap is not initialized assert!(PalSwapInitialized::::get(netuid)); - // ACT - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + Pallet::::do_clear_protocol_liquidity(netuid); - // All single-key maps should not have the key after liquidation assert!(!FeeRate::::contains_key(netuid)); assert!(!PalSwapInitialized::::contains_key(netuid)); assert!(!SwapBalancer::::contains_key(netuid)); @@ -917,7 +914,7 @@ fn test_clear_protocol_liquidity_green_path() { // --- Act --- // Green path: just clear protocol liquidity and wipe all V3 state. - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + Pallet::::do_clear_protocol_liquidity(netuid); // Flags assert!(!PalSwapInitialized::::contains_key(netuid)); @@ -926,7 +923,7 @@ fn test_clear_protocol_liquidity_green_path() { assert!(!FeeRate::::contains_key(netuid)); // --- And it's idempotent --- - assert_ok!(Pallet::::do_clear_protocol_liquidity(netuid)); + Pallet::::do_clear_protocol_liquidity(netuid); assert!(!PalSwapInitialized::::contains_key(netuid)); }); } diff --git a/primitives/swap-interface/src/lib.rs b/primitives/swap-interface/src/lib.rs index 85e8cacc7a..b813f193bc 100644 --- a/primitives/swap-interface/src/lib.rs +++ b/primitives/swap-interface/src/lib.rs @@ -47,7 +47,10 @@ pub trait SwapHandler { tao_delta: TaoBalance, alpha_delta: AlphaBalance, ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance); - fn clear_protocol_liquidity(netuid: NetUid) -> DispatchResult; + fn protocol_alpha_reservoir(netuid: NetUid) -> AlphaBalance; + fn protocol_tao_reservoir(netuid: NetUid) -> TaoBalance; + fn clear_protocol_liquidity_reservoirs(netuid: NetUid); + fn clear_protocol_liquidity(netuid: NetUid); fn init_swap(netuid: NetUid, maybe_price: Option); fn get_alpha_amount_for_tao(netuid: NetUid, tao_amount: TaoBalance) -> AlphaBalance; } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a39c473880..52a517873a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -234,7 +234,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: 423, + spec_version: 424, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 447ca995a325ba6ab2720237f70576d3e4e3936e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 25 Jun 2026 21:46:11 +0300 Subject: [PATCH 5/5] Refactor adjust_protocol_liquidity --- .../subtensor/src/coinbase/run_coinbase.rs | 20 +++--- pallets/swap/src/pallet/impls.rs | 21 +++---- pallets/swap/src/pallet/tests.rs | 62 +++++++------------ primitives/swap-interface/src/lib.rs | 2 +- 4 files changed, 42 insertions(+), 63 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index a66a0aaa73..117ef4ed32 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -140,7 +140,7 @@ impl Pallet { remaining_credit = remainder; let remaining_balance = remaining_credit.peek(); log::error!( - "Failed to spend credit: tao_to_materialize = {tao_in_i:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" + "Failed to spend credit: tao_delta = {tao_in_i:?}, netuid_i = {netuid_i:?}, remaining_balance = {remaining_balance:?}" ); TaoBalance::ZERO } @@ -150,22 +150,18 @@ impl Pallet { // Decide which current/reservoir liquidity can become price-active // without pushing balancer weights out of range. Only already // materialized current TAO is offered to the swap pallet. - let ( - price_active_tao, - _tao_to_materialize, - price_active_alpha, - alpha_to_materialize, - ) = T::SwapInterface::adjust_protocol_liquidity( - *netuid_i, - materialized_tao_delta, - alpha_in_i, - ); + let (price_active_tao, price_active_alpha) = + T::SwapInterface::adjust_protocol_liquidity( + *netuid_i, + materialized_tao_delta, + alpha_in_i, + ); // Materialize this block's alpha emission, then add only the // price-active portion to the pool reserve. The price-active // portion may include alpha that was materialized in an earlier // block and held in the reservoir. - let _ = Self::mint_alpha(*netuid_i, alpha_to_materialize); + let _ = Self::mint_alpha(*netuid_i, alpha_in_i); SubnetAlphaInEmission::::insert(*netuid_i, price_active_alpha); Self::increase_provided_alpha_reserve(*netuid_i, price_active_alpha); diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index ea24f7e821..07c4ffc62c 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -88,21 +88,18 @@ impl Pallet { /// /// Returns: /// 1. price-active TAO delta to add to `SubnetTAO` - /// 2. TAO delta to materialize by the caller - /// 3. price-active Alpha delta to add to `SubnetAlphaIn` - /// 4. Alpha delta to materialize by the caller + /// 2. price-active Alpha delta to add to `SubnetAlphaIn` /// /// Amounts that would push weights out of range are materialized but left in /// per-subnet reservoirs for a later balancer update. /// - /// Reservoir amounts may be included in the balancer update, but they are not - /// returned for materialization because they were already materialized when - /// first stored. + /// The caller is responsible for materializing the current `tao_delta` and + /// `alpha_delta`; reservoir amounts were materialized when first stored. pub(super) fn adjust_protocol_liquidity( netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance) { + ) -> (TaoBalance, AlphaBalance) { // Get reserves let alpha_reserve = T::AlphaReserve::reserve(netuid.into()); let tao_reserve = T::TaoReserve::reserve(netuid.into()); @@ -121,7 +118,7 @@ impl Pallet { BalancerTaoReservoir::::insert(netuid, TaoBalance::ZERO); BalancerAlphaReservoir::::insert(netuid, AlphaBalance::ZERO); SwapBalancer::::insert(netuid, new_balancer); - return (pending_tao, tao_delta, pending_alpha, alpha_delta); + return (pending_tao, pending_alpha); } if let Some(new_balancer) = Self::try_update_balancer( @@ -134,7 +131,7 @@ impl Pallet { BalancerTaoReservoir::::insert(netuid, pending_tao); BalancerAlphaReservoir::::insert(netuid, AlphaBalance::ZERO); SwapBalancer::::insert(netuid, new_balancer); - return (TaoBalance::ZERO, tao_delta, pending_alpha, alpha_delta); + return (TaoBalance::ZERO, pending_alpha); } if let Some(new_balancer) = Self::try_update_balancer( @@ -147,7 +144,7 @@ impl Pallet { BalancerTaoReservoir::::insert(netuid, TaoBalance::ZERO); BalancerAlphaReservoir::::insert(netuid, pending_alpha); SwapBalancer::::insert(netuid, new_balancer); - return (pending_tao, tao_delta, AlphaBalance::ZERO, alpha_delta); + return (pending_tao, AlphaBalance::ZERO); } BalancerTaoReservoir::::insert(netuid, pending_tao); @@ -165,7 +162,7 @@ impl Pallet { ); } - (TaoBalance::ZERO, tao_delta, AlphaBalance::ZERO, alpha_delta) + (TaoBalance::ZERO, AlphaBalance::ZERO) } fn try_update_balancer( @@ -467,7 +464,7 @@ impl SwapHandler for Pallet { netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance) { + ) -> (TaoBalance, AlphaBalance) { Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta) } diff --git a/pallets/swap/src/pallet/tests.rs b/pallets/swap/src/pallet/tests.rs index 997dfdc2e0..eafb5a8edd 100644 --- a/pallets/swap/src/pallet/tests.rs +++ b/pallets/swap/src/pallet/tests.rs @@ -165,17 +165,14 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = - Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(200_000_u64), - AlphaBalance::from(1_000_u64), - ); + let (price_active_tao, price_active_alpha) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(200_000_u64), + AlphaBalance::from(1_000_u64), + ); assert_eq!(price_active_tao, TaoBalance::ZERO); - assert_eq!(tao_materialized, TaoBalance::from(200_000_u64)); assert_eq!(price_active_alpha, AlphaBalance::from(1_000_u64)); - assert_eq!(alpha_materialized, AlphaBalance::from(1_000_u64)); assert_eq!( BalancerTaoReservoir::::get(netuid), TaoBalance::from(200_000_u64) @@ -197,17 +194,14 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = - Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(1_000_u64), - AlphaBalance::from(200_000_u64), - ); + let (price_active_tao, price_active_alpha) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(200_000_u64), + ); assert_eq!(price_active_tao, TaoBalance::from(1_000_u64)); - assert_eq!(tao_materialized, TaoBalance::from(1_000_u64)); assert_eq!(price_active_alpha, AlphaBalance::ZERO); - assert_eq!(alpha_materialized, AlphaBalance::from(200_000_u64)); assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); assert_eq!( BalancerAlphaReservoir::::get(netuid), @@ -226,32 +220,26 @@ mod dispatchables { TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = - Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(200_000_u64), - AlphaBalance::from(1_000_u64), - ); + let (price_active_tao, price_active_alpha) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(200_000_u64), + AlphaBalance::from(1_000_u64), + ); assert_eq!(price_active_tao, TaoBalance::ZERO); - assert_eq!(tao_materialized, TaoBalance::from(200_000_u64)); assert_eq!(price_active_alpha, AlphaBalance::from(1_000_u64)); - assert_eq!(alpha_materialized, AlphaBalance::from(1_000_u64)); tao += price_active_tao; alpha += price_active_alpha; TaoReserve::set_mock_reserve(netuid, tao); AlphaReserve::set_mock_reserve(netuid, alpha); - let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = - Swap::adjust_protocol_liquidity( - netuid, - TaoBalance::from(1_000_u64), - AlphaBalance::from(200_000_u64), - ); + let (price_active_tao, price_active_alpha) = Swap::adjust_protocol_liquidity( + netuid, + TaoBalance::from(1_000_u64), + AlphaBalance::from(200_000_u64), + ); - assert!(price_active_tao >= tao_materialized); - assert!(price_active_alpha >= alpha_materialized); - assert_eq!(tao_materialized, TaoBalance::from(1_000_u64)); - assert_eq!(alpha_materialized, AlphaBalance::from(200_000_u64)); + assert!(price_active_tao >= TaoBalance::from(1_000_u64)); + assert!(price_active_alpha >= AlphaBalance::from(200_000_u64)); assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); assert_eq!( BalancerAlphaReservoir::::get(netuid), @@ -261,7 +249,7 @@ mod dispatchables { } #[test] - fn test_adjust_protocol_liquidity_does_not_materialize_reservoir_amounts() { + fn test_adjust_protocol_liquidity_activates_reservoir_amounts() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); @@ -272,13 +260,11 @@ mod dispatchables { let tao_delta = TaoBalance::from(300_u64); let alpha_delta = AlphaBalance::from(400_u64); - let (price_active_tao, tao_materialized, price_active_alpha, alpha_materialized) = + let (price_active_tao, price_active_alpha) = Swap::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); assert_eq!(price_active_tao, TaoBalance::from(10_300_u64)); - assert_eq!(tao_materialized, tao_delta); assert_eq!(price_active_alpha, AlphaBalance::from(20_400_u64)); - assert_eq!(alpha_materialized, alpha_delta); assert_eq!(BalancerTaoReservoir::::get(netuid), TaoBalance::ZERO); assert_eq!( BalancerAlphaReservoir::::get(netuid), diff --git a/primitives/swap-interface/src/lib.rs b/primitives/swap-interface/src/lib.rs index b813f193bc..5b202a8bae 100644 --- a/primitives/swap-interface/src/lib.rs +++ b/primitives/swap-interface/src/lib.rs @@ -46,7 +46,7 @@ pub trait SwapHandler { netuid: NetUid, tao_delta: TaoBalance, alpha_delta: AlphaBalance, - ) -> (TaoBalance, TaoBalance, AlphaBalance, AlphaBalance); + ) -> (TaoBalance, AlphaBalance); fn protocol_alpha_reservoir(netuid: NetUid) -> AlphaBalance; fn protocol_tao_reservoir(netuid: NetUid) -> TaoBalance; fn clear_protocol_liquidity_reservoirs(netuid: NetUid);