From f6c2cada9a05276457ada9178879fc67c15d9e67 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Thu, 8 Aug 2024 14:01:15 -0700 Subject: [PATCH 01/11] added new ix to set liquidation status --- programs/drift/src/controller/liquidation.rs | 43 +++++++++++++++++--- programs/drift/src/instructions/keeper.rs | 42 +++++++++++++++++++ programs/drift/src/lib.rs | 6 +++ 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 6456ccbc4..7e44679d5 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -66,7 +66,7 @@ use crate::state::spot_market::SpotBalanceType; use crate::state::spot_market_map::SpotMarketMap; use crate::state::state::State; use crate::state::traits::Size; -use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats}; +use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats, UserStatus}; use crate::state::user_map::{UserMap, UserStatsMap}; use crate::validate; use crate::{get_then_update_id, load_mut}; @@ -118,7 +118,6 @@ pub fn liquidate_perp( drop(market); - // Settle user's funding payments so that collateral is up to date settle_funding_payment( user, user_key, @@ -126,7 +125,6 @@ pub fn liquidate_perp( now, )?; - // Settle user's funding payments so that collateral is up to date settle_funding_payment( liquidator, liquidator_key, @@ -711,7 +709,6 @@ pub fn liquidate_perp_with_fill( drop(market); - // Settle user's funding payments so that collateral is up to date settle_funding_payment( &mut user, user_key, @@ -719,7 +716,6 @@ pub fn liquidate_perp_with_fill( now, )?; - // Settle user's funding payments so that collateral is up to date settle_funding_payment( &mut liquidator, liquidator_key, @@ -3015,3 +3011,40 @@ pub fn calculate_margin_freed( Ok((margin_freed, margin_calculation_after)) } + +pub fn set_user_status_to_being_liquidated( + user: &mut User, + perp_market_map: &PerpMarketMap, + spot_market_map: &SpotMarketMap, + oracle_map: &mut OracleMap, + slot: u64, + state: &State, +) -> DriftResult { + validate!( + !user.is_bankrupt(), + ErrorCode::UserBankrupt, + "user bankrupt", + )?; + + validate!( + !user.is_being_liquidated(), + ErrorCode::UserIsBeingLiquidated, + "user is already being liquidated", + )?; + + let liquidation_margin_buffer_ratio = state.liquidation_margin_buffer_ratio; + let margin_calculation = calculate_margin_requirement_and_total_collateral_and_liability_info( + user, + perp_market_map, + spot_market_map, + oracle_map, + MarginContext::liquidation(liquidation_margin_buffer_ratio), + )?; + + if !user.is_being_liquidated() && margin_calculation.meets_margin_requirement() { + return Err(ErrorCode::SufficientCollateral); + } else { + user.enter_liquidation(slot)?; + } + Ok(()) +} diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index abf5bc682..c7b226cf0 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -984,6 +984,40 @@ pub fn handle_liquidate_perp_pnl_for_deposit<'c: 'info, 'info>( Ok(()) } +#[access_control( + liq_not_paused(&ctx.accounts.state) +)] +pub fn handle_set_user_status_to_being_liquidated<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, SetUserStatusToBeingLiquidated<'info>>, +) -> Result<()> { + let state = &ctx.accounts.state; + let clock = Clock::get()?; + let user = &mut load_mut!(ctx.accounts.user)?; + + let AccountMaps { + perp_market_map, + spot_market_map, + mut oracle_map, + } = load_maps( + &mut ctx.remaining_accounts.iter().peekable(), + &MarketSet::new(), + &MarketSet::new(), + clock.slot, + Some(state.oracle_guard_rails), + )?; + + controller::liquidation::set_user_status_to_being_liquidated( + user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + clock.slot, + &state, + )?; + + Ok(()) +} + #[access_control( withdraw_not_paused(&ctx.accounts.state) )] @@ -1912,6 +1946,14 @@ pub struct LiquidatePerpPnlForDeposit<'info> { pub user_stats: AccountLoader<'info, UserStats>, } +#[derive(Accounts)] +pub struct SetUserStatusToBeingLiquidated<'info> { + pub state: Box>, + pub authority: Signer<'info>, + #[account(mut)] + pub user: AccountLoader<'info, User>, +} + #[derive(Accounts)] #[instruction(spot_market_index: u16,)] pub struct ResolveBankruptcy<'info> { diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index ca933a504..3da2d0781 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -467,6 +467,12 @@ pub mod drift { ) } + pub fn set_user_status_to_being_liquidated<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, SetUserStatusToBeingLiquidated<'info>>, + ) -> Result<()> { + handle_set_user_status_to_being_liquidated(ctx) + } + pub fn resolve_perp_pnl_deficit<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, ResolvePerpPnlDeficit<'info>>, spot_market_index: u16, From 6623153a7f6a5c663dc6c789a72b0b674cb18a07 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Fri, 9 Aug 2024 11:29:38 -0700 Subject: [PATCH 02/11] finish tests for stting user status --- programs/drift/src/controller/liquidation.rs | 2 +- .../drift/src/controller/liquidation/tests.rs | 312 ++++++++++++++++++ 2 files changed, 313 insertions(+), 1 deletion(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 7e44679d5..3a68c0fc5 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -66,7 +66,7 @@ use crate::state::spot_market::SpotBalanceType; use crate::state::spot_market_map::SpotMarketMap; use crate::state::state::State; use crate::state::traits::Size; -use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats, UserStatus}; +use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats}; use crate::state::user_map::{UserMap, UserStatsMap}; use crate::validate; use crate::{get_then_update_id, load_mut}; diff --git a/programs/drift/src/controller/liquidation/tests.rs b/programs/drift/src/controller/liquidation/tests.rs index 936b76ed3..10adcdaad 100644 --- a/programs/drift/src/controller/liquidation/tests.rs +++ b/programs/drift/src/controller/liquidation/tests.rs @@ -7874,3 +7874,315 @@ pub mod resolve_spot_bankruptcy { assert_eq!(deposit_token_amount, 900 * QUOTE_PRECISION); } } + +pub mod set_user_status_to_being_liquidated { + + use crate::state::state::State; + use std::str::FromStr; + + use anchor_lang::Owner; + use solana_program::pubkey::Pubkey; + + use crate::controller::liquidation::set_user_status_to_being_liquidated; + use crate::controller::position::PositionDirection; + use crate::error::ErrorCode; + use crate::math::constants::{ + AMM_RESERVE_PRECISION, BASE_PRECISION_I128, BASE_PRECISION_I64, BASE_PRECISION_U64, + LIQUIDATION_FEE_PRECISION, PEG_PRECISION, QUOTE_PRECISION_I128, QUOTE_PRECISION_I64, + SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, + }; + use crate::state::oracle::{HistoricalOracleData, OracleSource}; + use crate::state::oracle_map::OracleMap; + use crate::state::perp_market::{MarketStatus, PerpMarket, AMM}; + use crate::state::perp_market_map::PerpMarketMap; + use crate::state::spot_market::{SpotBalanceType, SpotMarket}; + use crate::state::spot_market_map::SpotMarketMap; + use crate::state::user::{ + Order, OrderStatus, OrderType, PerpPosition, SpotPosition, User, UserStatus, + }; + use crate::test_utils::{get_orders, get_positions, get_pyth_price}; + use crate::{create_account_info, PRICE_PRECISION_I64}; + use crate::{create_anchor_account_info, LIQUIDATION_PCT_PRECISION}; + use crate::{test_utils::*, DEFAULT_LIQUIDATION_MARGIN_BUFFER_RATIO}; + + #[test] + pub fn failure_sufficient_collateral() { + let slot = 0_u64; + + let mut oracle_price = get_pyth_price(200, 6); + let oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + oracle_price, + &oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + + let mut market = PerpMarket { + amm: AMM { + base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, + bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, + sqrt_k: 100 * AMM_RESERVE_PRECISION, + peg_multiplier: 100 * PEG_PRECISION, + max_slippage_ratio: 50, + max_fill_reserve_fraction: 100, + order_step_size: 10000000, + quote_asset_amount: -150 * QUOTE_PRECISION_I128, + base_asset_amount_with_amm: BASE_PRECISION_I128, + oracle: oracle_price_key, + historical_oracle_data: HistoricalOracleData::default_price(oracle_price.agg.price), + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + number_of_users_with_base: 1, + status: MarketStatus::Initialized, + liquidator_fee: LIQUIDATION_FEE_PRECISION / 100, + if_liquidation_fee: LIQUIDATION_FEE_PRECISION / 100, + ..PerpMarket::default() + }; + create_anchor_account_info!(market, PerpMarket, market_account_info); + let perp_market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + + let mut spot_market = SpotMarket { + market_index: 0, + oracle_source: OracleSource::QuoteAsset, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 6, + initial_asset_weight: SPOT_WEIGHT_PRECISION, + maintenance_asset_weight: SPOT_WEIGHT_PRECISION, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap: PRICE_PRECISION_I64, + last_oracle_price_twap_5min: PRICE_PRECISION_I64, + ..HistoricalOracleData::default() + }, + ..SpotMarket::default() + }; + create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); + let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); + + let mut user = User { + orders: get_orders(Order { + market_index: 0, + status: OrderStatus::Open, + order_type: OrderType::Limit, + direction: PositionDirection::Long, + base_asset_amount: BASE_PRECISION_U64, + slot: 0, + ..Order::default() + }), + perp_positions: get_positions(PerpPosition { + market_index: 0, + base_asset_amount: BASE_PRECISION_I64, + quote_asset_amount: 100 * QUOTE_PRECISION_I64, + quote_entry_amount: 100 * QUOTE_PRECISION_I64, + quote_break_even_amount: 100 * QUOTE_PRECISION_I64, + open_orders: 1, + open_bids: BASE_PRECISION_I64, + ..PerpPosition::default() + }), + spot_positions: get_spot_positions(SpotPosition { + market_index: 0, + scaled_balance: 1000000000000, + cumulative_deposits: 100000000000, + balance_type: SpotBalanceType::Deposit, + ..SpotPosition::default() + }), + ..User::default() + }; + + let state = State { + liquidation_margin_buffer_ratio: DEFAULT_LIQUIDATION_MARGIN_BUFFER_RATIO, + initial_pct_to_liquidate: LIQUIDATION_PCT_PRECISION as u16, + liquidation_duration: 150, + ..Default::default() + }; + + let result = set_user_status_to_being_liquidated( + &mut user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + slot, + &state, + ); + + assert_eq!(result, Err(ErrorCode::SufficientCollateral)); + } + + #[test] + pub fn failure_from_user_statuses() { + let slot = 0_u64; + + let mut oracle_price = get_pyth_price(100, 6); + let oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + oracle_price, + &oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + + let mut user = User { + orders: [Order::default(); 32], + perp_positions: [PerpPosition::default(); 8], + spot_positions: [SpotPosition::default(); 8], + ..User::default() + }; + + user.add_user_status(UserStatus::Bankrupt); + let state = State { + liquidation_margin_buffer_ratio: DEFAULT_LIQUIDATION_MARGIN_BUFFER_RATIO, + initial_pct_to_liquidate: LIQUIDATION_PCT_PRECISION as u16, + liquidation_duration: 150, + ..Default::default() + }; + + let mut market = PerpMarket { + amm: AMM::default(), + ..PerpMarket::default() + }; + create_anchor_account_info!(market, PerpMarket, market_account_info); + let perp_market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + let mut spot_market = SpotMarket::default(); + create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); + let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); + + let mut result = set_user_status_to_being_liquidated( + &mut user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + slot, + &state, + ); + + assert_eq!(result, Err(ErrorCode::UserBankrupt)); + + user.remove_user_status(UserStatus::Bankrupt); + user.add_user_status(UserStatus::BeingLiquidated); + result = set_user_status_to_being_liquidated( + &mut user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + slot, + &state, + ); + assert_eq!(result, Err(ErrorCode::UserIsBeingLiquidated)); + } + + #[test] + pub fn success() { + let slot = 0_u64; + + let mut oracle_price = get_pyth_price(100, 6); + let oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + oracle_price, + &oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + + let mut market = PerpMarket { + amm: AMM { + base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, + bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, + sqrt_k: 100 * AMM_RESERVE_PRECISION, + peg_multiplier: 100 * PEG_PRECISION, + max_slippage_ratio: 50, + max_fill_reserve_fraction: 100, + order_step_size: 10000000, + quote_asset_amount: -150 * QUOTE_PRECISION_I128, + base_asset_amount_with_amm: BASE_PRECISION_I128, + oracle: oracle_price_key, + historical_oracle_data: HistoricalOracleData::default_price(oracle_price.agg.price), + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + number_of_users_with_base: 1, + status: MarketStatus::Initialized, + liquidator_fee: LIQUIDATION_FEE_PRECISION / 100, + if_liquidation_fee: LIQUIDATION_FEE_PRECISION / 100, + ..PerpMarket::default() + }; + create_anchor_account_info!(market, PerpMarket, market_account_info); + let perp_market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + + let mut spot_market = SpotMarket { + market_index: 0, + oracle_source: OracleSource::QuoteAsset, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 6, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap: PRICE_PRECISION_I64, + last_oracle_price_twap_5min: PRICE_PRECISION_I64, + ..HistoricalOracleData::default() + }, + ..SpotMarket::default() + }; + create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); + let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); + + let mut user = User { + orders: get_orders(Order { + market_index: 0, + status: OrderStatus::Open, + order_type: OrderType::Limit, + direction: PositionDirection::Long, + base_asset_amount: BASE_PRECISION_U64, + slot: 0, + ..Order::default() + }), + perp_positions: get_positions(PerpPosition { + market_index: 0, + base_asset_amount: BASE_PRECISION_I64, + quote_asset_amount: -150 * QUOTE_PRECISION_I64, + quote_entry_amount: -150 * QUOTE_PRECISION_I64, + quote_break_even_amount: -150 * QUOTE_PRECISION_I64, + open_orders: 1, + open_bids: BASE_PRECISION_I64, + ..PerpPosition::default() + }), + ..User::default() + }; + + let state = State { + liquidation_margin_buffer_ratio: DEFAULT_LIQUIDATION_MARGIN_BUFFER_RATIO, + initial_pct_to_liquidate: LIQUIDATION_PCT_PRECISION as u16, + liquidation_duration: 150, + ..Default::default() + }; + + let result = set_user_status_to_being_liquidated( + &mut user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + slot, + &state, + ); + + assert_eq!(user.status, UserStatus::BeingLiquidated as u8); + assert_eq!(result, Ok(())); + } +} From f3536edb8092048285cd1250f08f213ec297650a Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Fri, 9 Aug 2024 13:44:47 -0700 Subject: [PATCH 03/11] add get_liquidation_fee tests --- programs/drift/src/controller/liquidation.rs | 5 ++-- programs/drift/src/math/constants.rs | 2 ++ programs/drift/src/math/liquidation.rs | 27 +++++++++++++++++++- programs/drift/src/math/liquidation/tests.rs | 27 ++++++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 3a68c0fc5..d52080bc7 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -33,7 +33,7 @@ use crate::math::liquidation::{ calculate_liability_transfer_implied_by_asset_amount, calculate_liability_transfer_to_cover_margin_shortage, calculate_liquidation_multiplier, calculate_max_pct_to_liquidate, calculate_perp_if_fee, calculate_spot_if_fee, - get_liquidation_order_params, validate_transfer_satisfies_limit_price, + get_liquidation_fee, get_liquidation_order_params, validate_transfer_satisfies_limit_price, LiquidationMultiplierType, }; use crate::math::margin::{ @@ -965,13 +965,14 @@ pub fn liquidate_perp_with_fill( )?; let existing_direction = user.perp_positions[position_index].get_direction(); + let liquidator_fee_adjusted = get_liquidation_fee(liquidator_fee, user.last_active_slot, slot)?; let order_params = get_liquidation_order_params( market_index, existing_direction, base_asset_amount, oracle_price, - liquidator_fee, + liquidator_fee_adjusted, )?; let order_id = user.next_order_id; diff --git a/programs/drift/src/math/constants.rs b/programs/drift/src/math/constants.rs index 0e79b3da0..7a3f4dfac 100644 --- a/programs/drift/src/math/constants.rs +++ b/programs/drift/src/math/constants.rs @@ -174,6 +174,8 @@ pub const MAX_PEG_BPS_DECREASE: u128 = TEN_BPS as u128; // 10 bps decrease pub const MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT: u64 = 10 * PERCENTAGE_PRECISION_U64; // 1000% APR pub const MAX_CONCENTRATION_COEFFICIENT: u128 = 1_414_200; +pub const LIQUIDATION_FEE_INCREASE_PER_SLOT: u32 = LIQUIDATION_FEE_PRECISION / 10_000; // .01 bps per slot +pub const MAX_LIQUIDATION_FEE: u32 = 10 * LIQUIDATION_FEE_PRECISION; // 10% pub const MAX_LIQUIDATION_SLIPPAGE: i128 = 10_000; // expo = -2 pub const MAX_LIQUIDATION_SLIPPAGE_U128: u128 = 10_000; // expo = -2 pub const MAX_MARK_TWAP_DIVERGENCE: u128 = 500_000; // expo = -3 diff --git a/programs/drift/src/math/liquidation.rs b/programs/drift/src/math/liquidation.rs index 26a15572f..3e9472234 100644 --- a/programs/drift/src/math/liquidation.rs +++ b/programs/drift/src/math/liquidation.rs @@ -1,3 +1,5 @@ +use std::u32; + use crate::error::{DriftResult, ErrorCode}; use crate::math::casting::Cast; use crate::math::constants::{ @@ -18,9 +20,14 @@ use crate::state::perp_market_map::PerpMarketMap; use crate::state::spot_market::{SpotBalanceType, SpotMarket}; use crate::state::spot_market_map::SpotMarketMap; use crate::state::user::{OrderType, User}; -use crate::{validate, MarketType, OrderParams, PositionDirection, BASE_PRECISION}; +use crate::{ + validate, MarketType, OrderParams, PositionDirection, BASE_PRECISION, + LIQUIDATION_FEE_INCREASE_PER_SLOT, MAX_LIQUIDATION_FEE, +}; use solana_program::msg; +pub const LIQUIDATION_FEE_ADJUST_GRACE_PERIOD_SLOTS: u64 = 1_500; // ~10 minutes + #[cfg(test)] mod tests; @@ -486,3 +493,21 @@ pub fn get_liquidation_order_params( Ok(order_params) } + +pub fn get_liquidation_fee( + base_liquidation_fee: u32, + last_active_user_slot: u64, + current_slot: u64, +) -> DriftResult { + let slots_elapsed = current_slot.safe_sub(last_active_user_slot)?; + if slots_elapsed < LIQUIDATION_FEE_ADJUST_GRACE_PERIOD_SLOTS { + return Ok(base_liquidation_fee); + } + + let liquidation_fee = base_liquidation_fee.safe_add( + slots_elapsed + .safe_mul(LIQUIDATION_FEE_INCREASE_PER_SLOT.cast::()?)? + .cast::()?, + )?; + Ok(liquidation_fee.min(MAX_LIQUIDATION_FEE)) +} diff --git a/programs/drift/src/math/liquidation/tests.rs b/programs/drift/src/math/liquidation/tests.rs index 4c5fb40ca..ddbb6061e 100644 --- a/programs/drift/src/math/liquidation/tests.rs +++ b/programs/drift/src/math/liquidation/tests.rs @@ -826,3 +826,30 @@ mod calculate_max_pct_to_liquidate { assert_eq!(pct, LIQUIDATION_PCT_PRECISION); } } + +mod get_liquidation_fee { + use crate::math::liquidation::get_liquidation_fee; + use crate::{LIQUIDATION_FEE_PRECISION, MAX_LIQUIDATION_FEE}; + + #[test] + fn test() { + let user_slot: u64 = 0; + let base_liq_fee: u32 = 2 * LIQUIDATION_FEE_PRECISION; + + // Huge slot difference + let curr_slot: u64 = 100000; + let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); + assert_eq!(fee, MAX_LIQUIDATION_FEE); + + // Small slot difference within grace period + let curr_slot: u64 = 10; + let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); + assert_eq!(fee, base_liq_fee); + + // Successful increase + let target_liq_fee: u32 = 3 * LIQUIDATION_FEE_PRECISION; + let curr_slot: u64 = 10000; + let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); + assert_eq!(fee, target_liq_fee); + } +} From 51c5bcc09c6b69a80eab3ea8610dfcfdae455e5e Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Fri, 9 Aug 2024 14:07:52 -0700 Subject: [PATCH 04/11] adjust liquiation fee to use the right params --- programs/drift/src/math/constants.rs | 4 ++-- programs/drift/src/math/liquidation/tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/drift/src/math/constants.rs b/programs/drift/src/math/constants.rs index 7a3f4dfac..027aac981 100644 --- a/programs/drift/src/math/constants.rs +++ b/programs/drift/src/math/constants.rs @@ -174,8 +174,8 @@ pub const MAX_PEG_BPS_DECREASE: u128 = TEN_BPS as u128; // 10 bps decrease pub const MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT: u64 = 10 * PERCENTAGE_PRECISION_U64; // 1000% APR pub const MAX_CONCENTRATION_COEFFICIENT: u128 = 1_414_200; -pub const LIQUIDATION_FEE_INCREASE_PER_SLOT: u32 = LIQUIDATION_FEE_PRECISION / 10_000; // .01 bps per slot -pub const MAX_LIQUIDATION_FEE: u32 = 10 * LIQUIDATION_FEE_PRECISION; // 10% +pub const LIQUIDATION_FEE_INCREASE_PER_SLOT: u32 = LIQUIDATION_FEE_PRECISION / 1_000_000; // .01 bps per slot +pub const MAX_LIQUIDATION_FEE: u32 = 10 * LIQUIDATION_FEE_PRECISION / 100; // 10% pub const MAX_LIQUIDATION_SLIPPAGE: i128 = 10_000; // expo = -2 pub const MAX_LIQUIDATION_SLIPPAGE_U128: u128 = 10_000; // expo = -2 pub const MAX_MARK_TWAP_DIVERGENCE: u128 = 500_000; // expo = -3 diff --git a/programs/drift/src/math/liquidation/tests.rs b/programs/drift/src/math/liquidation/tests.rs index ddbb6061e..674e624da 100644 --- a/programs/drift/src/math/liquidation/tests.rs +++ b/programs/drift/src/math/liquidation/tests.rs @@ -834,7 +834,7 @@ mod get_liquidation_fee { #[test] fn test() { let user_slot: u64 = 0; - let base_liq_fee: u32 = 2 * LIQUIDATION_FEE_PRECISION; + let base_liq_fee: u32 = 2 * LIQUIDATION_FEE_PRECISION / 100; // Huge slot difference let curr_slot: u64 = 100000; @@ -847,7 +847,7 @@ mod get_liquidation_fee { assert_eq!(fee, base_liq_fee); // Successful increase - let target_liq_fee: u32 = 3 * LIQUIDATION_FEE_PRECISION; + let target_liq_fee: u32 = 3 * LIQUIDATION_FEE_PRECISION / 100; let curr_slot: u64 = 10000; let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); assert_eq!(fee, target_liq_fee); From 6679a64303a27efc79f21429d8e03e04c9681769 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Mon, 12 Aug 2024 11:08:49 -0700 Subject: [PATCH 05/11] bankrun test --- programs/drift/src/controller/liquidation.rs | 1 + programs/drift/src/instructions/keeper.rs | 1 - sdk/src/driftClient.ts | 33 ++++++++++++++++++++ sdk/src/idl/drift.json | 16 ++++++++++ tests/liquidatePerp.ts | 14 ++++++++- 5 files changed, 63 insertions(+), 2 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index d52080bc7..fbaec11b6 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -3043,6 +3043,7 @@ pub fn set_user_status_to_being_liquidated( )?; if !user.is_being_liquidated() && margin_calculation.meets_margin_requirement() { + msg!("margin calculation: {:?}", margin_calculation); return Err(ErrorCode::SufficientCollateral); } else { user.enter_liquidation(slot)?; diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index c7b226cf0..fce7542c4 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -1949,7 +1949,6 @@ pub struct LiquidatePerpPnlForDeposit<'info> { #[derive(Accounts)] pub struct SetUserStatusToBeingLiquidated<'info> { pub state: Box>, - pub authority: Signer<'info>, #[account(mut)] pub user: AccountLoader<'info, User>, } diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 237bb0283..15ffd9d81 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -5945,6 +5945,39 @@ export class DriftClient { ); } + public async getSetUserStatusToBeingLiquidatedIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount + ): Promise { + const remainingAccounts = this.getRemainingAccounts({ + userAccounts: [userAccount], + }); + return await this.program.instruction.setUserStatusToBeingLiquidated({ + accounts: { + state: await this.getStatePublicKey(), + user: userAccountPublicKey, + }, + remainingAccounts, + }); + } + + public async setUserStatusToBeingLiquidated( + userAccountPublicKey: PublicKey, + userAccount: UserAccount + ): Promise { + const { txSig } = await this.sendTransaction( + await this.buildTransaction( + await this.getSetUserStatusToBeingLiquidatedIx( + userAccountPublicKey, + userAccount + ) + ), + [], + this.opts + ); + return txSig; + } + public async liquidatePerp( userAccountPublicKey: PublicKey, userAccount: UserAccount, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 5374457d8..9a514f916 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1881,6 +1881,22 @@ } ] }, + { + "name": "setUserStatusToBeingLiquidated", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "user", + "isMut": true, + "isSigner": false + } + ], + "args": [] + }, { "name": "resolvePerpPnlDeficit", "accounts": [ diff --git a/tests/liquidatePerp.ts b/tests/liquidatePerp.ts index 9c14e2ea9..007012e0c 100644 --- a/tests/liquidatePerp.ts +++ b/tests/liquidatePerp.ts @@ -29,7 +29,12 @@ import { setFeedPriceNoProgram, sleep, } from './testHelpers'; -import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; +import { + convertToNumber, + MARGIN_PRECISION, + PERCENTAGE_PRECISION, + UserStatus, +} from '../sdk'; import { startAnchor } from 'solana-bankrun'; import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; @@ -320,6 +325,13 @@ describe('liquidate perp (no open orders)', () => { await setFeedPriceNoProgram(bankrunContextWrapper, 0.1, oracle); + const txSig1 = await liquidatorDriftClient.setUserStatusToBeingLiquidated( + await driftClient.getUserAccountPublicKey(), + driftClient.getUserAccount() + ); + console.log('setUserStatusToBeingLiquidated txSig:', txSig1); + assert(driftClient.getUserAccount().status === UserStatus.BEING_LIQUIDATED); + const txSig = await liquidatorDriftClient.liquidatePerp( await driftClient.getUserAccountPublicKey(), driftClient.getUserAccount(), From a1ed60b5dc665e49374a10c7a8530ddaf775f6ff Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Mon, 12 Aug 2024 11:11:05 -0700 Subject: [PATCH 06/11] remove unused vars --- tests/liquidatePerp.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/liquidatePerp.ts b/tests/liquidatePerp.ts index 007012e0c..11d6b1dc0 100644 --- a/tests/liquidatePerp.ts +++ b/tests/liquidatePerp.ts @@ -30,8 +30,6 @@ import { sleep, } from './testHelpers'; import { - convertToNumber, - MARGIN_PRECISION, PERCENTAGE_PRECISION, UserStatus, } from '../sdk'; From 400fc35d5a483d181887039ec12fc97b6b64627c Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Mon, 12 Aug 2024 13:23:13 -0700 Subject: [PATCH 07/11] max liq fee per market --- programs/drift/src/controller/liquidation.rs | 20 ++++++++++++++++++-- programs/drift/src/math/constants.rs | 1 - programs/drift/src/math/liquidation.rs | 5 +++-- programs/drift/src/math/liquidation/tests.rs | 11 ++++++----- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index fbaec11b6..ec12b339d 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -68,12 +68,14 @@ use crate::state::state::State; use crate::state::traits::Size; use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats}; use crate::state::user_map::{UserMap, UserStatsMap}; -use crate::validate; use crate::{get_then_update_id, load_mut}; +use crate::{validate, LIQUIDATION_FEE_PRECISION, MARGIN_PRECISION}; #[cfg(test)] mod tests; +const MAX_LIQUIDATION_MULTIPLIER: u32 = 3; + pub fn liquidate_perp( market_index: u16, liquidator_max_base_asset_amount: u64, @@ -965,7 +967,21 @@ pub fn liquidate_perp_with_fill( )?; let existing_direction = user.perp_positions[position_index].get_direction(); - let liquidator_fee_adjusted = get_liquidation_fee(liquidator_fee, user.last_active_slot, slot)?; + let max_liquidation_fee = + (perp_market_map.get_ref(&market_index)?.liquidator_fee * MAX_LIQUIDATION_MULTIPLIER).min( + perp_market_map + .get_ref(&market_index)? + .margin_ratio_maintenance + * LIQUIDATION_FEE_PRECISION + / MARGIN_PRECISION, + ); + + let liquidator_fee_adjusted = get_liquidation_fee( + liquidator_fee, + max_liquidation_fee, + user.last_active_slot, + slot, + )?; let order_params = get_liquidation_order_params( market_index, diff --git a/programs/drift/src/math/constants.rs b/programs/drift/src/math/constants.rs index 027aac981..1c652b3b2 100644 --- a/programs/drift/src/math/constants.rs +++ b/programs/drift/src/math/constants.rs @@ -175,7 +175,6 @@ pub const MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT: u64 = 10 * PERCENT pub const MAX_CONCENTRATION_COEFFICIENT: u128 = 1_414_200; pub const LIQUIDATION_FEE_INCREASE_PER_SLOT: u32 = LIQUIDATION_FEE_PRECISION / 1_000_000; // .01 bps per slot -pub const MAX_LIQUIDATION_FEE: u32 = 10 * LIQUIDATION_FEE_PRECISION / 100; // 10% pub const MAX_LIQUIDATION_SLIPPAGE: i128 = 10_000; // expo = -2 pub const MAX_LIQUIDATION_SLIPPAGE_U128: u128 = 10_000; // expo = -2 pub const MAX_MARK_TWAP_DIVERGENCE: u128 = 500_000; // expo = -3 diff --git a/programs/drift/src/math/liquidation.rs b/programs/drift/src/math/liquidation.rs index 3e9472234..9c4c141e2 100644 --- a/programs/drift/src/math/liquidation.rs +++ b/programs/drift/src/math/liquidation.rs @@ -22,7 +22,7 @@ use crate::state::spot_market_map::SpotMarketMap; use crate::state::user::{OrderType, User}; use crate::{ validate, MarketType, OrderParams, PositionDirection, BASE_PRECISION, - LIQUIDATION_FEE_INCREASE_PER_SLOT, MAX_LIQUIDATION_FEE, + LIQUIDATION_FEE_INCREASE_PER_SLOT, }; use solana_program::msg; @@ -496,6 +496,7 @@ pub fn get_liquidation_order_params( pub fn get_liquidation_fee( base_liquidation_fee: u32, + max_liquidation_fee: u32, last_active_user_slot: u64, current_slot: u64, ) -> DriftResult { @@ -509,5 +510,5 @@ pub fn get_liquidation_fee( .safe_mul(LIQUIDATION_FEE_INCREASE_PER_SLOT.cast::()?)? .cast::()?, )?; - Ok(liquidation_fee.min(MAX_LIQUIDATION_FEE)) + Ok(liquidation_fee.min(max_liquidation_fee)) } diff --git a/programs/drift/src/math/liquidation/tests.rs b/programs/drift/src/math/liquidation/tests.rs index 674e624da..2ca9f9c00 100644 --- a/programs/drift/src/math/liquidation/tests.rs +++ b/programs/drift/src/math/liquidation/tests.rs @@ -829,27 +829,28 @@ mod calculate_max_pct_to_liquidate { mod get_liquidation_fee { use crate::math::liquidation::get_liquidation_fee; - use crate::{LIQUIDATION_FEE_PRECISION, MAX_LIQUIDATION_FEE}; + use crate::LIQUIDATION_FEE_PRECISION; #[test] fn test() { let user_slot: u64 = 0; let base_liq_fee: u32 = 2 * LIQUIDATION_FEE_PRECISION / 100; + let max_liq_fee: u32 = 5 * LIQUIDATION_FEE_PRECISION / 100; // Huge slot difference let curr_slot: u64 = 100000; - let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); - assert_eq!(fee, MAX_LIQUIDATION_FEE); + let fee = get_liquidation_fee(base_liq_fee, max_liq_fee, user_slot, curr_slot).unwrap(); + assert_eq!(fee, max_liq_fee); // Small slot difference within grace period let curr_slot: u64 = 10; - let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); + let fee = get_liquidation_fee(base_liq_fee, max_liq_fee, user_slot, curr_slot).unwrap(); assert_eq!(fee, base_liq_fee); // Successful increase let target_liq_fee: u32 = 3 * LIQUIDATION_FEE_PRECISION / 100; let curr_slot: u64 = 10000; - let fee = get_liquidation_fee(base_liq_fee, user_slot, curr_slot).unwrap(); + let fee = get_liquidation_fee(base_liq_fee, max_liq_fee, user_slot, curr_slot).unwrap(); assert_eq!(fee, target_liq_fee); } } From 2dd82b5c0f4bdec0e517339c3bcee1b13af3d81d Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Mon, 12 Aug 2024 13:35:49 -0700 Subject: [PATCH 08/11] prettify --- tests/liquidatePerp.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/liquidatePerp.ts b/tests/liquidatePerp.ts index 11d6b1dc0..edb341cf6 100644 --- a/tests/liquidatePerp.ts +++ b/tests/liquidatePerp.ts @@ -29,10 +29,7 @@ import { setFeedPriceNoProgram, sleep, } from './testHelpers'; -import { - PERCENTAGE_PRECISION, - UserStatus, -} from '../sdk'; +import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; From bd3880421a525bfa1bc0f9974753e26f744555ec Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Thu, 15 Aug 2024 11:58:40 -0700 Subject: [PATCH 09/11] address comments --- programs/drift/src/controller/liquidation.rs | 15 ++++----------- programs/drift/src/instructions/keeper.rs | 1 + programs/drift/src/math/constants.rs | 1 + programs/drift/src/math/liquidation.rs | 7 ++++--- programs/drift/src/state/perp_market.rs | 16 +++++++++++++--- sdk/src/idl/drift.json | 4 ++-- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 934d6ff99..955d50d70 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -68,14 +68,12 @@ use crate::state::state::State; use crate::state::traits::Size; use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats}; use crate::state::user_map::{UserMap, UserStatsMap}; +use crate::validate; use crate::{get_then_update_id, load_mut}; -use crate::{validate, LIQUIDATION_FEE_PRECISION, MARGIN_PRECISION}; #[cfg(test)] mod tests; -const MAX_LIQUIDATION_MULTIPLIER: u32 = 3; - pub fn liquidate_perp( market_index: u16, liquidator_max_base_asset_amount: u64, @@ -964,14 +962,9 @@ pub fn liquidate_perp_with_fill( )?; let existing_direction = user.perp_positions[position_index].get_direction(); - let max_liquidation_fee = - (perp_market_map.get_ref(&market_index)?.liquidator_fee * MAX_LIQUIDATION_MULTIPLIER).min( - perp_market_map - .get_ref(&market_index)? - .margin_ratio_maintenance - * LIQUIDATION_FEE_PRECISION - / MARGIN_PRECISION, - ); + let max_liquidation_fee = perp_market_map + .get_ref(&market_index)? + .get_max_liquidation_fee()?; let liquidator_fee_adjusted = get_liquidation_fee( liquidator_fee, diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 531523bf4..b8aaff6cb 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -1951,6 +1951,7 @@ pub struct SetUserStatusToBeingLiquidated<'info> { pub state: Box>, #[account(mut)] pub user: AccountLoader<'info, User>, + pub authority: Signer<'info>, } #[derive(Accounts)] diff --git a/programs/drift/src/math/constants.rs b/programs/drift/src/math/constants.rs index 3804df92d..177250eae 100644 --- a/programs/drift/src/math/constants.rs +++ b/programs/drift/src/math/constants.rs @@ -174,6 +174,7 @@ pub const MAX_PEG_BPS_DECREASE: u128 = TEN_BPS as u128; // 10 bps decrease pub const MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT: u64 = 10 * PERCENTAGE_PRECISION_U64; // 1000% APR pub const MAX_CONCENTRATION_COEFFICIENT: u128 = 1_414_200; +pub const MAX_LIQUIDATION_MULTIPLIER: u32 = 3; pub const LIQUIDATION_FEE_INCREASE_PER_SLOT: u32 = LIQUIDATION_FEE_PRECISION / 1_000_000; // .01 bps per slot pub const MAX_LIQUIDATION_SLIPPAGE: i128 = 10_000; // expo = -2 pub const MAX_LIQUIDATION_SLIPPAGE_U128: u128 = 10_000; // expo = -2 diff --git a/programs/drift/src/math/liquidation.rs b/programs/drift/src/math/liquidation.rs index 9c4c141e2..5682b5dca 100644 --- a/programs/drift/src/math/liquidation.rs +++ b/programs/drift/src/math/liquidation.rs @@ -505,10 +505,11 @@ pub fn get_liquidation_fee( return Ok(base_liquidation_fee); } - let liquidation_fee = base_liquidation_fee.safe_add( + let liquidation_fee = base_liquidation_fee.saturating_add( slots_elapsed .safe_mul(LIQUIDATION_FEE_INCREASE_PER_SLOT.cast::()?)? - .cast::()?, - )?; + .cast::() + .unwrap_or(u32::MAX), + ); Ok(liquidation_fee.min(max_liquidation_fee)) } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 055543042..b230c7386 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -13,9 +13,10 @@ use crate::math::constants::{ use crate::math::constants::{ AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_U128, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, - LP_FEE_SLICE_DENOMINATOR, LP_FEE_SLICE_NUMERATOR, MARGIN_PRECISION_U128, PEG_PRECISION, - PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, - PERCENTAGE_PRECISION_U64, PRICE_PRECISION, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, + LIQUIDATION_FEE_PRECISION, LP_FEE_SLICE_DENOMINATOR, LP_FEE_SLICE_NUMERATOR, MARGIN_PRECISION, + MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION, PERCENTAGE_PRECISION, + PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, + SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, }; use crate::math::helpers::get_proportion_i128; use crate::math::margin::{ @@ -418,6 +419,15 @@ impl PerpMarket { Ok(margin_ratio) } + pub fn get_max_liquidation_fee(&self) -> DriftResult { + let max_liquidation_fee = (self.liquidator_fee.safe_mul(MAX_LIQUIDATION_MULTIPLIER)?).min( + self.margin_ratio_maintenance + .safe_mul(LIQUIDATION_FEE_PRECISION)? + .safe_div(MARGIN_PRECISION)?, + ); + Ok(max_liquidation_fee) + } + pub fn get_unrealized_asset_weight( &self, unrealized_pnl: i128, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 7681022c3..1c93828c8 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -2275,7 +2275,7 @@ }, { "name": "spotMarket", - "isMut": false, + "isMut": true, "isSigner": false }, { @@ -2311,7 +2311,7 @@ }, { "name": "spotMarket", - "isMut": false, + "isMut": true, "isSigner": false }, { From d0337ff099ac6c6778a2cd13c6cae57fd7d31758 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Thu, 15 Aug 2024 12:06:55 -0700 Subject: [PATCH 10/11] drift client changes for CI --- sdk/src/driftClient.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 5a3eff9a6..5fd178dca 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -5957,6 +5957,7 @@ export class DriftClient { accounts: { state: await this.getStatePublicKey(), user: userAccountPublicKey, + authority: this.wallet.publicKey, }, remainingAccounts, }); From e7074ec3fbbf99431e377e56658fc55e73f85a9a Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 21 Aug 2024 13:49:10 -0400 Subject: [PATCH 11/11] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac48f4f2e..886f8a15c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: dynamic liquidation fee for liq_perp_with_fill ([#1185](https://github.com/drift-labs/protocol-v2/pull/1185)) - program: calculate_accumulated_interest return early based on ts ([#1192](https://github.com/drift-labs/protocol-v2/pull/1192)) - program: add logging to pyth pull updates ([#1189](https://github.com/drift-labs/protocol-v2/pull/1189))