0x0::order_info
Order module defines the order struct and its methods. All order matching happens in this module.
OrderInfo
OrderFilled
OrderPlaced
OrderExpired
pool_id
order_id
balance_manager_id
client_order_id
trader
order_type
self_matching_option
price
is_bid
original_quantity
order_deep_price
expire_timestamp
executed_quantity
cumulative_quote_quantity
fills
fee_is_deep
paid_fees
maker_fees
epoch
status
fill_limit_reached
order_inserted
new
market_order
set_order_id
set_paid_fees
add_fill
fills_ref
calculate_partial_fill_balances
to_order
validate_inputs
assert_execution
remaining_quantity
can_match
match_maker
emit_orders_filled
emit_order_placed
emit_order_info
set_fill_limit_reached
set_order_inserted
order_filled_from_fill
order_expired_from_fill
emit_order_canceled_maker_from_fill
use 0x0::balances;
use 0x0::constants;
use 0x0::deep_price;
use 0x0::fill;
use 0x0::math;
use 0x0::order;
use 0x2::event;
use 0x2::object;
OrderInfo
OrderInfo struct represents all order information. This objects gets created at the beginning of the order lifecycle and gets updated until it is completed or placed in the book. It is returned at the end of the order lifecycle.
struct OrderInfo has copy, drop, store
pool_id: object::ID
order_id: u128
balance_manager_id: object::ID
client_order_id: u64
trader: address
order_type: u8
self_matching_option: u8
price: u64
is_bid: bool
original_quantity: u64
order_deep_price: deep_price::OrderDeepPrice
expire_timestamp: u64
executed_quantity: u64
cumulative_quote_quantity: u64
fills: vector<fill::Fill>
fee_is_deep: bool
paid_fees: u64
maker_fees: u64
epoch: u64
status: u8
market_order: bool
fill_limit_reached: bool
order_inserted: bool
timestamp: u64
OrderFilled
Emitted when a maker order is filled.
struct OrderFilled has copy, drop, store
pool_id: object::ID
maker_order_id: u128
taker_order_id: u128
maker_client_order_id: u64
taker_client_order_id: u64
price: u64
taker_is_bid: bool
taker_fee: u64
taker_fee_is_deep: bool
maker_fee: u64
maker_fee_is_deep: bool
base_quantity: u64
quote_quantity: u64
maker_balance_manager_id: object::ID
taker_balance_manager_id: object::ID
timestamp: u64
OrderPlaced
Emitted when a maker order is injected into the order book.
struct OrderPlaced has copy, drop, store
balance_manager_id: object::ID
pool_id: object::ID
order_id: u128
client_order_id: u64
trader: address
price: u64
is_bid: bool
placed_quantity: u64
expire_timestamp: u64
timestamp: u64
OrderExpired
Emitted when a maker order is expired.
struct OrderExpired has copy, drop, store
balance_manager_id: object::ID
pool_id: object::ID
order_id: u128
client_order_id: u64
trader: address
price: u64
is_bid: bool
original_quantity: u64
base_asset_quantity_canceled: u64
timestamp: u64
const EFOKOrderCannotBeFullyFilled: u64 = 6;
const EInvalidExpireTimestamp: u64 = 3;
const EInvalidOrderType: u64 = 4;
const EMarketOrderCannotBePostOnly: u64 = 7;
const EOrderBelowMinimumSize: u64 = 1;
const EOrderInvalidLotSize: u64 = 2;
const EOrderInvalidPrice: u64 = 0;
const EPOSTOrderCrossesOrderbook: u64 = 5;
const ESelfMatchingCancelTaker: u64 = 8;
pool_id
public fun pool_id(self: &order_info::OrderInfo): object::ID
order_id
public fun order_id(self: &order_info::OrderInfo): u128
balance_manager_id
public fun balance_manager_id(self: &order_info::OrderInfo): object::ID
public fun balance_manager_id(self: &OrderInfo): ID {
self.balance_manager_id
}
client_order_id
public fun client_order_id(self: &order_info::OrderInfo): u64
public fun client_order_id(self: &OrderInfo): u64 {
self.client_order_id
}
trader
public fun trader(self: &order_info::OrderInfo): address
order_type
public fun order_type(self: &order_info::OrderInfo): u8
public fun order_type(self: &OrderInfo): u8 {
self.order_type
}
self_matching_option
public fun self_matching_option(self: &order_info::OrderInfo): u8
public fun self_matching_option(self: &OrderInfo): u8 {
self.self_matching_option
}
price
public fun price(self: &order_info::OrderInfo): u64
is_bid
public fun is_bid(self: &order_info::OrderInfo): bool
original_quantity
public fun original_quantity(self: &order_info::OrderInfo): u64
public fun original_quantity(self: &OrderInfo): u64 {
self.original_quantity
}
order_deep_price
public fun order_deep_price(self: &order_info::OrderInfo): deep_price::OrderDeepPrice
public fun order_deep_price(self: &OrderInfo): OrderDeepPrice {
self.order_deep_price
}
expire_timestamp
public fun expire_timestamp(self: &order_info::OrderInfo): u64
public fun expire_timestamp(self: &OrderInfo): u64 {
self.expire_timestamp
}
executed_quantity
public fun executed_quantity(self: &order_info::OrderInfo): u64
public fun executed_quantity(self: &OrderInfo): u64 {
self.executed_quantity
}
cumulative_quote_quantity
public fun cumulative_quote_quantity(self: &order_info::OrderInfo): u64
public fun cumulative_quote_quantity(self: &OrderInfo): u64 {
self.cumulative_quote_quantity
}
fills
public fun fills(self: &order_info::OrderInfo): vector<fill::Fill>
fee_is_deep
public fun fee_is_deep(self: &order_info::OrderInfo): bool
public fun fee_is_deep(self: &OrderInfo): bool {
self.fee_is_deep
}
paid_fees
public fun paid_fees(self: &order_info::OrderInfo): u64
maker_fees
public fun maker_fees(self: &order_info::OrderInfo): u64
public fun maker_fees(self: &OrderInfo): u64 {
self.maker_fees
}
epoch
public fun epoch(self: &order_info::OrderInfo): u64
status
public fun status(self: &order_info::OrderInfo): u8
fill_limit_reached
public fun fill_limit_reached(self: &order_info::OrderInfo): bool
public fun fill_limit_reached(self: &OrderInfo): bool {
self.fill_limit_reached
}
order_inserted
public fun order_inserted(self: &order_info::OrderInfo): bool
public fun order_inserted(self: &OrderInfo): bool {
self.order_inserted
}
new
public(friend) fun new(pool_id: object::ID, balance_manager_id: object::ID, client_order_id: u64, trader: address, order_type: u8, self_matching_option: u8, price: u64, quantity: u64, is_bid: bool, fee_is_deep: bool, epoch: u64, expire_timestamp: u64, order_deep_price: deep_price::OrderDeepPrice, market_order: bool, timestamp: u64): order_info::OrderInfo
public(package) fun new(
pool_id: ID,
balance_manager_id: ID,
client_order_id: u64,
trader: address,
order_type: u8,
self_matching_option: u8,
price: u64,
quantity: u64,
is_bid: bool,
fee_is_deep: bool,
epoch: u64,
expire_timestamp: u64,
order_deep_price: OrderDeepPrice,
market_order: bool,
timestamp: u64,
): OrderInfo {
OrderInfo {
pool_id,
order_id: 0,
balance_manager_id,
client_order_id,
trader,
order_type,
self_matching_option,
price,
is_bid,
original_quantity: quantity,
order_deep_price,
expire_timestamp,
executed_quantity: 0,
cumulative_quote_quantity: 0,
fills: vector[],
fee_is_deep,
epoch,
paid_fees: 0,
maker_fees: 0,
status: constants::live(),
market_order,
fill_limit_reached: false,
order_inserted: false,
timestamp,
}
}
market_order
public(friend) fun market_order(self: &order_info::OrderInfo): bool
public(package) fun market_order(self: &OrderInfo): bool {
self.market_order
}
set_order_id
public(friend) fun set_order_id(self: &mut order_info::OrderInfo, order_id: u128)
public(package) fun set_order_id(self: &mut OrderInfo, order_id: u128) {
self.order_id = order_id;
}
set_paid_fees
public(friend) fun set_paid_fees(self: &mut order_info::OrderInfo, paid_fees: u64)
public(package) fun set_paid_fees(self: &mut OrderInfo, paid_fees: u64) {
self.paid_fees = paid_fees;
}
add_fill
public(friend) fun add_fill(self: &mut order_info::OrderInfo, fill: fill::Fill)
public(package) fun add_fill(self: &mut OrderInfo, fill: Fill) {
self.fills.push_back(fill);
}
fills_ref
public(friend) fun fills_ref(self: &mut order_info::OrderInfo): &mut vector<fill::Fill>
public(package) fun fills_ref(self: &mut OrderInfo): &mut vector<Fill> {
&mut self.fills
}
calculate_partial_fill_balances
Given a partially filled OrderInfo
, the taker fee and maker fee, for the user
placing the order, calculate all of the balances that need to be settled and
the balances that are owed. The executed quantity is multiplied by the taker_fee
and the remaining quantity is multiplied by the maker_fee to get the DEEP fee.
public(friend) fun calculate_partial_fill_balances(self: &mut order_info::OrderInfo, taker_fee: u64, maker_fee: u64): (balances::Balances, balances::Balances)
public(package) fun calculate_partial_fill_balances(
self: &mut OrderInfo,
taker_fee: u64,
maker_fee: u64,
): (Balances, Balances) {
let taker_deep_in = math::mul(
taker_fee,
self
.order_deep_price
.deep_quantity(
self.executed_quantity,
self.cumulative_quote_quantity,
),
);
self.paid_fees = taker_deep_in;
let fills = &mut self.fills;
let mut i = 0;
while (i < fills.length()) {
let fill = &mut fills[i];
if (!fill.expired()) {
let base_quantity = fill.base_quantity();
let quote_quantity = fill.quote_quantity();
let fill_taker_fee = math::mul(
taker_fee,
self
.order_deep_price
.deep_quantity(
base_quantity,
quote_quantity,
),
);
if (fill_taker_fee > 0) {
fill.set_fill_taker_fee(fill_taker_fee);
};
};
i = i + 1;
};
let mut settled_balances = balances::new(0, 0, 0);
let mut owed_balances = balances::new(0, 0, 0);
owed_balances.add_deep(taker_deep_in);
if (self.is_bid) {
settled_balances.add_base(self.executed_quantity);
owed_balances.add_quote(self.cumulative_quote_quantity);
} else {
settled_balances.add_quote(self.cumulative_quote_quantity);
owed_balances.add_base(self.executed_quantity);
};
let remaining_quantity = self.remaining_quantity();
if (self.order_inserted()) {
let maker_deep_in = math::mul(
maker_fee,
self
.order_deep_price
.deep_quantity(
remaining_quantity,
math::mul(remaining_quantity, self.price()),
),
);
self.maker_fees = maker_deep_in;
owed_balances.add_deep(maker_deep_in);
if (self.is_bid) {
owed_balances.add_quote(
math::mul(remaining_quantity, self.price()),
);
} else {
owed_balances.add_base(remaining_quantity);
};
};
(settled_balances, owed_balances)
}
to_order
OrderInfo
is converted to an Order
before being injected into the order book.
This is done to save space in the order book. Order contains the minimum
information required to match orders.
public(friend) fun to_order(self: &order_info::OrderInfo): order::Order
public(package) fun to_order(self: &OrderInfo): Order {
order::new(
self.order_id,
self.balance_manager_id,
self.client_order_id,
self.original_quantity,
self.executed_quantity,
self.fee_is_deep,
self.order_deep_price,
self.epoch,
self.status,
self.expire_timestamp,
)
}
validate_inputs
Validates that the initial order created meets the pool requirements.
public(friend) fun validate_inputs(order_info: &order_info::OrderInfo, tick_size: u64, min_size: u64, lot_size: u64, timestamp: u64)
public(package) fun validate_inputs(
order_info: &OrderInfo,
tick_size: u64,
min_size: u64,
lot_size: u64,
timestamp: u64,
) {
assert!(order_info.original_quantity >= min_size, EOrderBelowMinimumSize);
assert!(order_info.original_quantity % lot_size == 0, EOrderInvalidLotSize);
assert!(timestamp <= order_info.expire_timestamp, EInvalidExpireTimestamp);
assert!(
order_info.order_type >= constants::no_restriction() &&
order_info.order_type <= constants::max_restriction(),
EInvalidOrderType,
);
if (order_info.market_order) {
assert!(
order_info.order_type != constants::post_only(),
EMarketOrderCannotBePostOnly,
);
return
};
assert!(
order_info.price >= constants::min_price() &&
order_info.price <= constants::max_price(),
EOrderInvalidPrice,
);
assert!(order_info.price % tick_size == 0, EOrderInvalidPrice);
}
assert_execution
Assert order types after partial fill against the order book.
public(friend) fun assert_execution(self: &mut order_info::OrderInfo): bool
public(package) fun assert_execution(self: &mut OrderInfo): bool {
if (self.order_type == constants::post_only()) {
assert!(self.executed_quantity == 0, EPOSTOrderCrossesOrderbook)
};
if (self.order_type == constants::fill_or_kill()) {
assert!(
self.executed_quantity == self.original_quantity,
EFOKOrderCannotBeFullyFilled,
)
};
if (self.order_type == constants::immediate_or_cancel()) {
if (self.remaining_quantity() > 0) {
self.status = constants::canceled();
} else {
self.status = constants::filled();
};
return true
};
if (self.remaining_quantity() == 0) {
self.status = constants::filled();
return true
};
if (self.fill_limit_reached) {
return true
};
false
}
remaining_quantity
Returns the remaining quantity for the order.
public(friend) fun remaining_quantity(self: &order_info::OrderInfo): u64
public(package) fun remaining_quantity(self: &OrderInfo): u64 {
self.original_quantity - self.executed_quantity
}
can_match
Returns true if two opposite orders are overlapping in price.
public(friend) fun can_match(self: &order_info::OrderInfo, order: &order::Order): bool
public(package) fun can_match(self: &OrderInfo, order: &Order): bool {
let maker_price = order.price();
(
self.original_quantity - self.executed_quantity > 0 && (
self.is_bid && self.price >= maker_price ||
!self.is_bid && self.price <= maker_price,
),
)
}
match_maker
Matches an OrderInfo
with an Order
from the book. Appends a Fill
to fills.
If the book order is expired, the Fill
will have the expired flag set to true.
Funds for the match or an expired order are returned to the maker as settled.
public(friend) fun match_maker(self: &mut order_info::OrderInfo, maker: &mut order::Order, timestamp: u64): bool
public(package) fun match_maker(
self: &mut OrderInfo,
maker: &mut Order,
timestamp: u64,
): bool {
if (!self.can_match(maker)) return false;
if (self.self_matching_option() == constants::cancel_taker()) {
assert!(
maker.balance_manager_id() != self.balance_manager_id(),
ESelfMatchingCancelTaker,
);
};
let expire_maker =
self.self_matching_option() == constants::cancel_maker() &&
maker.balance_manager_id() == self.balance_manager_id();
let fill = maker.generate_fill(
timestamp,
self.remaining_quantity(),
self.is_bid,
expire_maker,
self.fee_is_deep,
);
self.fills.push_back(fill);
if (fill.expired()) return true;
self.executed_quantity = self.executed_quantity + fill.base_quantity();
self.cumulative_quote_quantity =
self.cumulative_quote_quantity + fill.quote_quantity();
self.status = constants::partially_filled();
if (self.remaining_quantity() == 0) self.status = constants::filled();
true
}
emit_orders_filled
Emit all fills for this order in a vector of OrderFilled
events.
To avoid DOS attacks, 100 fills are emitted at a time. Up to 10,000
fills can be emitted in a single call.
public(friend) fun emit_orders_filled(self: &order_info::OrderInfo, timestamp: u64)
public(package) fun emit_orders_filled(self: &OrderInfo, timestamp: u64) {
let mut i = 0;
while (i < self.fills.length()) {
let fill = &self.fills[i];
if (!fill.expired()) {
event::emit(self.order_filled_from_fill(fill, timestamp));
} else {
let cancel_maker = self.balance_manager_id() == fill.balance_manager_id();
if (cancel_maker) {
self.emit_order_canceled_maker_from_fill(fill, timestamp);
} else {
event::emit(self.order_expired_from_fill(fill, timestamp));
};
};
i = i + 1;
};
}
emit_order_placed
public(friend) fun emit_order_placed(self: &order_info::OrderInfo)
public(package) fun emit_order_placed(self: &OrderInfo) {
event::emit(OrderPlaced {
balance_manager_id: self.balance_manager_id,
pool_id: self.pool_id,
order_id: self.order_id,
client_order_id: self.client_order_id,
is_bid: self.is_bid,
trader: self.trader,
placed_quantity: self.remaining_quantity(),
price: self.price,
expire_timestamp: self.expire_timestamp,
timestamp: self.timestamp,
});
}
emit_order_info
public(friend) fun emit_order_info(self: &order_info::OrderInfo)
public(package) fun emit_order_info(self: &OrderInfo) {
event::emit(*self);
}
set_fill_limit_reached
public(friend) fun set_fill_limit_reached(self: &mut order_info::OrderInfo)
public(package) fun set_fill_limit_reached(self: &mut OrderInfo) {
self.fill_limit_reached = true;
}
set_order_inserted
public(friend) fun set_order_inserted(self: &mut order_info::OrderInfo)
public(package) fun set_order_inserted(self: &mut OrderInfo) {
self.order_inserted = true;
}
order_filled_from_fill
fun order_filled_from_fill(self: &order_info::OrderInfo, fill: &fill::Fill, timestamp: u64): order_info::OrderFilled
fun order_filled_from_fill(
self: &OrderInfo,
fill: &Fill,
timestamp: u64,
): OrderFilled {
OrderFilled {
pool_id: self.pool_id,
maker_order_id: fill.maker_order_id(),
taker_order_id: self.order_id,
maker_client_order_id: fill.maker_client_order_id(),
taker_client_order_id: self.client_order_id,
price: fill.execution_price(),
taker_is_bid: self.is_bid,
taker_fee: fill.taker_fee(),
taker_fee_is_deep: fill.taker_fee_is_deep(),
maker_fee: fill.maker_fee(),
maker_fee_is_deep: fill.maker_fee_is_deep(),
base_quantity: fill.base_quantity(),
quote_quantity: fill.quote_quantity(),
maker_balance_manager_id: fill.balance_manager_id(),
taker_balance_manager_id: self.balance_manager_id,
timestamp,
}
}
order_expired_from_fill
fun order_expired_from_fill(self: &order_info::OrderInfo, fill: &fill::Fill, timestamp: u64): order_info::OrderExpired
fun order_expired_from_fill(
self: &OrderInfo,
fill: &Fill,
timestamp: u64,
): OrderExpired {
OrderExpired {
balance_manager_id: fill.balance_manager_id(),
pool_id: self.pool_id,
order_id: fill.maker_order_id(),
client_order_id: fill.maker_client_order_id(),
trader: self.trader(),
price: fill.execution_price(),
is_bid: !self.is_bid(),
original_quantity: fill.original_maker_quantity(),
base_asset_quantity_canceled: fill.base_quantity(),
timestamp
}
}
emit_order_canceled_maker_from_fill
fun emit_order_canceled_maker_from_fill(self: &order_info::OrderInfo, fill: &fill::Fill, timestamp: u64)
fun emit_order_canceled_maker_from_fill(
self: &OrderInfo,
fill: &Fill,
timestamp: u64,
) {
order::emit_cancel_maker(
fill.balance_manager_id(),
self.pool_id,
fill.maker_order_id(),
fill.maker_client_order_id(),
self.trader(),
fill.execution_price(),
!self.is_bid(),
fill.original_maker_quantity(),
fill.base_quantity(),
timestamp
)
}