-
Notifications
You must be signed in to change notification settings - Fork 100
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: settle_pnls that handles invalid oracle and efficient cus #1030
Changes from 2 commits
4d6b57a
0b00ce5
24e9661
0ba64c5
7d40581
26321be
17ecc0e
80cde25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ use crate::state::oracle_map::OracleMap; | |
use crate::state::paused_operations::PerpOperation; | ||
use crate::state::perp_market::MarketStatus; | ||
use crate::state::perp_market_map::PerpMarketMap; | ||
use crate::state::settle_pnl_mode::SettlePnlMode; | ||
use crate::state::spot_market::{SpotBalance, SpotBalanceType}; | ||
use crate::state::spot_market_map::SpotMarketMap; | ||
use crate::state::state::State; | ||
|
@@ -56,6 +57,8 @@ pub fn settle_pnl( | |
oracle_map: &mut OracleMap, | ||
clock: &Clock, | ||
state: &State, | ||
meets_margin_requirement: Option<bool>, | ||
mode: SettlePnlMode, | ||
) -> DriftResult { | ||
validate!(!user.is_bankrupt(), ErrorCode::UserBankrupt)?; | ||
let now = clock.unix_timestamp; | ||
|
@@ -119,14 +122,23 @@ pub fn settle_pnl( | |
} | ||
} | ||
} else if unrealized_pnl < 0 { | ||
// may already be cached | ||
let meets_margin_requirement = match meets_margin_requirement { | ||
Some(meets_margin_requirement) => meets_margin_requirement, | ||
None => meets_maintenance_margin_requirement( | ||
user, | ||
perp_market_map, | ||
spot_market_map, | ||
oracle_map, | ||
)?, | ||
}; | ||
|
||
// cannot settle pnl this way on a user who is in liquidation territory | ||
if !(meets_maintenance_margin_requirement( | ||
user, | ||
perp_market_map, | ||
spot_market_map, | ||
oracle_map, | ||
)?) { | ||
return Err(ErrorCode::InsufficientCollateralForSettlingPNL); | ||
if !meets_margin_requirement { | ||
return mode.result( | ||
ErrorCode::InsufficientCollateralForSettlingPNL, | ||
"Does not meet margin requirement", | ||
); | ||
} | ||
} | ||
|
||
|
@@ -151,55 +163,63 @@ pub fn settle_pnl( | |
if !is_oracle_valid_for_action(oracle_validity, Some(DriftAction::SettlePnl))? | ||
|| !perp_market.is_price_divergence_ok_for_settle_pnl(oracle_price)? | ||
{ | ||
validate!( | ||
perp_market.amm.last_oracle_valid, | ||
ErrorCode::InvalidOracle, | ||
"Oracle Price detected as invalid ({}) on last perp market update", | ||
oracle_validity | ||
)?; | ||
|
||
validate!( | ||
oracle_map.slot == perp_market.amm.last_update_slot, | ||
ErrorCode::AMMNotUpdatedInSameSlot, | ||
"Market={} AMM must be updated in a prior instruction within same slot (current={} != amm={}, last_oracle_valid={})", | ||
market_index, | ||
oracle_map.slot, | ||
perp_market.amm.last_update_slot, | ||
perp_market.amm.last_oracle_valid | ||
)?; | ||
if !perp_market.amm.last_oracle_valid { | ||
let msg = format!( | ||
"Oracle Price detected as invalid ({}) on last perp market update for Market = {}", | ||
oracle_validity, | ||
market_index | ||
); | ||
return mode.result(ErrorCode::InvalidOracle, &msg); | ||
} | ||
|
||
if oracle_map.slot != perp_market.amm.last_update_slot { | ||
let msg = format!( | ||
"Market={} AMM must be updated in a prior instruction within same slot (current={} != amm={}, last_oracle_valid={})", | ||
market_index, | ||
oracle_map.slot, | ||
perp_market.amm.last_update_slot, | ||
perp_market.amm.last_oracle_valid | ||
); | ||
return mode.result(ErrorCode::AMMNotUpdatedInSameSlot, &msg); | ||
} | ||
} | ||
} | ||
} | ||
|
||
validate!( | ||
!perp_market.is_operation_paused(PerpOperation::SettlePnl), | ||
ErrorCode::InvalidMarketStatusToSettlePnl, | ||
"Cannot settle pnl under current market = {} status", | ||
market_index | ||
)?; | ||
if perp_market.is_operation_paused(PerpOperation::SettlePnl) { | ||
let msg = format!( | ||
"Cannot settle pnl under current market = {} status", | ||
market_index | ||
); | ||
return mode.result(ErrorCode::InvalidMarketStatusToSettlePnl, &msg); | ||
} | ||
|
||
if user.perp_positions[position_index].base_asset_amount != 0 { | ||
validate!( | ||
!perp_market.is_operation_paused(PerpOperation::SettlePnlWithPosition), | ||
ErrorCode::InvalidMarketStatusToSettlePnl, | ||
"Cannot settle pnl with position under current market = {} operation paused", | ||
market_index | ||
)?; | ||
if perp_market.is_operation_paused(PerpOperation::SettlePnlWithPosition) { | ||
let msg = format!( | ||
"Cannot settle pnl with position under current market = {} operation paused", | ||
market_index | ||
); | ||
return mode.result(ErrorCode::InvalidMarketStatusToSettlePnl, &msg); | ||
} | ||
|
||
validate!( | ||
perp_market.status == MarketStatus::Active, | ||
ErrorCode::InvalidMarketStatusToSettlePnl, | ||
"Cannot settle pnl with position under non-Active current market = {} status", | ||
market_index | ||
)?; | ||
if perp_market.status != MarketStatus::Active { | ||
let msg = format!( | ||
"Cannot settle pnl with position under non-Active current market = {} status", | ||
market_index | ||
); | ||
return mode.result(ErrorCode::InvalidMarketStatusToSettlePnl, &msg); | ||
} | ||
} else { | ||
validate!( | ||
perp_market.status == MarketStatus::Active | ||
|| perp_market.status == MarketStatus::ReduceOnly, | ||
ErrorCode::InvalidMarketStatusToSettlePnl, | ||
"Cannot settle pnl under current market = {} status (neither Active or ReduceOnly)", | ||
market_index | ||
)?; | ||
if perp_market.status != MarketStatus::Active | ||
&& perp_market.status != MarketStatus::ReduceOnly | ||
{ | ||
let msg = format!( | ||
"Cannot settle pnl under current market = {} status (neither Active or ReduceOnly)", | ||
market_index | ||
); | ||
return mode.result(ErrorCode::InvalidMarketStatusToSettlePnl, &msg); | ||
} | ||
} | ||
|
||
let pnl_pool_token_amount = get_token_amount( | ||
|
@@ -237,25 +257,31 @@ pub fn settle_pnl( | |
user_unsettled_pnl, | ||
now, | ||
)?; | ||
|
||
if user_unsettled_pnl == 0 { | ||
msg!("User has no unsettled pnl for market {}", market_index); | ||
return Ok(()); | ||
let msg = format!("User has no unsettled pnl for market {}", market_index); | ||
return mode.result(ErrorCode::NoUnsettledPnl, &msg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it bad these dont always return OK? do we need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only the 'MUST' will cause |
||
} else if pnl_to_settle_with_user == 0 { | ||
msg!( | ||
let msg = format!( | ||
"Pnl Pool cannot currently settle with user for market {}", | ||
market_index | ||
); | ||
return Ok(()); | ||
return mode.result(ErrorCode::PnlPoolCantSettleUser, &msg); | ||
} | ||
|
||
validate!( | ||
pnl_to_settle_with_user < 0 | ||
|| max_pnl_pool_excess > 0 | ||
|| (pnl_to_settle_with_user > 0 && user.is_being_liquidated()) | ||
|| (user.authority.eq(authority) || user.delegate.eq(authority)), | ||
ErrorCode::UserMustSettleTheirOwnPositiveUnsettledPNL, | ||
"User must settle their own unsettled pnl when its positive and pnl pool not in excess" | ||
)?; | ||
let user_must_settle_themself = pnl_to_settle_with_user > 0 | ||
&& max_pnl_pool_excess < 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be <= There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
&& !user.is_being_liquidated() | ||
&& !user.authority.eq(authority) | ||
&& !user.delegate.eq(authority); | ||
|
||
if user_must_settle_themself { | ||
let msg = format!( | ||
"Market = {} user must settle their own unsettled pnl when its positive and pnl pool not in excess", | ||
market_index | ||
); | ||
return mode.result(ErrorCode::UserMustSettleTheirOwnPositiveUnsettledPNL, &msg); | ||
} | ||
|
||
update_spot_balances( | ||
pnl_to_settle_with_user.unsigned_abs(), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should market_index info get propagated if errors get hit for logs?