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: bitflags for exchange status #330

Merged
merged 4 commits into from
Jan 18, 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
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