Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Orderbook]: Implement Fill Limit Order #47

Merged
merged 4 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
64 changes: 49 additions & 15 deletions contracts/orderbook/src/order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn place_limit(
let order_id = new_order_id(deps.storage)?;

// Build limit order
let limit_order = LimitOrder::new(
let mut limit_order = LimitOrder::new(
book_id,
tick_id,
order_id,
Expand All @@ -64,22 +64,57 @@ pub fn place_limit(
quantity,
);

// Save the order to the orderbook
orders().save(deps.storage, &(book_id, tick_id, order_id), &limit_order)?;
// Determine if the order needs to be filled
let should_fill = match order_direction {
OrderDirection::Ask => tick_id <= orderbook.next_bid_tick,
OrderDirection::Bid => tick_id >= orderbook.next_ask_tick,
};

// Update tick liquidity
TICK_LIQUIDITY.update(deps.storage, &(book_id, tick_id), |liquidity| {
Ok::<Uint128, ContractError>(liquidity.unwrap_or_default().checked_add(quantity)?)
})?;
let mut response = Response::default();
// Run order fill if criteria met
if should_fill {
// Convert order to market order for filling
let mut market_order: MarketOrder = limit_order.clone().into();
// Generate vector of fulfillments for current orders and a payment for the placed order
let (fulfillments, placed_order_payment) =
run_market_order(deps.storage, &mut market_order, Some(tick_id))?;
// Resolve given fulfillments and current placed order
let fulfillment_msgs = resolve_fulfillments(deps.storage, fulfillments)?;
// Update limit order quantity to reflect fulfilled amount
crnbarr93 marked this conversation as resolved.
Show resolved Hide resolved
limit_order.quantity = market_order.quantity;

Ok(Response::new()
response = response
.add_messages(fulfillment_msgs)
.add_message(placed_order_payment)
}

// Only save the order if not fully filled
if limit_order.quantity > Uint128::zero() {
// Save the order to the orderbook
orders().save(deps.storage, &(book_id, tick_id, order_id), &limit_order)?;

// Update tick liquidity
TICK_LIQUIDITY.update(deps.storage, &(book_id, tick_id), |liquidity| {
Ok::<Uint128, ContractError>(
liquidity
.unwrap_or_default()
.checked_add(limit_order.quantity)?,
)
})?;
}

Ok(response
.add_attribute("method", "placeLimit")
.add_attribute("owner", info.sender.to_string())
.add_attribute("book_id", book_id.to_string())
.add_attribute("tick_id", tick_id.to_string())
.add_attribute("order_id", order_id.to_string())
.add_attribute("order_direction", format!("{order_direction:?}"))
.add_attribute("quantity", quantity.to_string()))
.add_attribute("quantity", quantity.to_string())
.add_attribute(
"quantity_fulfilled",
quantity.checked_sub(limit_order.quantity)?,
))
}

pub fn cancel_limit(
Expand Down Expand Up @@ -158,7 +193,7 @@ pub fn run_market_order(
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 placed_order_fulfilled_denom = orderbook.get_opposite_denom(&order.order_direction);

let (min_tick, max_tick, ordering) = match order.order_direction {
OrderDirection::Ask => {
Expand Down Expand Up @@ -230,7 +265,7 @@ pub fn run_market_order(
fulfillments,
BankMsg::Send {
to_address: order.owner.to_string(),
amount: vec![coin(amount_fulfilled.u128(), placed_order_denom)],
amount: vec![coin(amount_fulfilled.u128(), placed_order_fulfilled_denom)],
},
));
}
Expand All @@ -242,7 +277,7 @@ pub fn run_market_order(
fulfillments,
BankMsg::Send {
to_address: order.owner.to_string(),
amount: vec![coin(amount_fulfilled.u128(), placed_order_denom)],
amount: vec![coin(amount_fulfilled.u128(), placed_order_fulfilled_denom)],
},
));
}
Expand All @@ -253,7 +288,7 @@ pub fn run_market_order(
fulfillments,
BankMsg::Send {
to_address: order.owner.to_string(),
amount: vec![coin(amount_fulfilled.u128(), placed_order_denom)],
amount: vec![coin(amount_fulfilled.u128(), placed_order_fulfilled_denom)],
},
))
}
Expand All @@ -268,7 +303,6 @@ pub fn resolve_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,
Expand All @@ -277,7 +311,7 @@ pub fn resolve_fulfillments(
reason: Some("Fulfillment is part of another order book".to_string()),
}
);
let denom = orderbook.get_expected_denom(&fulfillment.order.order_direction);
let denom = orderbook.get_opposite_denom(&fulfillment.order.order_direction);
// TODO: Add price detection for tick
let msg = fulfillment
.order
Expand Down
3 changes: 1 addition & 2 deletions contracts/orderbook/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub const ORDERBOOKS: Map<&u64, Orderbook> = Map::new("orderbooks");
/// Key: (orderbook_id, tick)
pub const TICK_LIQUIDITY: Map<&(u64, i64), Uint128> = Map::new("tick_liquidity");

// TODO: Check additional gas fee for adding more indexes
pub struct OrderIndexes {
// Index by owner; Generic types: MultiIndex<Index Key: owner, Input Data: LimitOrder, Map Key: (orderbook_id, tick, order_id)>
pub owner: MultiIndex<'static, Addr, LimitOrder, (u64, i64, u64)>,
Expand Down Expand Up @@ -87,7 +86,7 @@ pub fn reduce_tick_liquidity(
TICK_LIQUIDITY.remove(storage, &(book_id, tick_id));
} else {
TICK_LIQUIDITY.save(storage, &(book_id, tick_id), &new_liquidity)?;
}
};
Ok(())
}

Expand Down
Loading
Loading