diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 1274a84a6f..0dc8440980 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -140,7 +140,7 @@ impl Pallet { *total = total.saturating_add(tao_in_i); }); // Adjust protocol liquidity based on new reserves - T::SwapInterface::adjust_protocol_liquidity(*netuid_i); + T::SwapInterface::adjust_protocol_liquidity(*netuid_i, tao_in_i, alpha_in_i); } // --- 5. Compute owner cuts and remove them from alpha_out remaining. diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index f4801d63f0..973233bd9e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1062,7 +1062,6 @@ pub mod pallet { StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; #[pallet::storage] /// --- MAP ( netuid ) --> alpha_supply_in_subnet | Returns the amount of alpha in the subnet. - /// TODO: Deprecate, not accurate and not used in v3 anymore pub type SubnetAlphaOut = StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultZeroU64>; #[pallet::storage] // --- MAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it @@ -2573,6 +2572,11 @@ impl> Error::::HotKeyAccountNotExists ); + // Increse alpha out counter + SubnetAlphaOut::::mutate(netuid, |total| { + *total = total.saturating_add(alpha); + }); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha); Ok(()) @@ -2589,6 +2593,11 @@ impl> Error::::HotKeyAccountNotExists ); + // Decrese alpha out counter + SubnetAlphaOut::::mutate(netuid, |total| { + *total = total.saturating_sub(alpha); + }); + Ok(Self::decrease_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, netuid, alpha, )) diff --git a/pallets/swap-interface/src/lib.rs b/pallets/swap-interface/src/lib.rs index f09928a2b2..10f3cb0fb2 100644 --- a/pallets/swap-interface/src/lib.rs +++ b/pallets/swap-interface/src/lib.rs @@ -27,7 +27,7 @@ pub trait SwapHandler { fn current_alpha_price(netuid: NetUid) -> U96F32; fn max_price() -> u64; fn min_price() -> u64; - fn adjust_protocol_liquidity(netuid: NetUid); + fn adjust_protocol_liquidity(netuid: NetUid, tao_delta: u64, alpha_delta: u64); fn is_user_liquidity_enabled(netuid: NetUid) -> bool; } @@ -47,4 +47,5 @@ pub struct UpdateLiquidityResult { pub alpha: u64, pub fee_tao: u64, pub fee_alpha: u64, + pub removed: bool, } diff --git a/pallets/swap/src/pallet/impls.rs b/pallets/swap/src/pallet/impls.rs index 3f54dbfd74..08cd2c4b74 100644 --- a/pallets/swap/src/pallet/impls.rs +++ b/pallets/swap/src/pallet/impls.rs @@ -326,14 +326,7 @@ impl Pallet { } /// Adjusts protocol liquidity with new values of TAO and Alpha reserve - pub(super) fn adjust_protocol_liquidity(netuid: NetUid) { - // Get updated reserves, calculate liquidity - let tao_reserve = ::SubnetInfo::tao_reserve(netuid.into()); - let alpha_reserve = ::SubnetInfo::alpha_reserve(netuid.into()); - let liquidity = - helpers_128bit::sqrt((tao_reserve as u128).saturating_mul(alpha_reserve as u128)) - as u64; - + pub(super) fn adjust_protocol_liquidity(netuid: NetUid, tao_delta: u64, alpha_delta: u64) { // Update protocol position with new liquidity let protocol_account_id = Self::protocol_account_id(); let mut positions = @@ -341,8 +334,22 @@ impl Pallet { .collect::>(); if let Some(position) = positions.get_mut(0) { - position.liquidity = liquidity; - Positions::::insert((netuid, protocol_account_id, position.id), position.clone()); + let current_sqrt_price = Pallet::::current_price_sqrt(netuid); + let maybe_token_amounts = position.to_token_amounts(current_sqrt_price); + if let Ok((tao, alpha)) = maybe_token_amounts { + // Get updated reserves, calculate liquidity + let new_tao_reserve = tao.saturating_add(tao_delta); + let new_alpha_reserve = alpha.saturating_add(alpha_delta); + let new_liquidity = helpers_128bit::sqrt( + (new_tao_reserve as u128).saturating_mul(new_alpha_reserve as u128), + ) as u64; + + position.liquidity = new_liquidity; + Positions::::insert( + (netuid, protocol_account_id, position.id), + position.clone(), + ); + } } } @@ -855,6 +862,7 @@ impl Pallet { alpha, fee_tao, fee_alpha, + removed: true, }) } @@ -938,10 +946,12 @@ impl Pallet { // If delta brings the position liquidity below MinimumLiquidity, eliminate position and // withdraw full amounts + let mut remove = false; if (liquidity_delta < 0) && (position.liquidity.saturating_sub(delta_liquidity_abs) < T::MinimumLiquidity::get()) { delta_liquidity_abs = position.liquidity; + remove = true; } // Adjust liquidity at the ticks based on the delta sign @@ -960,15 +970,20 @@ impl Pallet { // Remove liquidity from user position position.liquidity = position.liquidity.saturating_sub(delta_liquidity_abs); } - Positions::::insert(&(netuid, coldkey_account_id, position.id), position); - // TODO: Withdraw balances and update pool reserves + // Update or, in case if full liquidity is removed, remove the position + if remove { + Positions::::remove((netuid, coldkey_account_id, position_id)); + } else { + Positions::::insert(&(netuid, coldkey_account_id, position.id), position); + } Ok(UpdateLiquidityResult { tao: tao.saturating_to_num::(), alpha: alpha.saturating_to_num::(), fee_tao, fee_alpha, + removed: remove, }) } @@ -1173,8 +1188,8 @@ impl SwapHandler for Pallet { .saturating_to_num() } - fn adjust_protocol_liquidity(netuid: NetUid) { - Self::adjust_protocol_liquidity(netuid); + fn adjust_protocol_liquidity(netuid: NetUid, tao_delta: u64, alpha_delta: u64) { + Self::adjust_protocol_liquidity(netuid, tao_delta, alpha_delta); } fn is_user_liquidity_enabled(netuid: NetUid) -> bool { diff --git a/pallets/swap/src/pallet/mod.rs b/pallets/swap/src/pallet/mod.rs index 3a0e00bc8d..d226605d40 100644 --- a/pallets/swap/src/pallet/mod.rs +++ b/pallets/swap/src/pallet/mod.rs @@ -1,4 +1,5 @@ use core::num::NonZeroU64; +use core::ops::Neg; use frame_support::{PalletId, pallet_prelude::*, traits::Get}; use frame_system::pallet_prelude::*; @@ -149,11 +150,11 @@ mod pallet { /// First enable even indicates a switch from V2 to V3 swap. UserLiquidityToggled { netuid: NetUid, enable: bool }, - /// Event emitted when liquidity is added to a subnet's liquidity pool. + /// Event emitted when a liquidity position is added to a subnet's liquidity pool. LiquidityAdded { /// The coldkey account that owns the position coldkey: T::AccountId, - /// The hotkey account associated with the position + /// The hotkey account where Alpha comes from hotkey: T::AccountId, /// The subnet identifier netuid: NetUid, @@ -167,10 +168,12 @@ mod pallet { alpha: u64, }, - /// Event emitted when liquidity is removed from a subnet's liquidity pool. + /// Event emitted when a liquidity position is removed from a subnet's liquidity pool. LiquidityRemoved { /// The coldkey account that owns the position coldkey: T::AccountId, + /// The hotkey account where Alpha goes to + hotkey: T::AccountId, /// The subnet identifier netuid: NetUid, /// Unique identifier for the liquidity position @@ -184,6 +187,29 @@ mod pallet { /// The amount of Alpha fees earned from the position fee_alpha: u64, }, + + /// Event emitted when a liquidity position is modified in a subnet's liquidity pool. + /// Modifying causes the fees to be claimed. + LiquidityModified { + /// The coldkey account that owns the position + coldkey: T::AccountId, + /// The hotkey account where Alpha comes from or goes to + hotkey: T::AccountId, + /// The subnet identifier + netuid: NetUid, + /// Unique identifier for the liquidity position + position_id: PositionId, + /// The amount of liquidity added to or removed from the position + liquidity: i64, + /// The amount of TAO tokens returned to the user + tao: i64, + /// The amount of Alpha tokens returned to the user + alpha: i64, + /// The amount of TAO fees earned from the position + fee_tao: u64, + /// The amount of Alpha fees earned from the position + fee_alpha: u64, + }, } #[pallet::error] @@ -402,6 +428,7 @@ mod pallet { // Emit an event Self::deposit_event(Event::LiquidityRemoved { coldkey, + hotkey, netuid: netuid.into(), position_id, tao: result.tao, @@ -456,14 +483,16 @@ mod pallet { ); // Emit an event - Self::deposit_event(Event::LiquidityAdded { + Self::deposit_event(Event::LiquidityModified { coldkey: coldkey.clone(), hotkey: hotkey.clone(), netuid, position_id, - liquidity: liquidity_delta as u64, - tao: result.tao, - alpha: result.alpha, + liquidity: liquidity_delta, + tao: result.tao as i64, + alpha: result.alpha as i64, + fee_tao: result.fee_tao, + fee_alpha: result.fee_alpha, }); } else { // Credit the returned tao and alpha to the account @@ -471,15 +500,30 @@ mod pallet { T::BalanceOps::increase_stake(&coldkey, &hotkey, netuid.into(), result.alpha)?; // Emit an event - Self::deposit_event(Event::LiquidityRemoved { - coldkey: coldkey.clone(), - netuid, - position_id, - tao: result.tao, - alpha: result.alpha, - fee_tao: result.fee_tao, - fee_alpha: result.fee_alpha, - }); + if result.removed { + Self::deposit_event(Event::LiquidityRemoved { + coldkey: coldkey.clone(), + hotkey: hotkey.clone(), + netuid, + position_id, + tao: result.tao, + alpha: result.alpha, + fee_tao: result.fee_tao, + fee_alpha: result.fee_alpha, + }); + } else { + Self::deposit_event(Event::LiquidityModified { + coldkey: coldkey.clone(), + hotkey: hotkey.clone(), + netuid, + position_id, + liquidity: liquidity_delta, + tao: (result.tao as i64).neg(), + alpha: (result.alpha as i64).neg(), + fee_tao: result.fee_tao, + fee_alpha: result.fee_alpha, + }); + } } // Credit accrued fees to user account (no matter if liquidity is added or removed)