Skip to content

Commit 5351bcf

Browse files
authored
Add modify_position extrinsic, fix active ticks management (#1613)
2 parents 42bf8eb + a2e112a commit 5351bcf

File tree

9 files changed

+157
-38
lines changed

9 files changed

+157
-38
lines changed

pallets/subtensor/src/macros/dispatches.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,5 +2149,94 @@ mod dispatches {
21492149

21502150
Ok(())
21512151
}
2152+
2153+
/// Modify a liquidity position.
2154+
///
2155+
/// Parameters:
2156+
/// - origin: The origin of the transaction
2157+
/// - netuid: Subnet ID
2158+
/// - position_id: ID of the position to remove
2159+
/// - liquidity_delta: Liquidity to add (if positive) or remove (if negative)
2160+
///
2161+
/// Emits `Event::LiquidityRemoved` on success
2162+
#[pallet::call_index(105)]
2163+
#[pallet::weight((
2164+
Weight::from_parts(50_000_000, 0)
2165+
.saturating_add(T::DbWeight::get().reads(4))
2166+
.saturating_add(T::DbWeight::get().writes(4)),
2167+
DispatchClass::Operational,
2168+
Pays::Yes
2169+
))]
2170+
pub fn modify_position(
2171+
origin: OriginFor<T>,
2172+
hotkey: T::AccountId,
2173+
netuid: u16,
2174+
position_id: u128,
2175+
liquidity_delta: i64,
2176+
) -> DispatchResult {
2177+
let coldkey = ensure_signed(origin)?;
2178+
2179+
// Ensure that the subnet exists.
2180+
ensure!(
2181+
Self::if_subnet_exist(netuid),
2182+
Error::<T>::SubNetworkDoesNotExist
2183+
);
2184+
2185+
// Ensure the hotkey account exists
2186+
ensure!(
2187+
Self::hotkey_account_exists(&hotkey),
2188+
Error::<T>::HotKeyAccountNotExists
2189+
);
2190+
2191+
// Add or remove liquidity
2192+
let result = T::SwapInterface::modify_position(netuid, &coldkey, &hotkey, position_id, liquidity_delta)?;
2193+
2194+
if liquidity_delta > 0 {
2195+
// Remove TAO and Alpha balances or fail transaction if they can't be removed exactly
2196+
let tao_provided = Self::remove_balance_from_coldkey_account(&coldkey, result.tao)?;
2197+
ensure!(tao_provided == result.tao, Error::<T>::InsufficientBalance);
2198+
2199+
let alpha_provided = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
2200+
&hotkey, &coldkey, netuid, result.alpha,
2201+
);
2202+
ensure!(alpha_provided == result.alpha, Error::<T>::InsufficientBalance);
2203+
2204+
// Emit an event
2205+
Self::deposit_event(Event::LiquidityAdded {
2206+
coldkey,
2207+
hotkey,
2208+
netuid,
2209+
position_id,
2210+
liquidity: liquidity_delta as u64,
2211+
tao: result.tao,
2212+
alpha: result.alpha,
2213+
});
2214+
} else {
2215+
// Credit the returned tao and alpha to the account
2216+
Self::add_balance_to_coldkey_account(
2217+
&coldkey,
2218+
result.tao.saturating_add(result.fee_tao),
2219+
);
2220+
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
2221+
&hotkey,
2222+
&coldkey,
2223+
netuid,
2224+
result.alpha.saturating_add(result.fee_alpha),
2225+
);
2226+
2227+
// Emit an event
2228+
Self::deposit_event(Event::LiquidityRemoved {
2229+
coldkey,
2230+
netuid: netuid.into(),
2231+
position_id,
2232+
tao: result.tao,
2233+
alpha: result.alpha,
2234+
fee_tao: result.fee_tao,
2235+
fee_alpha: result.fee_alpha,
2236+
});
2237+
}
2238+
2239+
Ok(())
2240+
}
21522241
}
21532242
}

pallets/subtensor/src/staking/stake_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use safe_math::*;
33
use share_pool::{SharePool, SharePoolDataOperations};
44
use sp_std::ops::Neg;
55
use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32};
6-
use subtensor_swap_interface::{LiquidityDataProvider, OrderType, SwapHandler, SwapResult};
6+
use subtensor_swap_interface::{OrderType, SwapHandler, SwapResult};
77

