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

program: fill_perp checks maintenance margin req if position is reduced #714

Merged
merged 7 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- program: if order reduces maker position, check maintenance margin requirement ([#714](https://github.com/drift-labs/protocol-v2/pull/714))

### Fixes

### Breaking
Expand Down
105 changes: 77 additions & 28 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1516,7 +1516,8 @@

let mut base_asset_amount = 0_u64;
let mut quote_asset_amount = 0_u64;
let mut makers_filled: BTreeMap<Pubkey, bool> = BTreeMap::new();
let mut maker_fills: BTreeMap<Pubkey, i64> = BTreeMap::new();
let maker_direction = user.orders[user_order_index].direction.opposite();
for fulfillment_method in fulfillment_methods.iter() {
if user.orders[user_order_index].status != OrderStatus::Open {
break;
Expand Down Expand Up @@ -1573,7 +1574,7 @@
Some(&maker),
)?;

let (fill_base_asset_amount, fill_quote_asset_amount) =
let (fill_base_asset_amount, fill_quote_asset_amount, maker_fill_base_asset_amount) =
fulfill_perp_order_with_match(
market.deref_mut(),
user,
Expand All @@ -1598,8 +1599,13 @@
oracle_map,
)?;

if fill_base_asset_amount != 0 {
makers_filled.insert(*maker_key, true);
if maker_fill_base_asset_amount != 0 {
update_maker_fills_map(
&mut maker_fills,
maker_key,
maker_direction,
maker_fill_base_asset_amount,
)?;
}

(fill_base_asset_amount, fill_quote_asset_amount)
Expand All @@ -1621,6 +1627,16 @@
quote_asset_amount
)?;

let total_maker_fill = maker_fills.values().sum::<i64>();

validate!(

Check warning on line 1632 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L1632

Added line #L1632 was not covered by tests
total_maker_fill.unsigned_abs() <= base_asset_amount,
ErrorCode::DefaultError,

Check warning on line 1634 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L1634

Added line #L1634 was not covered by tests
"invalid total maker fill {} total fill {}",
total_maker_fill,
base_asset_amount
)?;

let taker_margin_calculation =
calculate_margin_requirement_and_total_collateral_and_liability_info(
user,
Expand All @@ -1643,16 +1659,22 @@
return Err(ErrorCode::InsufficientCollateral);
}

for (maker_key, _) in makers_filled {
for (maker_key, maker_base_asset_amount_filled) in maker_fills {
let maker = makers_and_referrer.get_ref(&maker_key)?;

let margin_type = select_margin_type_for_perp_maker(
&maker,
maker_base_asset_amount_filled,
market_index,
)?;

let maker_margin_calculation =
calculate_margin_requirement_and_total_collateral_and_liability_info(
&maker,
perp_market_map,
spot_market_map,
oracle_map,
MarginContext::standard(MarginRequirementType::Fill),
MarginContext::standard(margin_type),
)?;

if !maker_margin_calculation.meets_margin_requirement() {
Expand Down Expand Up @@ -1693,6 +1715,27 @@
Ok((Some(referrer), Some(referrer_stats)))
}

#[inline(always)]
fn update_maker_fills_map(
map: &mut BTreeMap<Pubkey, i64>,
maker_key: &Pubkey,
maker_direction: PositionDirection,
fill: u64,
) -> DriftResult {
let signed_fill = match maker_direction {
PositionDirection::Long => fill.cast::<i64>()?,
PositionDirection::Short => -fill.cast::<i64>()?,
};

if let Some(maker_filled) = map.get_mut(maker_key) {
*maker_filled = maker_filled.safe_add(signed_fill)?;
} else {
map.insert(*maker_key, signed_fill);
}

Ok(())
}

fn determine_if_user_order_is_position_decreasing(
user: &User,
market_index: u16,
Expand Down Expand Up @@ -2006,12 +2049,12 @@
slot: u64,
fee_structure: &FeeStructure,
oracle_map: &mut OracleMap,
) -> DriftResult<(u64, u64)> {
) -> DriftResult<(u64, u64, u64)> {
if !are_orders_same_market_but_different_sides(
&maker.orders[maker_order_index],
&taker.orders[taker_order_index],
) {
return Ok((0_u64, 0_u64));
return Ok((0_u64, 0_u64, 0_u64));
}

let (bid_price, ask_price) = market.amm.bid_ask_price(market.amm.reserve_price()?)?;
Expand Down Expand Up @@ -2060,7 +2103,7 @@
maker_price,
taker_price
);
return Ok((0_u64, 0_u64));
return Ok((0_u64, 0_u64, 0_u64));
}

let (base_asset_amount, _) = calculate_fill_for_matched_orders(
Expand All @@ -2072,7 +2115,7 @@
)?;

if base_asset_amount == 0 {
return Ok((0_u64, 0_u64));
return Ok((0_u64, 0_u64, 0_u64));

Check warning on line 2118 in programs/drift/src/controller/orders.rs

View check run for this annotation

Codecov / codecov/patch

programs/drift/src/controller/orders.rs#L2118

Added line #L2118 was not covered by tests
}

let sanitize_clamp_denominator = market.get_sanitize_clamp_denominator()?;
Expand Down Expand Up @@ -2133,17 +2176,18 @@
let taker_base_asset_amount = taker.orders[taker_order_index]
.get_base_asset_amount_unfilled(Some(taker_existing_position))?;

let (base_asset_amount_fulfilled, quote_asset_amount) = calculate_fill_for_matched_orders(
maker_base_asset_amount,
maker_price,
taker_base_asset_amount,
PERP_DECIMALS,
maker_direction,
)?;
let (base_asset_amount_fulfilled_by_maker, quote_asset_amount) =
calculate_fill_for_matched_orders(
maker_base_asset_amount,
maker_price,
taker_base_asset_amount,
PERP_DECIMALS,
maker_direction,
)?;

validate_fill_price(
quote_asset_amount,
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
BASE_PRECISION_U64,
taker_direction,
taker_price,
Expand All @@ -2152,14 +2196,15 @@

validate_fill_price(
quote_asset_amount,
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
BASE_PRECISION_U64,
maker_direction,
maker_price,
false,
)?;

total_base_asset_amount = total_base_asset_amount.safe_add(base_asset_amount_fulfilled)?;
total_base_asset_amount =
total_base_asset_amount.safe_add(base_asset_amount_fulfilled_by_maker)?;
total_quote_asset_amount = total_quote_asset_amount.safe_add(quote_asset_amount)?;

let maker_position_index = get_position_index(
Expand All @@ -2168,7 +2213,7 @@
)?;

let maker_position_delta = get_position_delta_for_fill(
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
quote_asset_amount,
maker.orders[maker_order_index].direction,
)?;
Expand All @@ -2192,7 +2237,7 @@
)?;

let taker_position_delta = get_position_delta_for_fill(
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
quote_asset_amount,
taker.orders[taker_order_index].direction,
)?;
Expand Down Expand Up @@ -2304,26 +2349,26 @@

update_order_after_fill(
&mut taker.orders[taker_order_index],
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
quote_asset_amount,
)?;

decrease_open_bids_and_asks(
&mut taker.perp_positions[taker_position_index],
&taker.orders[taker_order_index].direction,
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
)?;

update_order_after_fill(
&mut maker.orders[maker_order_index],
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
quote_asset_amount,
)?;

decrease_open_bids_and_asks(
&mut maker.perp_positions[maker_position_index],
&maker.orders[maker_order_index].direction,
base_asset_amount_fulfilled,
base_asset_amount_fulfilled_by_maker,
)?;

let fill_record_id = get_then_update_id!(market, next_fill_record_id);
Expand All @@ -2340,7 +2385,7 @@
Some(*filler_key),
Some(fill_record_id),
Some(filler_reward),
Some(base_asset_amount_fulfilled),
Some(base_asset_amount_fulfilled_by_maker),
Some(quote_asset_amount),
Some(taker_fee),
Some(maker_rebate),
Expand Down Expand Up @@ -2369,7 +2414,11 @@
market_position.open_orders -= 1;
}

Ok((total_base_asset_amount, total_quote_asset_amount))
Ok((
total_base_asset_amount,
total_quote_asset_amount,
base_asset_amount_fulfilled_by_maker,
))
}

pub fn update_order_after_fill(
Expand Down
Loading
Loading