Skip to content

Commit

Permalink
Merge branch 'main' into alpo/tick-to-price-clean
Browse files Browse the repository at this point in the history
  • Loading branch information
AlpinYukseloglu committed Feb 24, 2024
2 parents eafbe80 + c6b008e commit 94a43bd
Show file tree
Hide file tree
Showing 6 changed files with 1,967 additions and 12 deletions.
11 changes: 11 additions & 0 deletions contracts/orderbook/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ pub enum ContractError {
// Tick out of bounds error
#[error("Tick out of bounds: {tick_id:?}")]
TickOutOfBounds { tick_id: i64 },
#[error("Cannot fulfill order. Order ID: {order_id:?}, Book ID: {book_id:?}, Amount Required: {amount_required:?}, Amount Remaining: {amount_remaining:?} {reason:?}")]
InvalidFulfillment {
order_id: u64,
book_id: u64,
amount_required: Uint128,
amount_remaining: Uint128,
reason: Option<String>,
},

#[error("Mismatched order direction")]
MismatchedOrderDirection {},
}

pub type ContractResult<T> = Result<T, ContractError>;
173 changes: 170 additions & 3 deletions contracts/orderbook/src/order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ use crate::constants::{MAX_TICK, MIN_TICK};
use crate::error::ContractError;
use crate::state::ORDERBOOKS;
use crate::state::*;
use crate::types::{LimitOrder, OrderDirection, REPLY_ID_REFUND};
use crate::types::{Fulfillment, LimitOrder, MarketOrder, OrderDirection, REPLY_ID_REFUND};
use cosmwasm_std::{
coin, ensure, ensure_eq, BankMsg, DepsMut, Env, MessageInfo, Response, SubMsg, Uint128,
coin, ensure, ensure_eq, ensure_ne, BankMsg, Decimal, DepsMut, Env, MessageInfo, Order,
Response, Storage, SubMsg, Uint128,
};
use cw_storage_plus::Bound;
use cw_utils::{must_pay, nonpayable};

#[allow(clippy::manual_range_contains)]
pub fn place_limit(
deps: DepsMut,
env: Env,
_env: Env,
info: MessageInfo,
book_id: u64,
tick_id: i64,
Expand Down Expand Up @@ -147,3 +149,168 @@ pub fn place_market(
.add_attribute("method", "placeMarket")
.add_attribute("owner", info.sender))
}

#[allow(clippy::manual_range_contains)]
pub fn run_market_order(
storage: &mut dyn Storage,
order: &mut MarketOrder,
tick_bound: Option<i64>,
) -> Result<(Vec<Fulfillment>, BankMsg), ContractError> {
let mut fulfillments: Vec<Fulfillment> = vec![];
let mut amount_fulfilled: Uint128 = Uint128::zero();
let orderbook = ORDERBOOKS.load(storage, &order.book_id)?;
let placed_order_denom = orderbook.get_expected_denom(&order.order_direction);

let (min_tick, max_tick, ordering) = match order.order_direction {
OrderDirection::Ask => {
if let Some(tick_bound) = tick_bound {
ensure!(
tick_bound <= orderbook.next_bid_tick
&& tick_bound <= MAX_TICK
&& tick_bound >= MIN_TICK,
ContractError::InvalidTickId {
tick_id: tick_bound
}
);
}
(tick_bound, Some(orderbook.next_bid_tick), Order::Descending)
}
OrderDirection::Bid => {
if let Some(tick_bound) = tick_bound {
ensure!(
tick_bound >= orderbook.next_ask_tick
&& tick_bound <= MAX_TICK
&& tick_bound >= MIN_TICK,
ContractError::InvalidTickId {
tick_id: tick_bound
}
);
}
(Some(orderbook.next_ask_tick), tick_bound, Order::Ascending)
}
};

// Create ticks iterator between first tick and requested tick
let ticks = TICK_LIQUIDITY.prefix(order.book_id).range(
storage,
min_tick.map(Bound::inclusive),
max_tick.map(Bound::inclusive),
ordering,
);

for maybe_current_tick in ticks {
let current_tick = maybe_current_tick?.0;

// Create orders iterator for all orders on current tick
let tick_orders = orders().prefix((order.book_id, current_tick)).range(
storage,
None,
None,
Order::Ascending,
);

for maybe_current_order in tick_orders {
let current_order = maybe_current_order?.1;
ensure_ne!(
current_order.order_direction,
order.order_direction,
ContractError::MismatchedOrderDirection {}
);
let fill_quantity = order.quantity.min(current_order.quantity);
// Add to total amount fulfilled from placed order
amount_fulfilled = amount_fulfilled.checked_add(fill_quantity)?;
// Generate fulfillment for current order
let fulfillment = Fulfillment::new(current_order, fill_quantity);
fulfillments.push(fulfillment);

// Update remaining order quantity
order.quantity = order.quantity.checked_sub(fill_quantity)?;
// TODO: Price detection
if order.quantity.is_zero() {
return Ok((
fulfillments,
BankMsg::Send {
to_address: order.owner.to_string(),
amount: vec![coin(amount_fulfilled.u128(), placed_order_denom)],
},
));
}
}

// TODO: Price detection
if order.quantity.is_zero() {
return Ok((
fulfillments,
BankMsg::Send {
to_address: order.owner.to_string(),
amount: vec![coin(amount_fulfilled.u128(), placed_order_denom)],
},
));
}
}

// TODO: Price detection
Ok((
fulfillments,
BankMsg::Send {
to_address: order.owner.to_string(),
amount: vec![coin(amount_fulfilled.u128(), placed_order_denom)],
},
))
}

pub fn resolve_fulfillments(
storage: &mut dyn Storage,
fulfillments: Vec<Fulfillment>,
) -> Result<Vec<BankMsg>, ContractError> {
let mut msgs: Vec<BankMsg> = vec![];
let orderbook = ORDERBOOKS.load(storage, &fulfillments[0].order.book_id)?;
for mut fulfillment in fulfillments {
ensure_eq!(
fulfillment.order.book_id,
orderbook.book_id,
// TODO: Error not expressive
ContractError::InvalidFulfillment {
order_id: fulfillment.order.order_id,
book_id: fulfillment.order.book_id,
amount_required: fulfillment.amount,
amount_remaining: fulfillment.order.quantity,
reason: Some("Fulfillment is part of another order book".to_string()),
}
);
let denom = orderbook.get_expected_denom(&fulfillment.order.order_direction);
// TODO: Add price detection for tick
let msg = fulfillment
.order
.fill(&denom, fulfillment.amount, Decimal::one())?;
msgs.push(msg);
if fulfillment.order.quantity.is_zero() {
orders().remove(
storage,
&(
fulfillment.order.book_id,
fulfillment.order.tick_id,
fulfillment.order.order_id,
),
)?;
} else {
orders().save(
storage,
&(
fulfillment.order.book_id,
fulfillment.order.tick_id,
fulfillment.order.order_id,
),
&fulfillment.order,
)?;
}
// TODO: possible optimization by grouping tick/liquidity and calling this once per tick?
reduce_tick_liquidity(
storage,
fulfillment.order.book_id,
fulfillment.order.tick_id,
fulfillment.amount,
)?;
}
Ok(msgs)
}
Loading

0 comments on commit 94a43bd

Please sign in to comment.