88
impl<T: Config> Pallet<T> {
99
/// Retrieves the total alpha issuance for a given subnet.

pallets/subtensor/src/tests/mock.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ use sp_runtime::{
2121
traits::{BlakeTwo256, IdentityLookup},
2222
};
2323
use sp_std::cmp::Ordering;
24-
use substrate_fixed::types::U64F64;
25-
use subtensor_swap_interface::{LiquidityDataProvider, OrderType, SwapHandler};
24+
use subtensor_swap_interface::{OrderType, SwapHandler};
2625

2726
use crate::utils::rate_limiting::TransactionType;
2827
use crate::*;

pallets/subtensor/src/tests/staking2.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use frame_support::{
44
weights::Weight,
55
};
66
use sp_core::U256;
7-
use substrate_fixed::types::{I96F32, U96F32};
87
use subtensor_swap_interface::SwapHandler;
98

109
use super::mock;

pallets/swap-interface/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ pub trait SwapHandler<AccountId> {
3030
coldkey_account_id: &AccountId,
3131
position_id: u128,
3232
) -> Result<UpdateLiquidityResult, DispatchError>;
33+
fn modify_position(
34+
netuid: u16,
35+
coldkey_account_id: &AccountId,
36+
hotkey_account_id: &AccountId,
37+
position_id: u128,
38+
liquidity_delta: i64,
39+
) -> Result<UpdateLiquidityResult, DispatchError>;
3340
fn approx_fee_amount(netuid: u16, amount: u64) -> u64;
3441
fn current_alpha_price(netuid: u16) -> U96F32;
3542
fn max_price() -> u64;

pallets/swap/src/pallet/impls.rs

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,6 @@ impl<T: Config> Pallet<T> {
412412
let mut refund: u64 = 0;
413413
let mut iteration_counter: u16 = 0;
414414
let mut in_acc: u64 = 0;
415-
let liquidity_before = CurrentLiquidity::<T>::get(netuid);
416415

417416
// Swap one tick at a time until we reach one of the stop conditions
418417
while amount_remaining > 0 {
@@ -431,6 +430,11 @@ impl<T: Config> Pallet<T> {
431430
amount_remaining = 0;
432431
}
433432

433+
// The swap step didn't exchange anything
434+
if swap_result.amount_to_take == 0 {
435+
amount_remaining = 0;
436+
}
437+
434438
iteration_counter = iteration_counter.saturating_add(1);
435439

436440
ensure!(
@@ -882,7 +886,7 @@ impl<T: Config> Pallet<T> {
882886
})
883887
}
884888

885-
fn modify_position(
889+
pub fn modify_position(
886890
netuid: NetUid,
887891
coldkey_account_id: &T::AccountId,
888892
hotkey_account_id: &T::AccountId,
@@ -1019,6 +1023,9 @@ impl<T: Config> Pallet<T> {
10191023
});
10201024
}
10211025
});
1026+
1027+
// Update active ticks
1028+
ActiveTickIndexManager::insert::<T>(netuid, tick_index);
10221029
}
10231030

10241031
/// Remove liquidity at tick index.
@@ -1043,6 +1050,9 @@ impl<T: Config> Pallet<T> {
10431050
// If no liquidity is left at the tick, remove it
10441051
if tick.liquidity_gross == 0 {
10451052
*maybe_tick = None;
1053+
1054+
// Update active ticks: Final liquidity is zero, remove this tick from active.
1055+
ActiveTickIndexManager::remove::<T>(netuid, tick_index);
10461056
}
10471057
}
10481058
});
@@ -1157,6 +1167,17 @@ impl<T: Config> SwapHandler<T::AccountId> for Pallet<T> {
11571167
.map_err(Into::into)
11581168
}
11591169

