From 80f66c344d4fb4f08d2ba01743b40cfeb20af872 Mon Sep 17 00:00:00 2001 From: Connor Barr Date: Mon, 1 Apr 2024 14:24:08 +0100 Subject: [PATCH] Duplicated tick state for order direction --- contracts/sumtree-orderbook/src/order.rs | 28 +++++++------ .../sumtree-orderbook/src/tests/test_order.rs | 15 ++++--- .../sumtree-orderbook/src/types/order.rs | 4 +- contracts/sumtree-orderbook/src/types/tick.rs | 39 +++++++++++++++++-- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/contracts/sumtree-orderbook/src/order.rs b/contracts/sumtree-orderbook/src/order.rs index 23f346f..03ff2bd 100644 --- a/contracts/sumtree-orderbook/src/order.rs +++ b/contracts/sumtree-orderbook/src/order.rs @@ -77,6 +77,7 @@ pub fn place_limit( let mut tick_state = TICK_STATE .load(deps.storage, &(book_id, tick_id)) .unwrap_or_default(); + let mut tick_values = tick_state.get_values(order_direction); // Build limit order let limit_order = LimitOrder::new( @@ -86,7 +87,7 @@ pub fn place_limit( order_direction, info.sender.clone(), quantity, - tick_state.cumulative_total_value, + tick_values.cumulative_total_value, ); // Determine if the order needs to be filled @@ -108,7 +109,7 @@ pub fn place_limit( // Save the order to the orderbook orders().save(deps.storage, &(book_id, tick_id, order_id), &limit_order)?; - tick_state.total_amount_of_liquidity = tick_state + tick_values.total_amount_of_liquidity = tick_values .total_amount_of_liquidity .checked_add(Decimal256::from_ratio( limit_order.quantity.u128(), @@ -117,10 +118,11 @@ pub fn place_limit( .unwrap(); } - tick_state.cumulative_total_value = tick_state + tick_values.cumulative_total_value = tick_values .cumulative_total_value .checked_add(Decimal256::from_ratio(quantity, Uint256::one()))?; + tick_state.set_values(order_direction, tick_values); TICK_STATE.save(deps.storage, &(book_id, tick_id), &tick_state)?; Ok(response @@ -162,8 +164,9 @@ pub fn cancel_limit( let tick_state = TICK_STATE .load(deps.storage, &(book_id, tick_id)) .unwrap_or_default(); + let tick_values = tick_state.get_values(order.order_direction); ensure!( - tick_state.effective_total_amount_swapped <= order.etas, + tick_values.effective_total_amount_swapped <= order.etas, ContractError::CancelFilledOrder ); @@ -189,6 +192,7 @@ pub fn cancel_limit( .ok_or(ContractError::InvalidTickId { tick_id: order.tick_id, })?; + let mut curr_tick_values = curr_tick_state.get_values(order.order_direction); let mut new_node = TreeNode::new( order.book_id, @@ -226,10 +230,10 @@ pub fn cancel_limit( &(order.book_id, order.tick_id, order.order_id), )?; - curr_tick_state.total_amount_of_liquidity = curr_tick_state + curr_tick_values.total_amount_of_liquidity = curr_tick_values .total_amount_of_liquidity .checked_sub(Decimal256::from_ratio(order.quantity, Uint256::one()))?; - + curr_tick_state.set_values(order.order_direction, curr_tick_values); TICK_STATE.save( deps.storage, &(order.book_id, order.tick_id), @@ -337,8 +341,8 @@ pub fn run_market_order( let mut tick_updates: Vec<(i64, TickState)> = Vec::new(); for maybe_current_tick in ticks { let (current_tick_id, mut current_tick) = maybe_current_tick?; + let mut current_tick_values = current_tick.get_values(order.order_direction.opposite()); let tick_price = tick_to_price(current_tick_id)?; - let output_quantity = amount_to_value(order.order_direction, order.quantity, tick_price)?; let output_quantity_dec = @@ -346,18 +350,19 @@ pub fn run_market_order( // If order quantity is less than the current tick's liquidity, fill the whole order. // Otherwise, fill the whole tick. - let fill_amount_dec = if output_quantity_dec < current_tick.total_amount_of_liquidity { + let fill_amount_dec = if output_quantity_dec < current_tick_values.total_amount_of_liquidity + { output_quantity_dec } else { - current_tick.total_amount_of_liquidity + current_tick_values.total_amount_of_liquidity }; // Update tick and order state to process the fill - current_tick.total_amount_of_liquidity = current_tick + current_tick_values.total_amount_of_liquidity = current_tick_values .total_amount_of_liquidity .checked_sub(fill_amount_dec)?; - current_tick.effective_total_amount_swapped = current_tick + current_tick_values.effective_total_amount_swapped = current_tick_values .effective_total_amount_swapped .checked_add(fill_amount_dec)?; @@ -371,6 +376,7 @@ pub fn run_market_order( amount_to_value(order.order_direction.opposite(), fill_amount, tick_price)?; order.quantity = order.quantity.checked_sub(input_filled)?; + current_tick.set_values(order.order_direction.opposite(), current_tick_values); // Add the updated tick state to the vector tick_updates.push((current_tick_id, current_tick)); diff --git a/contracts/sumtree-orderbook/src/tests/test_order.rs b/contracts/sumtree-orderbook/src/tests/test_order.rs index 62dff3a..10cbadd 100644 --- a/contracts/sumtree-orderbook/src/tests/test_order.rs +++ b/contracts/sumtree-orderbook/src/tests/test_order.rs @@ -205,8 +205,9 @@ fn test_place_limit() { let state = TICK_STATE .load(&deps.storage, &(test.book_id, test.tick_id)) .unwrap_or_default(); + let values = state.get_values(test.order_direction); assert!( - state.total_amount_of_liquidity.is_zero(), + values.total_amount_of_liquidity.is_zero(), "{}", format_test_name(test.name) ); @@ -305,7 +306,8 @@ fn test_place_limit() { // Validate liquidity updated as intended let state = TICK_STATE .load(&deps.storage, &(test.book_id, test.tick_id)) - .unwrap(); + .unwrap() + .get_values(test.order_direction); assert_eq!( state.total_amount_of_liquidity, Decimal256::from_ratio(test.quantity, Uint256::one()), @@ -476,7 +478,8 @@ fn test_cancel_limit() { // Verify Liqudity was updated as intended let state = TICK_STATE .load(deps.as_ref().storage, &(test.book_id, test.tick_id)) - .unwrap_or_default(); + .unwrap_or_default() + .get_values(test.order_direction); if test.place_order { assert_eq!( state.total_amount_of_liquidity, @@ -567,7 +570,8 @@ fn test_cancel_limit() { // Validate liquidity updated as intended let state = TICK_STATE .load(deps.as_ref().storage, &(test.book_id, test.tick_id)) - .unwrap_or_default(); + .unwrap_or_default() + .get_values(test.order_direction); assert!( state.total_amount_of_liquidity.is_zero(), @@ -1041,7 +1045,8 @@ fn test_run_market_order() { for (tick_id, expected_etas) in test.expected_tick_etas { let tick_state = TICK_STATE .load(&deps.storage, &(valid_book_id, tick_id)) - .unwrap(); + .unwrap() + .get_values(test.placed_order.order_direction.opposite()); assert_eq!( expected_etas, tick_state.effective_total_amount_swapped, diff --git a/contracts/sumtree-orderbook/src/types/order.rs b/contracts/sumtree-orderbook/src/types/order.rs index 67138a6..4447e48 100644 --- a/contracts/sumtree-orderbook/src/types/order.rs +++ b/contracts/sumtree-orderbook/src/types/order.rs @@ -4,8 +4,8 @@ use cosmwasm_std::{Addr, Decimal256, Uint128}; #[cw_serde] #[derive(Copy)] pub enum OrderDirection { - Bid, - Ask, + Bid = 0, + Ask = 1, } impl OrderDirection { diff --git a/contracts/sumtree-orderbook/src/types/tick.rs b/contracts/sumtree-orderbook/src/types/tick.rs index 56b9bc5..80852d7 100644 --- a/contracts/sumtree-orderbook/src/types/tick.rs +++ b/contracts/sumtree-orderbook/src/types/tick.rs @@ -1,9 +1,10 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Decimal256; -/// Represents the state of a specific price tick in a liquidity pool. +use super::OrderDirection; + #[cw_serde] -pub struct TickState { +pub struct TickValues { /// Total Amount of Liquidity at tick (TAL) /// - Every limit order placement increments this value. /// - Every swap at this tick decrements this value. @@ -21,12 +22,42 @@ pub struct TickState { pub effective_total_amount_swapped: Decimal256, } -impl Default for TickState { +impl Default for TickValues { fn default() -> Self { - TickState { + TickValues { total_amount_of_liquidity: Decimal256::zero(), cumulative_total_value: Decimal256::zero(), effective_total_amount_swapped: Decimal256::zero(), } } } + +/// Represents the state of a specific price tick in a liquidity pool. +/// +/// The state is split into two parts for the ask and bid directions. +#[cw_serde] +#[derive(Default)] +pub struct TickState { + /// Values for the ask direction of the tick + pub ask_values: TickValues, + /// Values for the bid direction of the tick + pub bid_values: TickValues, +} + +impl TickState { + pub fn get_values(&self, direction: OrderDirection) -> TickValues { + if direction == OrderDirection::Ask { + self.ask_values.clone() + } else { + self.bid_values.clone() + } + } + + pub fn set_values(&mut self, direction: OrderDirection, values: TickValues) { + if direction == OrderDirection::Ask { + self.ask_values = values; + } else { + self.bid_values = values; + } + } +}