Skip to content

Commit

Permalink
program: add buffer before limit tif can be expired (#551)
Browse files Browse the repository at this point in the history
* program: throw error if filler passes limit order w no makers and amm is unavailable

* dont pay reward for expiring limit orders, block unfillable spot orders

* add back incentive to cancel limit tif

* add back logic to expire limit orders

* CHANGELOG

* program: dont let limit orders get filled in btwn when its expired and buffer met

* make new function for validate_perp_fill_possible

* udapte CHANGELOG
  • Loading branch information
crispheaney committed Jul 25, 2023
1 parent c1778da commit 3d790e2
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixes

- program: add buffer before limit tif can be expired ([#551](https://github.com/drift-labs/protocol-v2/pull/530))
- ts-sdk: fix abs for dustBaseAssetValue in getPerpPositionWithLPSettle ([#543](https://github.com/drift-labs/protocol-v2/pull/543))
- program: add a fixed buffer margin requirement for lp_shares ([#546](https://github.com/drift-labs/protocol-v2/pull/546))
- program: use fill margin type in fulfill_spot_order
Expand Down
6 changes: 4 additions & 2 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,9 @@ pub fn fill_perp_order(
return Ok(0);
}

let should_expire_order = should_expire_order(user, order_index, now)?;
validate_perp_fill_possible(state, user, order_index, slot, makers_and_referrer.0.len())?;

let should_expire_order = should_expire_order_before_fill(user, order_index, now)?;

let position_index =
get_position_index(&user.perp_positions, user.orders[order_index].market_index)?;
Expand Down Expand Up @@ -3216,7 +3218,7 @@ pub fn fill_spot_order(
}
}

let should_expire_order = should_expire_order(user, order_index, now)?;
let should_expire_order = should_expire_order_before_fill(user, order_index, now)?;

let should_cancel_reduce_only = if user.orders[order_index].reduce_only {
let market_index = user.orders[order_index].market_index;
Expand Down
2 changes: 2 additions & 0 deletions programs/drift/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@ pub enum ErrorCode {
SpotMarketReduceOnly,
#[msg("FundingWasNotUpdated")]
FundingWasNotUpdated,
#[msg("ImpossibleFill")]
ImpossibleFill,
}

#[macro_export]
Expand Down
49 changes: 46 additions & 3 deletions programs/drift/src/math/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ use crate::controller::position::PositionDelta;
use crate::controller::position::PositionDirection;
use crate::error::{DriftResult, ErrorCode};
use crate::math::amm::calculate_amm_available_liquidity;
use crate::math::auction::is_auction_complete;
use crate::math::auction::{is_amm_available_liquidity_source, is_auction_complete};
use crate::math::casting::Cast;
use crate::{
math, PostOnlyParam, BASE_PRECISION_I128, OPEN_ORDER_MARGIN_REQUIREMENT, PERCENTAGE_PRECISION,
PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, QUOTE_PRECISION_I128, SPOT_WEIGHT_PRECISION,
math, PostOnlyParam, State, BASE_PRECISION_I128, OPEN_ORDER_MARGIN_REQUIREMENT,
PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, QUOTE_PRECISION_I128,
SPOT_WEIGHT_PRECISION,
};

use crate::math::constants::MARGIN_PRECISION_U128;
Expand Down Expand Up @@ -301,6 +302,29 @@ pub fn get_position_delta_for_fill(
})
}

#[inline(always)]
pub fn validate_perp_fill_possible(
state: &State,
user: &User,
order_index: usize,
slot: u64,
num_makers: usize,
) -> DriftResult {
let amm_available = is_amm_available_liquidity_source(
&user.orders[order_index],
state.min_perp_auction_duration,
slot,
)?;

if !amm_available && num_makers == 0 && user.orders[order_index].is_limit_order() {
msg!("invalid fill. order is limit order, amm is not available and no makers present");
return Err(ErrorCode::ImpossibleFill);
}

Ok(())
}

#[inline(always)]
pub fn should_cancel_market_order_after_fill(
user: &User,
user_order_index: usize,
Expand All @@ -315,6 +339,25 @@ pub fn should_cancel_market_order_after_fill(
&& is_auction_complete(order.slot, order.auction_duration, slot)?)
}

#[inline(always)]
pub fn should_expire_order_before_fill(
user: &User,
order_index: usize,
now: i64,
) -> DriftResult<bool> {
let should_order_be_expired = should_expire_order(user, order_index, now)?;
if should_order_be_expired && user.orders[order_index].is_limit_order() {
let now_plus_buffer = now.safe_add(15)?;
if !should_expire_order(user, order_index, now_plus_buffer)? {
msg!("invalid fill. cant force expire limit order until 15s after max_ts. max ts {}, now {}, now plus buffer {}", user.orders[order_index].max_ts, now, now_plus_buffer);
return Err(ErrorCode::ImpossibleFill);
}
}

Ok(should_order_be_expired)
}

#[inline(always)]
pub fn should_expire_order(user: &User, user_order_index: usize, now: i64) -> DriftResult<bool> {
let order = &user.orders[user_order_index];
if order.status != OrderStatus::Open || order.max_ts == 0 || order.must_be_triggered() {
Expand Down
18 changes: 14 additions & 4 deletions sdk/src/dlob/DLOB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -931,12 +931,22 @@ export class DLOB {

// All bids/asks that can expire
// dont try to expire limit orders with tif as its inefficient use of blockspace
const bidGenerators = [nodeLists.market.bid.getGenerator()];
const askGenerators = [nodeLists.market.ask.getGenerator()];
const bidGenerators = [
nodeLists.takingLimit.bid.getGenerator(),
nodeLists.restingLimit.bid.getGenerator(),
nodeLists.floatingLimit.bid.getGenerator(),
nodeLists.market.bid.getGenerator(),
];
const askGenerators = [
nodeLists.takingLimit.ask.getGenerator(),
nodeLists.restingLimit.ask.getGenerator(),
nodeLists.floatingLimit.ask.getGenerator(),
nodeLists.market.ask.getGenerator(),
];

for (const bidGenerator of bidGenerators) {
for (const bid of bidGenerator) {
if (isOrderExpired(bid.order, ts)) {
if (isOrderExpired(bid.order, ts, true)) {
nodesToFill.push({
node: bid,
makerNodes: [],
Expand All @@ -947,7 +957,7 @@ export class DLOB {

for (const askGenerator of askGenerators) {
for (const ask of askGenerator) {
if (isOrderExpired(ask.order, ts)) {
if (isOrderExpired(ask.order, ts, true)) {
nodesToFill.push({
node: ask,
makerNodes: [],
Expand Down
15 changes: 13 additions & 2 deletions sdk/src/math/orders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,11 @@ function isSameDirection(
);
}

export function isOrderExpired(order: Order, ts: number): boolean {
export function isOrderExpired(
order: Order,
ts: number,
enforceBuffer = false
): boolean {
if (
mustBeTriggered(order) ||
!isVariant(order.status, 'open') ||
Expand All @@ -288,7 +292,14 @@ export function isOrderExpired(order: Order, ts: number): boolean {
return false;
}

return new BN(ts).gt(order.maxTs);
let maxTs;
if (enforceBuffer && isLimitOrder(order)) {
maxTs = order.maxTs.addn(15);
} else {
maxTs = order.maxTs;
}

return new BN(ts).gt(maxTs);
}

export function isMarketOrder(order: Order): boolean {
Expand Down

0 comments on commit 3d790e2

Please sign in to comment.