1170+
fn modify_position(
1171+
netuid: u16,
1172+
coldkey_account_id: &T::AccountId,
1173+
hotkey_account_id: &T::AccountId,
1174+
position_id: u128,
1175+
liquidity_delta: i64,
1176+
) -> Result<UpdateLiquidityResult, DispatchError> {
1177+
Self::modify_position(netuid.into(), coldkey_account_id, hotkey_account_id, position_id.into(), liquidity_delta)
1178+
.map_err(Into::into)
1179+
}
1180+
11601181
fn approx_fee_amount(netuid: u16, amount: u64) -> u64 {
11611182
Self::calculate_fee_amount(netuid.into(), amount)
11621183
}
@@ -1206,7 +1227,7 @@ pub enum SwapStepAction {
12061227
#[cfg(test)]
12071228
mod tests {
12081229
use approx::assert_abs_diff_eq;
1209-
use frame_support::{assert_err, assert_noop, assert_ok};
1230+
use frame_support::{assert_err, assert_ok};
12101231
use sp_arithmetic::helpers_128bit;
12111232

12121233
use super::*;
@@ -1647,7 +1668,6 @@ mod tests {
16471668
#[test]
16481669
fn test_modify_position_basic() {
16491670
new_test_ext().execute_with(|| {
1650-
let min_price = tick_to_price(TickIndex::MIN);
16511671
let max_price = tick_to_price(TickIndex::MAX);
16521672
let max_tick = price_to_tick(max_price);
16531673
let limit_price = 1000.0_f64;
@@ -1667,20 +1687,18 @@ mod tests {
16671687
// 4_000_000_000_u64,
16681688
// ),
16691689
// // Repeat the protocol liquidity at current to max range: Expect the same alpha
1670-
(0.25, max_price, 2_000_000_000_u64, 1_000_000_000, 4_000_000_000),
1671-
// Repeat the protocol liquidity at min to current range: Expect all the same tao
1672-
// (min_price, 0.24999, 2_000_000_000_u64, 1_000_000_000, 0),
1673-
// // Half to double price - just some sane wothdraw amounts
1674-
// (0.125, 0.5, 2_000_000_000_u64, 293_000_000, 1_171_000_000),
1690+
(0.25, max_price, 2_000_000_000_u64, 4_000_000_000),
1691+
// Half to double price - just some sane wothdraw amounts
1692+
// (0.125, 0.5, 2_000_000_000_u64, 1_171_000_000),
16751693
// // Both below price - tao is non-zero, alpha is zero
16761694
// (0.12, 0.13, 2_000_000_000_u64, 28_270_000, 0),
16771695
// // Both above price - tao is zero, alpha is non-zero
16781696
// (0.3, 0.4, 2_000_000_000_u64, 0, 489_200_000),
16791697
]
16801698
.into_iter()
16811699
.enumerate()
1682-
.map(|(n, v)| (NetUid::from(n as u16), v.0, v.1, v.2, v.3, v.4))
1683-
.for_each(|(netuid, price_low, price_high, liquidity, tao, alpha)| {
1700+
.map(|(n, v)| (NetUid::from(n as u16), v.0, v.1, v.2, v.3))
1701+
.for_each(|(netuid, price_low, price_high, liquidity, alpha)| {
16841702
// Calculate ticks (assuming tick math is tested separately)
16851703
let tick_low = price_to_tick(price_low);
16861704
let tick_high = price_to_tick(price_high);
@@ -1748,10 +1766,11 @@ mod tests {
17481766
&OK_COLDKEY_ACCOUNT_ID,
17491767
&OK_HOTKEY_ACCOUNT_ID,
17501768
position_id,
1751-
-1_i64 * ((liquidity / 10) as i64),
1769+
-1_i64 * ((liquidity / 100) as i64),
17521770
)
17531771
.unwrap();
1754-
assert_abs_diff_eq!(modify_result.alpha, alpha / 10, epsilon = alpha / 1000);
1772+
1773+
assert_abs_diff_eq!(modify_result.alpha, alpha / 100, epsilon = alpha / 1000);
17551774
assert_eq!(modify_result.fee_tao, 0);
17561775
assert_eq!(modify_result.fee_alpha, 0);
17571776
});
@@ -2015,10 +2034,6 @@ mod tests {
20152034
// Calculate the expected output amount for the cornercase of one step
20162035
let order_liquidity = order_liquidity_fraction * position_liquidity as f64;
20172036

2018-
let input_amount = match order_type {
2019-
OrderType::Buy => order_liquidity * sqrt_current_price.to_num::<f64>(),
2020-
OrderType::Sell => order_liquidity / sqrt_current_price.to_num::<f64>(),
2021-
};
20222037
let output_amount = match order_type {
20232038
OrderType::Buy => {
20242039
let denom = sqrt_current_price.to_num::<f64>()
@@ -2165,7 +2180,6 @@ mod tests {
21652180
let min_price = tick_to_price(TickIndex::MIN);
21662181
let max_price = tick_to_price(TickIndex::MAX);
21672182
let max_tick = price_to_tick(max_price);
2168-
let current_price = 0.25;
21692183
let netuid = NetUid(1);
21702184
assert_eq!(max_tick, TickIndex::MAX);
21712185

@@ -2350,22 +2364,12 @@ mod tests {
23502364
let order_type = OrderType::Sell;
23512365
let liquidity = 1_000_000_000_000_000_000;
23522366
let tick_low = TickIndex::MIN;
2353-
let tick_high = TickIndex::MAX;
23542367

23552368
let sqrt_limit_price: SqrtPrice = tick_low.try_to_sqrt_price().unwrap();
23562369

23572370
// Setup swap
23582371
assert_ok!(Pallet::<Test>::maybe_initialize_v3(netuid));
23592372

2360-
// Get tick infos before the swap
2361-
let tick_low_info_before = Ticks::<Test>::get(netuid, tick_low).unwrap_or_default();
2362-
let tick_high_info_before = Ticks::<Test>::get(netuid, tick_high).unwrap_or_default();
2363-
let liquidity_before = CurrentLiquidity::<Test>::get(netuid);
2364-
2365-
// Get current price
2366-
let sqrt_current_price = AlphaSqrtPrice::<Test>::get(netuid);
2367-
let current_price = (sqrt_current_price * sqrt_current_price).to_num::<f64>();
2368-
23692373
// Swap
23702374
let swap_result =
23712375
Pallet::<Test>::do_swap(netuid, order_type, liquidity, sqrt_limit_price, true)
@@ -2374,4 +2378,25 @@ mod tests {
23742378
assert!(swap_result.amount_paid_out > 0);
23752379
});
23762380
}
2381+
2382+
// cargo test --package pallet-subtensor-swap --lib -- pallet::impls::tests::test_price_tick_price_roundtrip --exact --show-output
2383+
#[test]
2384+
fn test_price_tick_price_roundtrip() {
2385+
new_test_ext().execute_with(|| {
2386+
let netuid = NetUid::from(1);
2387+
2388+
// Setup swap
2389+
assert_ok!(Pallet::<Test>::maybe_initialize_v3(netuid));
2390+
2391+
let current_price = SqrtPrice::from_num(0.50000051219212275465);
2392+
let tick = TickIndex::try_from_sqrt_price(current_price).unwrap();
2393+
let round_trip_price = TickIndex::try_to_sqrt_price(&tick).unwrap();
2394+
assert!(round_trip_price <= current_price);
2395+
2396+
let roundtrip_tick = TickIndex::try_from_sqrt_price(round_trip_price).unwrap();
2397+
assert!(tick == roundtrip_tick);
2398+
});
2399+
}
2400+
2401+
23772402
}

pallets/swap/src/pallet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use substrate_fixed::types::U64F64;
66
use subtensor_swap_interface::LiquidityDataProvider;
77

88
use crate::{
9-
NetUid, SqrtPrice,
9+
NetUid,
1010
position::{Position, PositionId},
1111
tick::{LayerLevel, Tick, TickIndex},
1212
weights::WeightInfo,

pallets/swap/src/position.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ impl Position {
9797
let fee_tao_agg = self.fees_in_range::<T>(true);
9898
let fee_alpha_agg = self.fees_in_range::<T>(false);
9999

100-
let mut fee_tao = fee_tao_agg.saturating_sub(U64F64::saturating_from_num(self.fees_tao));
101-
let mut fee_alpha = fee_alpha_agg.saturating_sub(U64F64::saturating_from_num(self.fees_alpha));
100+
let mut fee_tao = fee_tao_agg.saturating_sub(self.fees_tao);
101+
let mut fee_alpha = fee_alpha_agg.saturating_sub(self.fees_alpha);
102102

103103
self.fees_tao = fee_tao_agg;
104104
self.fees_alpha = fee_alpha_agg;

pallets/swap/src/tick.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use substrate_fixed::types::U64F64;
1515
use subtensor_macros::freeze_struct;
1616

1717
use crate::pallet::{
18-
AlphaSqrtPrice, Config, CurrentTick, FeeGlobalAlpha, FeeGlobalTao, TickIndexBitmapWords, Ticks,
18+
Config, CurrentTick, FeeGlobalAlpha, FeeGlobalTao, TickIndexBitmapWords, Ticks,
1919
};
2020
use crate::{NetUid, SqrtPrice};
2121

@@ -692,9 +692,9 @@ pub enum LayerLevel {
692692
Bottom = 2,
693693
}
694694

695-
#[freeze_struct("183175773f3f92e0")]
695+
#[freeze_struct("4015a04919eb5e2e")]
696696
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen)]
697-
struct BitmapLayer {
697+
pub(crate) struct BitmapLayer {
698698
word: u32,
699699
bit: u32,
700700
}

0 commit comments

Comments
 (0)