Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
OrderBook: exchange dynamic weight (#755)
Signed-off-by: Mikhail Tagirov <dev.mikhail.tagirov@outlook.com>
  • Loading branch information
wer1st authored Oct 3, 2023
commit 59d3b11413ccce050ef2ee6f83f59f3a76f9bd33
12 changes: 6 additions & 6 deletions pallets/order-book/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ benchmarks! {
// nothing changed
}

exchange {
exchange_single_order {
let caller = alice::<T>();

let order_book_id = OrderBookId::<AssetIdOf<T>, T::DEXId> {
Expand All @@ -677,7 +677,7 @@ benchmarks! {
&DEX.into(),
&VAL.into(),
&XOR.into(),
SwapAmount::with_desired_output(balance!(3500), balance!(360)),
SwapAmount::with_desired_output(balance!(1685), balance!(170)), // this amount executes only one limit order
)
.unwrap();
}
Expand All @@ -687,20 +687,20 @@ benchmarks! {
order_book_id,
owner_id: caller.clone(),
direction: PriceVariant::Sell,
amount: OrderAmount::Base(balance!(355.13473).into()),
average_price: balance!(9.855414408497867837).into(),
amount: OrderAmount::Base(balance!(168.5).into()),
average_price: balance!(10).into(),
to: None,
}
.into(),
);

assert_eq!(
<T as Config>::AssetInfoProvider::free_balance(&order_book_id.base, &caller).unwrap(),
caller_base_balance - balance!(355.13473)
caller_base_balance - balance!(168.5)
);
assert_eq!(
<T as Config>::AssetInfoProvider::free_balance(&order_book_id.quote, &caller).unwrap(),
caller_quote_balance + balance!(3499.999935)
caller_quote_balance + balance!(1685)
);
}

Expand Down
40 changes: 33 additions & 7 deletions pallets/order-book/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ pub mod pallet {
const MIN_ORDER_LIFESPAN: MomentOf<Self>;
const MILLISECS_PER_BLOCK: MomentOf<Self>;
const MAX_PRICE_SHIFT: Perbill;
/// The soft ratio between min & max order amounts.
/// In particular, it defines the optimal number of limit orders that could be executed by one big market order in one block.
const SOFT_MIN_MAX_RATIO: usize;
/// The soft ratio between min & max order amounts.
/// In particular, it defines the max number of limit orders that could be executed by one big market order in one block.
const HARD_MIN_MAX_RATIO: usize;

/// Because this pallet emits events, it depends on the runtime's definition of an event.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
Expand Down Expand Up @@ -644,15 +650,15 @@ pub mod pallet {
}

#[pallet::call_index(4)]
#[pallet::weight(<T as Config>::WeightInfo::place_limit_order())]
#[pallet::weight(Pallet::<T>::exchange_weight())] // in the worst case the limit order is converted into market order and the exchange occurs
pub fn place_limit_order(
origin: OriginFor<T>,
order_book_id: OrderBookId<AssetIdOf<T>, T::DEXId>,
price: Balance,
amount: Balance,
side: PriceVariant,
lifespan: Option<MomentOf<T>>,
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let mut order_book =
<OrderBooks<T>>::get(order_book_id).ok_or(Error::<T>::UnknownOrderBook)?;
Expand All @@ -677,12 +683,27 @@ pub mod pallet {
);

let mut data = CacheDataLayer::<T>::new();
order_book.place_limit_order(order, &mut data)?;

let executed_orders_count = order_book.place_limit_order(order, &mut data)?;

data.commit();
<OrderBooks<T>>::insert(order_book_id, order_book);

Ok(().into())
// Note: be careful with changing the weight. The fee depends on it,
// the market-maker fee is charged for some weight, and the regular fee for none weight
let actual_weight = if executed_orders_count == 0 {
// if the extrinsic just places the limit order, the weight of the placing is returned
Some(<T as Config>::WeightInfo::place_limit_order())
} else {
// if the limit order was converted into market order, then None weight is returned
// this weight will be replaced with worst case weight - exchange_weight()
None
};

Ok(PostDispatchInfo {
actual_weight,
pays_fee: Pays::Yes,
})
}

#[pallet::call_index(5)]
Expand Down Expand Up @@ -1137,7 +1158,7 @@ impl<T: Config> LiquiditySource<T::DEXId, T::AccountId, T::AssetId, Balance, Dis
let market_order =
MarketOrder::<T>::new(sender.clone(), direction, order_book_id, amount, to.clone());

let (input_amount, output_amount) =
let (input_amount, output_amount, executed_orders_count) =
order_book.execute_market_order(market_order, &mut data)?;

let fee = 0; // todo (m.tagirov)
Expand All @@ -1161,7 +1182,10 @@ impl<T: Config> LiquiditySource<T::DEXId, T::AccountId, T::AssetId, Balance, Dis

data.commit();

Ok((result, Self::exchange_weight()))
let weight = <T as Config>::WeightInfo::exchange_single_order()
.saturating_mul(executed_orders_count as u64);

Ok((result, weight))
}

fn check_rewards(
Expand Down Expand Up @@ -1263,7 +1287,9 @@ impl<T: Config> LiquiditySource<T::DEXId, T::AccountId, T::AssetId, Balance, Dis
}

fn exchange_weight() -> Weight {
<T as Config>::WeightInfo::exchange()
// SOFT_MIN_MAX_RATIO is approximately the max number of limit orders could be executed by one market order
<T as Config>::WeightInfo::exchange_single_order()
.saturating_mul(<T as Config>::SOFT_MIN_MAX_RATIO as u64)
}

fn check_rewards_weight() -> Weight {
Expand Down
17 changes: 10 additions & 7 deletions pallets/order-book/src/order_book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,12 @@ impl<T: crate::Config + Sized> OrderBook<T> {
self.last_order_id
}

/// Tries to place the limit order and returns market input & deal input amounts.
/// In some cases if the limit order crosses the spread, part or all of the amount could be converted into a market order and as a result, the deal input is not empty.
/// Tries to place the limit order and returns the count of executed limit orders (if the limit order crosses the spread and was converted into market order).
pub fn place_limit_order(
&self,
limit_order: LimitOrder<T>,
data: &mut impl DataLayer<T>,
) -> Result<(), DispatchError> {
) -> Result<usize, DispatchError> {
ensure!(
self.status == OrderBookStatus::Trade || self.status == OrderBookStatus::PlaceAndCancel,
Error::<T>::PlacementOfLimitOrdersIsForbidden
Expand Down Expand Up @@ -155,6 +154,8 @@ impl<T: crate::Config + Sized> OrderBook<T> {
let maybe_deal_amount = market_change.deal_base_amount();
let (market_input, deal_input) = (market_change.market_input, market_change.deal_input);

let executed_orders_count = market_change.count_of_executed_orders();

self.apply_market_change(market_change, data)?;

match (market_input, deal_input) {
Expand Down Expand Up @@ -201,7 +202,7 @@ impl<T: crate::Config + Sized> OrderBook<T> {
}
}

Ok(())
Ok(executed_orders_count)
}

pub fn cancel_limit_order(
Expand Down Expand Up @@ -232,12 +233,12 @@ impl<T: crate::Config + Sized> OrderBook<T> {
Ok(count)
}

/// Executes market order and returns input & output amounts
/// Executes market order and returns input & output amounts & count of executed limit orders
pub fn execute_market_order(
&self,
market_order: MarketOrder<T>,
data: &mut impl DataLayer<T>,
) -> Result<(OrderAmount, OrderAmount), DispatchError> {
) -> Result<(OrderAmount, OrderAmount, usize), DispatchError> {
ensure!(
self.status == OrderBookStatus::Trade,
Error::<T>::TradingIsForbidden
Expand All @@ -258,6 +259,8 @@ impl<T: crate::Config + Sized> OrderBook<T> {
return Err(Error::<T>::PriceCalculationFailed.into());
};

let executed_orders_count = market_change.count_of_executed_orders();

self.apply_market_change(market_change, data)?;

T::Delegate::emit_event(
Expand All @@ -271,7 +274,7 @@ impl<T: crate::Config + Sized> OrderBook<T> {
},
);

Ok((input, output))
Ok((input, output, executed_orders_count))
}

pub fn align_limit_orders(&self, data: &mut impl DataLayer<T>) -> Result<(), DispatchError> {
Expand Down
Loading