Skip to content

Commit

Permalink
program: bitflags for exchange status (#330)
Browse files Browse the repository at this point in the history
* program: bitflags for exchange status

* CHANGELOG

* updates pauseExchange test
  • Loading branch information
crispheaney committed Jan 18, 2023
1 parent 87a8b21 commit 86d68b3
Show file tree
Hide file tree
Showing 18 changed files with 150 additions and 88 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: bitflags for exchange status ([#330](https://github.com/drift-labs/protocol-v2/pull/330))
- program: update fee calculation for filling against openbook
- program: relax conditions for valid oracle price in fulfill_perp_order
- program: handle fallback price when amm has no liquidity ([#324](https://github.com/drift-labs/protocol-v2/pull/324))
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/drift/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ switchboard-v2 = "0.1.10"
arrayref = "0.3.6"
base64 = "0.13.0"
serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "85b4f14", version = "0.5.6", features = ["no-entrypoint"] }
enumflags2 = "0.6.4"

[dev-dependencies]
bytes = "1.2.0"
Expand Down
7 changes: 3 additions & 4 deletions programs/drift/src/controller/orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,8 +701,7 @@ pub fn fill_perp_order(
let is_oracle_valid: bool;
let oracle_validity: OracleValidity;
let oracle_price: i64;
let mut amm_is_available = state.exchange_status != ExchangeStatus::AmmPaused;

let mut amm_is_available = !state.amm_paused()?;
{
let market = &mut perp_market_map.get_ref_mut(&market_index)?;
amm_is_available &= market.status != MarketStatus::AmmPaused;
Expand Down Expand Up @@ -903,8 +902,8 @@ pub fn fill_perp_order(
// Try to update the funding rate at the end of every trade
{
let market = &mut perp_market_map.get_ref_mut(&market_index)?;
let funding_paused = matches!(state.exchange_status, ExchangeStatus::FundingPaused)
|| matches!(market.status, MarketStatus::FundingPaused);
let funding_paused =
state.funding_paused()? || matches!(market.status, MarketStatus::FundingPaused);

controller::funding::update_funding_rate(
market_index,
Expand Down
4 changes: 2 additions & 2 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {

**ctx.accounts.state = State {
admin: *ctx.accounts.admin.key,
exchange_status: ExchangeStatus::Active,
exchange_status: ExchangeStatus::active(),
whitelist_mint: Pubkey::default(),
discount_mint: Pubkey::default(),
oracle_guard_rails: OracleGuardRails::default(),
Expand Down Expand Up @@ -1977,7 +1977,7 @@ pub fn handle_update_discount_mint(

pub fn handle_update_exchange_status(
ctx: Context<AdminUpdateState>,
exchange_status: ExchangeStatus,
exchange_status: u8,
) -> Result<()> {
ctx.accounts.state.exchange_status = exchange_status;
Ok(())
Expand Down
56 changes: 35 additions & 21 deletions programs/drift/src/instructions/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,57 +66,71 @@ pub fn valid_oracle_for_perp_market(
}

pub fn liq_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if matches!(
state.exchange_status,
ExchangeStatus::LiqPaused | ExchangeStatus::Paused
) {
if state
.get_exchange_status()?
.contains(ExchangeStatus::LiqPaused)
{
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn funding_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if matches!(
state.exchange_status,
ExchangeStatus::FundingPaused | ExchangeStatus::Paused
) {
if state.funding_paused()? {
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn amm_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if matches!(
state.exchange_status,
ExchangeStatus::AmmPaused | ExchangeStatus::Paused
) {
if state.amm_paused()? {
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn fill_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if matches!(
state.exchange_status,
ExchangeStatus::FillPaused | ExchangeStatus::Paused
) {
if state
.get_exchange_status()?
.contains(ExchangeStatus::FillPaused)
{
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn deposit_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if state
.get_exchange_status()?
.contains(ExchangeStatus::DepositPaused)
{
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn withdraw_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if matches!(
state.exchange_status,
ExchangeStatus::WithdrawPaused | ExchangeStatus::Paused
) {
if state
.get_exchange_status()?
.contains(ExchangeStatus::WithdrawPaused)
{
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn settle_pnl_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if state
.get_exchange_status()?
.contains(ExchangeStatus::SettlePnlPaused)
{
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
}

pub fn exchange_not_paused(state: &Account<State>) -> anchor_lang::Result<()> {
if state.exchange_status == ExchangeStatus::Paused {
if state.get_exchange_status()?.is_all() {
return Err(ErrorCode::ExchangePaused.into());
}
Ok(())
Expand Down
12 changes: 6 additions & 6 deletions programs/drift/src/instructions/keeper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::state::spot_market::SpotMarket;
use crate::state::spot_market_map::{
get_writable_spot_market_set, get_writable_spot_market_set_from_many,
};
use crate::state::state::{ExchangeStatus, State};
use crate::state::state::State;
use crate::state::user::{MarketType, User, UserStats};
use crate::validate;
use crate::{controller, load, math};
Expand Down Expand Up @@ -340,7 +340,7 @@ pub fn handle_force_cancel_orders<'info>(ctx: Context<ForceCancelOrder>) -> Resu
}

#[access_control(
withdraw_not_paused(&ctx.accounts.state)
settle_pnl_not_paused(&ctx.accounts.state)
)]
pub fn handle_settle_pnl(ctx: Context<SettlePNL>, market_index: u16) -> Result<()> {
let clock = Clock::get()?;
Expand Down Expand Up @@ -460,7 +460,7 @@ pub fn handle_settle_lp<'info>(ctx: Context<SettleLP>, market_index: u16) -> Res
}

#[access_control(
withdraw_not_paused(&ctx.accounts.state)
settle_pnl_not_paused(&ctx.accounts.state)
)]
pub fn handle_settle_expired_market(ctx: Context<UpdateAMM>, market_index: u16) -> Result<()> {
let clock = Clock::get()?;
Expand Down Expand Up @@ -970,7 +970,7 @@ pub fn handle_resolve_perp_bankruptcy(
}

#[access_control(
withdraw_not_paused(&ctx.accounts.state)
withdraw_not_paused(&ctx.accounts.state)
)]
pub fn handle_resolve_spot_bankruptcy(
ctx: Context<ResolveBankruptcy>,
Expand Down Expand Up @@ -1108,7 +1108,7 @@ pub fn handle_update_funding_rate(
&mut oracle_map,
now,
&state.oracle_guard_rails,
matches!(state.exchange_status, ExchangeStatus::FundingPaused),
state.funding_paused()?,
None,
)?;

Expand Down Expand Up @@ -1211,7 +1211,7 @@ pub fn handle_update_spot_market_cumulative_interest(

let oracle_price_data = oracle_map.get_price_data(&spot_market.oracle)?;

if !matches!(state.exchange_status, ExchangeStatus::FundingPaused) {
if !state.funding_paused()? {
controller::spot_balance::update_spot_market_cumulative_interest(
spot_market,
Some(oracle_price_data),
Expand Down
4 changes: 4 additions & 0 deletions programs/drift/src/instructions/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ pub fn handle_initialize_user_stats(ctx: Context<InitializeUserStats>) -> Result
Ok(())
}

#[access_control(
deposit_not_paused(&ctx.accounts.state)
)]
pub fn handle_deposit(
ctx: Context<Deposit>,
market_index: u16,
Expand Down Expand Up @@ -472,6 +475,7 @@ pub fn handle_withdraw(
}

#[access_control(
deposit_not_paused(&ctx.accounts.state)
withdraw_not_paused(&ctx.accounts.state)
)]
pub fn handle_transfer_deposit(
Expand Down
2 changes: 1 addition & 1 deletion programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ pub mod drift {

pub fn update_exchange_status(
ctx: Context<AdminUpdateState>,
exchange_status: ExchangeStatus,
exchange_status: u8,
) -> Result<()> {
handle_update_exchange_status(ctx, exchange_status)
}
Expand Down
48 changes: 35 additions & 13 deletions programs/drift/src/state/state.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use anchor_lang::prelude::*;
use borsh::{BorshDeserialize, BorshSerialize};
use enumflags2::BitFlags;

use crate::error::DriftResult;
use crate::math::constants::{
FEE_DENOMINATOR, FEE_PERCENTAGE_DENOMINATOR, MAX_REFERRER_REWARD_EPOCH_UPPER_BOUND,
};
use crate::math::safe_unwrap::SafeUnwrap;
use crate::state::traits::Size;

#[account]
Expand All @@ -29,26 +31,46 @@ pub struct State {
pub min_perp_auction_duration: u8,
pub default_market_order_time_in_force: u8,
pub default_spot_auction_duration: u8,
pub exchange_status: ExchangeStatus,
pub exchange_status: u8,
pub liquidation_duration: u8,
pub initial_pct_to_liquidate: u16,
pub padding: [u8; 14],
}

#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq)]
#[derive(BitFlags, Clone, Copy, PartialEq, Debug, Eq)]
pub enum ExchangeStatus {
Active,
FundingPaused,
AmmPaused,
FillPaused,
LiqPaused,
WithdrawPaused,
Paused,
// Active = 0b00000000
DepositPaused = 0b00000001,
WithdrawPaused = 0b00000010,
AmmPaused = 0b00000100,
FillPaused = 0b00001000,
LiqPaused = 0b00010000,
FundingPaused = 0b00100000,
SettlePnlPaused = 0b01000000,
// Paused = 0b11111111
}

impl Default for ExchangeStatus {
fn default() -> Self {
ExchangeStatus::Active
impl ExchangeStatus {
pub fn active() -> u8 {
BitFlags::<ExchangeStatus>::empty().bits() as u8
}
}

impl State {
pub fn get_exchange_status(&self) -> DriftResult<BitFlags<ExchangeStatus>> {
BitFlags::<ExchangeStatus>::from_bits(usize::from(self.exchange_status)).safe_unwrap()
}

pub fn amm_paused(&self) -> DriftResult<bool> {
Ok(self
.get_exchange_status()?
.contains(ExchangeStatus::AmmPaused))
}

pub fn funding_paused(&self) -> DriftResult<bool> {
Ok(self
.get_exchange_status()?
.contains(ExchangeStatus::FundingPaused))
}
}

Expand Down
16 changes: 6 additions & 10 deletions sdk/src/idl/drift.json
Original file line number Diff line number Diff line change
Expand Up @@ -3705,9 +3705,7 @@
"args": [
{
"name": "exchangeStatus",
"type": {
"defined": "ExchangeStatus"
}
"type": "u8"
}
]
},
Expand Down Expand Up @@ -4384,9 +4382,7 @@
},
{
"name": "exchangeStatus",
"type": {
"defined": "ExchangeStatus"
}
"type": "u8"
},
{
"name": "liquidationDuration",
Expand Down Expand Up @@ -6360,10 +6356,10 @@
"kind": "enum",
"variants": [
{
"name": "Active"
"name": "DepositPaused"
},
{
"name": "FundingPaused"
"name": "WithdrawPaused"
},
{
"name": "AmmPaused"
Expand All @@ -6375,10 +6371,10 @@
"name": "LiqPaused"
},
{
"name": "WithdrawPaused"
"name": "FundingPaused"
},
{
"name": "Paused"
"name": "SettlePnlPaused"
}
]
}
Expand Down
10 changes: 6 additions & 4 deletions sdk/src/math/exchangeStatus.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import {
ExchangeStatus,
isOneOfVariant,
isVariant,
PerpMarketAccount,
SpotMarketAccount,
StateAccount,
} from '../types';

export function exchangePaused(state: StateAccount): boolean {
return isVariant(state.exchangeStatus, 'paused');
return state.exchangeStatus !== ExchangeStatus.ACTIVE;
}

export function fillPaused(
state: StateAccount,
market: PerpMarketAccount | SpotMarketAccount
): boolean {
return (
isOneOfVariant(state.exchangeStatus, ['paused', 'fillPaused']) ||
(state.exchangeStatus & ExchangeStatus.FILL_PAUSED) ===
ExchangeStatus.FILL_PAUSED ||
isOneOfVariant(market.status, ['paused', 'fillPaused'])
);
}
Expand All @@ -25,7 +26,8 @@ export function ammPaused(
market: PerpMarketAccount | SpotMarketAccount
): boolean {
return (
isOneOfVariant(state.exchangeStatus, ['paused', 'ammPaused']) ||
(state.exchangeStatus & ExchangeStatus.AMM_PAUSED) ===
ExchangeStatus.AMM_PAUSED ||
isOneOfVariant(market.status, ['paused', 'ammPaused'])
);
}
Loading

0 comments on commit 86d68b3

Please sign in to comment.