diff --git a/programs/mango-v4/src/error.rs b/programs/mango-v4/src/error.rs index cd212b740..7ac7ccacd 100644 --- a/programs/mango-v4/src/error.rs +++ b/programs/mango-v4/src/error.rs @@ -69,6 +69,8 @@ pub enum MangoError { TokenInReduceOnlyMode, #[msg("market is in reduce only mode")] MarketInReduceOnlyMode, + #[msg("group is halted")] + GroupIsHalted, #[msg("the perp position has non-zero base lots")] PerpHasBaseLots, #[msg("there are open or unsettled serum3 orders")] diff --git a/programs/mango-v4/src/instructions/account_close.rs b/programs/mango-v4/src/instructions/account_close.rs index 017969a76..0d59565bb 100644 --- a/programs/mango-v4/src/instructions/account_close.rs +++ b/programs/mango-v4/src/instructions/account_close.rs @@ -6,6 +6,9 @@ use crate::state::*; #[derive(Accounts)] pub struct AccountClose<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/account_create.rs b/programs/mango-v4/src/instructions/account_create.rs index d52e0cf27..4c7234f06 100644 --- a/programs/mango-v4/src/instructions/account_create.rs +++ b/programs/mango-v4/src/instructions/account_create.rs @@ -7,6 +7,9 @@ use crate::util::fill_from_str; #[derive(Accounts)] #[instruction(account_num: u32, token_count: u8, serum3_count: u8, perp_count: u8, perp_oo_count: u8)] pub struct AccountCreate<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/account_edit.rs b/programs/mango-v4/src/instructions/account_edit.rs index 70621c7dc..70530b7e0 100644 --- a/programs/mango-v4/src/instructions/account_edit.rs +++ b/programs/mango-v4/src/instructions/account_edit.rs @@ -6,6 +6,9 @@ use crate::util::fill_from_str; #[derive(Accounts)] pub struct AccountEdit<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/account_expand.rs b/programs/mango-v4/src/instructions/account_expand.rs index fe655228e..bff654074 100644 --- a/programs/mango-v4/src/instructions/account_expand.rs +++ b/programs/mango-v4/src/instructions/account_expand.rs @@ -1,10 +1,14 @@ use anchor_lang::prelude::*; +use crate::error::MangoError; use crate::state::*; use crate::util::checked_math as cm; #[derive(Accounts)] pub struct AccountExpand<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/alt_extend.rs b/programs/mango-v4/src/instructions/alt_extend.rs index 12f200fd1..1c01653ef 100644 --- a/programs/mango-v4/src/instructions/alt_extend.rs +++ b/programs/mango-v4/src/instructions/alt_extend.rs @@ -2,12 +2,14 @@ use anchor_lang::prelude::*; use solana_address_lookup_table_program as solana_alt; use crate::address_lookup_table_program; +use crate::error::MangoError; use crate::state::*; #[derive(Accounts)] pub struct AltExtend<'info> { #[account( has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/alt_set.rs b/programs/mango-v4/src/instructions/alt_set.rs index 3284c09fd..4f1cdcd14 100644 --- a/programs/mango-v4/src/instructions/alt_set.rs +++ b/programs/mango-v4/src/instructions/alt_set.rs @@ -9,6 +9,7 @@ pub struct AltSet<'info> { #[account( mut, has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/group_close.rs b/programs/mango-v4/src/instructions/group_close.rs index a97b347b4..cd9447a97 100644 --- a/programs/mango-v4/src/instructions/group_close.rs +++ b/programs/mango-v4/src/instructions/group_close.rs @@ -1,4 +1,4 @@ -use crate::state::*; +use crate::{error::MangoError, state::*}; use anchor_lang::prelude::*; use anchor_spl::token::{self, CloseAccount, Token, TokenAccount}; @@ -6,9 +6,10 @@ use anchor_spl::token::{self, CloseAccount, Token, TokenAccount}; pub struct GroupClose<'info> { #[account( mut, - constraint = group.load()?.is_testing(), has_one = admin, has_one = insurance_vault, + constraint = group.load()?.is_testing(), + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, close = sol_destination )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/group_edit.rs b/programs/mango-v4/src/instructions/group_edit.rs index 425b46f4a..dd7bf4183 100644 --- a/programs/mango-v4/src/instructions/group_edit.rs +++ b/programs/mango-v4/src/instructions/group_edit.rs @@ -18,6 +18,7 @@ pub fn group_edit( ctx: Context, admin_opt: Option, fast_listing_admin_opt: Option, + security_admin_opt: Option, testing_opt: Option, version_opt: Option, ) -> Result<()> { @@ -31,6 +32,10 @@ pub fn group_edit( group.fast_listing_admin = fast_listing_admin; } + if let Some(security_admin) = security_admin_opt { + group.security_admin = security_admin; + } + if let Some(testing) = testing_opt { group.testing = testing; } diff --git a/programs/mango-v4/src/instructions/group_toggle_halt.rs b/programs/mango-v4/src/instructions/group_toggle_halt.rs new file mode 100644 index 000000000..b8060e295 --- /dev/null +++ b/programs/mango-v4/src/instructions/group_toggle_halt.rs @@ -0,0 +1,19 @@ +use anchor_lang::prelude::*; + +use crate::state::*; + +#[derive(Accounts)] +pub struct GroupToggleHalt<'info> { + #[account( + mut, + constraint = group.load()?.admin == admin.key() || group.load()?.security_admin == admin.key(), + )] + pub group: AccountLoader<'info, Group>, + pub admin: Signer<'info>, +} + +pub fn group_toggle_halt(ctx: Context, halted: bool) -> Result<()> { + let mut group = ctx.accounts.group.load_mut()?; + group.halted = if halted { 1 } else { 0 }; + Ok(()) +} diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index e339c99f7..521e64aed 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -10,6 +10,7 @@ pub use flash_loan::*; pub use group_close::*; pub use group_create::*; pub use group_edit::*; +pub use group_toggle_halt::*; pub use health_region::*; pub use perp_cancel_all_orders::*; pub use perp_cancel_all_orders_by_side::*; @@ -63,6 +64,7 @@ mod flash_loan; mod group_close; mod group_create; mod group_edit; +mod group_toggle_halt; mod health_region; mod perp_cancel_all_orders; mod perp_cancel_all_orders_by_side; diff --git a/programs/mango-v4/src/instructions/perp_cancel_all_orders.rs b/programs/mango-v4/src/instructions/perp_cancel_all_orders.rs index 3145c79a3..94dddb705 100644 --- a/programs/mango-v4/src/instructions/perp_cancel_all_orders.rs +++ b/programs/mango-v4/src/instructions/perp_cancel_all_orders.rs @@ -5,6 +5,9 @@ use crate::state::{BookSide, Group, MangoAccountFixed, MangoAccountLoader, Order #[derive(Accounts)] pub struct PerpCancelAllOrders<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/perp_cancel_all_orders_by_side.rs b/programs/mango-v4/src/instructions/perp_cancel_all_orders_by_side.rs index 75d1befb7..a3f702f5a 100644 --- a/programs/mango-v4/src/instructions/perp_cancel_all_orders_by_side.rs +++ b/programs/mango-v4/src/instructions/perp_cancel_all_orders_by_side.rs @@ -7,6 +7,9 @@ use crate::state::{ #[derive(Accounts)] pub struct PerpCancelAllOrdersBySide<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/perp_cancel_order.rs b/programs/mango-v4/src/instructions/perp_cancel_order.rs index e688c7ec2..ab4f99b59 100644 --- a/programs/mango-v4/src/instructions/perp_cancel_order.rs +++ b/programs/mango-v4/src/instructions/perp_cancel_order.rs @@ -5,6 +5,9 @@ use crate::state::{BookSide, Group, MangoAccountFixed, MangoAccountLoader, Order #[derive(Accounts)] pub struct PerpCancelOrder<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/perp_cancel_order_by_client_order_id.rs b/programs/mango-v4/src/instructions/perp_cancel_order_by_client_order_id.rs index f9baaae0e..27e55eb22 100644 --- a/programs/mango-v4/src/instructions/perp_cancel_order_by_client_order_id.rs +++ b/programs/mango-v4/src/instructions/perp_cancel_order_by_client_order_id.rs @@ -5,6 +5,9 @@ use crate::state::{BookSide, Group, MangoAccountFixed, MangoAccountLoader, Order #[derive(Accounts)] pub struct PerpCancelOrderByClientOrderId<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/perp_close_market.rs b/programs/mango-v4/src/instructions/perp_close_market.rs index 90c3bcdae..35aaeee81 100644 --- a/programs/mango-v4/src/instructions/perp_close_market.rs +++ b/programs/mango-v4/src/instructions/perp_close_market.rs @@ -1,12 +1,13 @@ use anchor_lang::prelude::*; use anchor_spl::token::Token; -use crate::state::*; +use crate::{error::MangoError, state::*}; #[derive(Accounts)] pub struct PerpCloseMarket<'info> { #[account( constraint = group.load()?.is_testing(), + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, has_one = admin, )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/perp_consume_events.rs b/programs/mango-v4/src/instructions/perp_consume_events.rs index eb66374a1..5bc4c766f 100644 --- a/programs/mango-v4/src/instructions/perp_consume_events.rs +++ b/programs/mango-v4/src/instructions/perp_consume_events.rs @@ -9,6 +9,9 @@ use crate::logs::{emit_perp_balances, FillLog}; #[derive(Accounts)] pub struct PerpConsumeEvents<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/perp_create_market.rs b/programs/mango-v4/src/instructions/perp_create_market.rs index 7573c403e..96d2b62ee 100644 --- a/programs/mango-v4/src/instructions/perp_create_market.rs +++ b/programs/mango-v4/src/instructions/perp_create_market.rs @@ -13,7 +13,8 @@ use crate::logs::PerpMarketMetaDataLog; pub struct PerpCreateMarket<'info> { #[account( has_one = admin, - constraint = group.load()?.perps_supported() + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, + constraint = group.load()?.perps_supported(), )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/perp_deactivate_position.rs b/programs/mango-v4/src/instructions/perp_deactivate_position.rs index 6c6668c20..d30d0f66c 100644 --- a/programs/mango-v4/src/instructions/perp_deactivate_position.rs +++ b/programs/mango-v4/src/instructions/perp_deactivate_position.rs @@ -5,6 +5,9 @@ use crate::state::*; #[derive(Accounts)] pub struct PerpDeactivatePosition<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/perp_edit_market.rs b/programs/mango-v4/src/instructions/perp_edit_market.rs index 3dbafc981..49c8d042e 100644 --- a/programs/mango-v4/src/instructions/perp_edit_market.rs +++ b/programs/mango-v4/src/instructions/perp_edit_market.rs @@ -7,7 +7,7 @@ use crate::logs::PerpMarketMetaDataLog; #[derive(Accounts)] pub struct PerpEditMarket<'info> { #[account( - has_one = admin, + has_one = admin )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/perp_liq_bankruptcy.rs b/programs/mango-v4/src/instructions/perp_liq_bankruptcy.rs new file mode 100644 index 000000000..f1524f42e --- /dev/null +++ b/programs/mango-v4/src/instructions/perp_liq_bankruptcy.rs @@ -0,0 +1,239 @@ +use anchor_lang::prelude::*; +use anchor_spl::token; +use anchor_spl::token::Token; +use anchor_spl::token::TokenAccount; +use fixed::types::I80F48; + +use crate::error::*; +use crate::health::*; +use crate::state::*; +use crate::util::checked_math as cm; + +use crate::logs::{emit_perp_balances, PerpLiqBankruptcyLog, TokenBalanceLog}; + +// Remaining accounts: +// - merged health accounts for liqor+liqee +#[derive(Accounts)] +pub struct PerpLiqBankruptcy<'info> { + #[account( + has_one = insurance_vault, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] + pub group: AccountLoader<'info, Group>, + + #[account(mut, has_one = group)] + pub perp_market: AccountLoader<'info, PerpMarket>, + + #[account( + mut, + has_one = group + // liqor_owner is checked at #1 + )] + pub liqor: AccountLoader<'info, MangoAccountFixed>, + pub liqor_owner: Signer<'info>, + + #[account( + mut, + has_one = group + )] + pub liqee: AccountLoader<'info, MangoAccountFixed>, + + #[account( + mut, + has_one = group, + // address is checked at #2 + )] + pub settle_bank: AccountLoader<'info, Bank>, + + #[account( + mut, + address = settle_bank.load()?.vault + )] + pub settle_vault: Account<'info, TokenAccount>, + + /// CHECK: Oracle can have different account types + #[account(address = settle_bank.load()?.oracle)] + pub settle_oracle: UncheckedAccount<'info>, + + // future: this would be an insurance fund vault specific to a + // trustless token, separate from the shared one on the group + #[account(mut)] + pub insurance_vault: Account<'info, TokenAccount>, + + pub token_program: Program<'info, Token>, +} + +impl<'info> PerpLiqBankruptcy<'info> { + pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> { + let program = self.token_program.to_account_info(); + let accounts = token::Transfer { + from: self.insurance_vault.to_account_info(), + to: self.settle_vault.to_account_info(), + authority: self.group.to_account_info(), + }; + CpiContext::new(program, accounts) + } +} + +pub fn perp_liq_bankruptcy(ctx: Context, max_liab_transfer: u64) -> Result<()> { + let group = ctx.accounts.group.load()?; + let group_pk = &ctx.accounts.group.key(); + + let mut liqor = ctx.accounts.liqor.load_full_mut()?; + // account constraint #1 + require!( + liqor + .fixed + .is_owner_or_delegate(ctx.accounts.liqor_owner.key()), + MangoError::SomeError + ); + require!(!liqor.fixed.being_liquidated(), MangoError::BeingLiquidated); + + let mut liqee = ctx.accounts.liqee.load_full_mut()?; + let mut liqee_health_cache = { + let account_retriever = ScanningAccountRetriever::new(ctx.remaining_accounts, group_pk)?; + new_health_cache(&liqee.borrow(), &account_retriever) + .context("create liqee health cache")? + }; + + // Check if liqee is bankrupt + require!( + !liqee_health_cache.has_liquidatable_assets(), + MangoError::IsNotBankrupt + ); + liqee.fixed.set_being_liquidated(true); + + // Find bankrupt liab amount + let mut perp_market = ctx.accounts.perp_market.load_mut()?; + let settle_token_index = perp_market.settle_token_index; + let liqee_perp_position = liqee.perp_position_mut(perp_market.perp_market_index)?; + require_msg!( + liqee_perp_position.base_position_lots() == 0, + "liqee must have zero base position" + ); + require!( + !liqee_perp_position.has_open_orders(), + MangoError::HasOpenPerpOrders + ); + + let liqee_pnl = liqee_perp_position.quote_position_native(); + require_msg!( + liqee_pnl.is_negative(), + "liqee pnl must be negative, was {}", + liqee_pnl + ); + let liab_transfer = (-liqee_pnl).min(I80F48::from(max_liab_transfer)); + + // Preparation for covering it with the insurance fund + let insurance_vault_amount = if perp_market.elligible_for_group_insurance_fund() { + ctx.accounts.insurance_vault.amount + } else { + 0 + }; + + let liquidation_fee_factor = cm!(I80F48::ONE + perp_market.liquidation_fee); + + let insurance_transfer = cm!(liab_transfer * liquidation_fee_factor) + .checked_ceil() + .unwrap() + .checked_to_num::() + .unwrap() + .min(insurance_vault_amount); + + let insurance_transfer_i80f48 = I80F48::from(insurance_transfer); + let insurance_fund_exhausted = insurance_transfer == insurance_vault_amount; + let insurance_liab_transfer = + cm!(insurance_transfer_i80f48 / liquidation_fee_factor).min(liab_transfer); + + // Try using the insurance fund if possible + if insurance_transfer > 0 { + let mut settle_bank = ctx.accounts.settle_bank.load_mut()?; + require_eq!(settle_bank.token_index, settle_token_index); + require_keys_eq!(settle_bank.mint, ctx.accounts.insurance_vault.mint); + + // move insurance assets into quote bank + let group_seeds = group_seeds!(group); + token::transfer( + ctx.accounts.transfer_ctx().with_signer(&[group_seeds]), + insurance_transfer, + )?; + + // credit the liqor with quote tokens + let (liqor_quote, _, _) = liqor.ensure_token_position(settle_token_index)?; + settle_bank.deposit( + liqor_quote, + insurance_transfer_i80f48, + Clock::get()?.unix_timestamp.try_into().unwrap(), + )?; + + emit!(TokenBalanceLog { + mango_group: ctx.accounts.group.key(), + mango_account: ctx.accounts.liqor.key(), + token_index: settle_token_index, + indexed_position: liqor_quote.indexed_position.to_bits(), + deposit_index: settle_bank.deposit_index.to_bits(), + borrow_index: settle_bank.borrow_index.to_bits(), + }); + + // transfer perp quote loss from the liqee to the liqor + let liqor_perp_position = liqor + .ensure_perp_position(perp_market.perp_market_index, settle_token_index)? + .0; + liqee_perp_position.record_settle(-insurance_liab_transfer); + liqor_perp_position.record_liquidation_quote_change(-insurance_liab_transfer); + + emit_perp_balances( + ctx.accounts.group.key(), + ctx.accounts.liqor.key(), + perp_market.perp_market_index, + liqor_perp_position, + &perp_market, + ); + } + + // Socialize loss if the insurance fund is exhausted + let remaining_liab = liab_transfer - insurance_liab_transfer; + let mut socialized_loss = I80F48::ZERO; + if insurance_fund_exhausted && remaining_liab.is_positive() { + perp_market.socialize_loss(-remaining_liab)?; + liqee_perp_position.record_settle(-remaining_liab); + require_eq!(liqee_perp_position.quote_position_native(), 0); + socialized_loss = remaining_liab; + } + + emit_perp_balances( + ctx.accounts.group.key(), + ctx.accounts.liqee.key(), + perp_market.perp_market_index, + liqee_perp_position, + &perp_market, + ); + + emit!(PerpLiqBankruptcyLog { + mango_group: ctx.accounts.group.key(), + liqee: ctx.accounts.liqee.key(), + liqor: ctx.accounts.liqor.key(), + perp_market_index: perp_market.perp_market_index, + insurance_transfer: insurance_transfer_i80f48.to_bits(), + socialized_loss: socialized_loss.to_bits() + }); + + // Check liqee health again + liqee_health_cache.recompute_perp_info(liqee_perp_position, &perp_market)?; + let liqee_init_health = liqee_health_cache.health(HealthType::Init); + liqee + .fixed + .maybe_recover_from_being_liquidated(liqee_init_health); + + drop(perp_market); + + // Check liqor's health + if !liqor.fixed.is_in_health_region() { + let account_retriever = ScanningAccountRetriever::new(ctx.remaining_accounts, group_pk)?; + let liqor_health = compute_health(&liqor.borrow(), HealthType::Init, &account_retriever) + .context("compute liqor health")?; + require!(liqor_health >= 0, MangoError::HealthMustBePositive); + } + + Ok(()) +} diff --git a/programs/mango-v4/src/instructions/perp_liq_base_position.rs b/programs/mango-v4/src/instructions/perp_liq_base_position.rs index 34ef11eda..aa5bc66ba 100644 --- a/programs/mango-v4/src/instructions/perp_liq_base_position.rs +++ b/programs/mango-v4/src/instructions/perp_liq_base_position.rs @@ -11,6 +11,9 @@ use crate::logs::{emit_perp_balances, PerpLiqBasePositionLog}; #[derive(Accounts)] pub struct PerpLiqBasePosition<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group, has_one = oracle)] diff --git a/programs/mango-v4/src/instructions/perp_liq_force_cancel_orders.rs b/programs/mango-v4/src/instructions/perp_liq_force_cancel_orders.rs index b33901a1d..d284ee67c 100644 --- a/programs/mango-v4/src/instructions/perp_liq_force_cancel_orders.rs +++ b/programs/mango-v4/src/instructions/perp_liq_force_cancel_orders.rs @@ -7,6 +7,9 @@ use crate::state::*; #[derive(Accounts)] pub struct PerpLiqForceCancelOrders<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/perp_place_order.rs b/programs/mango-v4/src/instructions/perp_place_order.rs index cd66742af..9af01690b 100644 --- a/programs/mango-v4/src/instructions/perp_place_order.rs +++ b/programs/mango-v4/src/instructions/perp_place_order.rs @@ -11,6 +11,9 @@ use crate::state::{ #[derive(Accounts)] pub struct PerpPlaceOrder<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/perp_settle_fees.rs b/programs/mango-v4/src/instructions/perp_settle_fees.rs index 6bd30abe5..576582876 100644 --- a/programs/mango-v4/src/instructions/perp_settle_fees.rs +++ b/programs/mango-v4/src/instructions/perp_settle_fees.rs @@ -12,6 +12,9 @@ use crate::logs::{emit_perp_balances, PerpSettleFeesLog, TokenBalanceLog}; #[derive(Accounts)] pub struct PerpSettleFees<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group, has_one = oracle)] diff --git a/programs/mango-v4/src/instructions/perp_settle_pnl.rs b/programs/mango-v4/src/instructions/perp_settle_pnl.rs index 95731e37f..2d1381afe 100644 --- a/programs/mango-v4/src/instructions/perp_settle_pnl.rs +++ b/programs/mango-v4/src/instructions/perp_settle_pnl.rs @@ -11,6 +11,9 @@ use crate::state::{Group, MangoAccountFixed, MangoAccountLoader, PerpMarket}; #[derive(Accounts)] pub struct PerpSettlePnl<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/perp_update_funding.rs b/programs/mango-v4/src/instructions/perp_update_funding.rs index bfcd93ea1..576ba777b 100644 --- a/programs/mango-v4/src/instructions/perp_update_funding.rs +++ b/programs/mango-v4/src/instructions/perp_update_funding.rs @@ -1,10 +1,14 @@ use anchor_lang::prelude::*; use crate::accounts_zerocopy::*; +use crate::error::MangoError; use crate::state::{BookSide, Group, Orderbook, PerpMarket}; #[derive(Accounts)] pub struct PerpUpdateFunding<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, // Required for group metadata parsing #[account( diff --git a/programs/mango-v4/src/instructions/serum3_cancel_all_orders.rs b/programs/mango-v4/src/instructions/serum3_cancel_all_orders.rs index 9bb32086d..58546b256 100644 --- a/programs/mango-v4/src/instructions/serum3_cancel_all_orders.rs +++ b/programs/mango-v4/src/instructions/serum3_cancel_all_orders.rs @@ -8,6 +8,9 @@ use crate::state::*; #[derive(Accounts)] pub struct Serum3CancelAllOrders<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/serum3_cancel_order.rs b/programs/mango-v4/src/instructions/serum3_cancel_order.rs index 428cc7f8e..53529cd40 100644 --- a/programs/mango-v4/src/instructions/serum3_cancel_order.rs +++ b/programs/mango-v4/src/instructions/serum3_cancel_order.rs @@ -13,6 +13,9 @@ use crate::serum3_cpi::load_open_orders_ref; #[derive(Accounts)] pub struct Serum3CancelOrder<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/serum3_close_open_orders.rs b/programs/mango-v4/src/instructions/serum3_close_open_orders.rs index d6da507a5..4c7190658 100644 --- a/programs/mango-v4/src/instructions/serum3_close_open_orders.rs +++ b/programs/mango-v4/src/instructions/serum3_close_open_orders.rs @@ -5,6 +5,9 @@ use crate::state::*; #[derive(Accounts)] pub struct Serum3CloseOpenOrders<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/serum3_create_open_orders.rs b/programs/mango-v4/src/instructions/serum3_create_open_orders.rs index fa40cdb14..be4a49b43 100644 --- a/programs/mango-v4/src/instructions/serum3_create_open_orders.rs +++ b/programs/mango-v4/src/instructions/serum3_create_open_orders.rs @@ -5,6 +5,9 @@ use crate::state::*; #[derive(Accounts)] pub struct Serum3CreateOpenOrders<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/serum3_deregister_market.rs b/programs/mango-v4/src/instructions/serum3_deregister_market.rs index c0481d85a..8efe7b281 100644 --- a/programs/mango-v4/src/instructions/serum3_deregister_market.rs +++ b/programs/mango-v4/src/instructions/serum3_deregister_market.rs @@ -1,14 +1,15 @@ use anchor_lang::prelude::*; use anchor_spl::token::Token; -use crate::state::*; +use crate::{error::MangoError, state::*}; #[derive(Accounts)] pub struct Serum3DeregisterMarket<'info> { #[account( mut, - constraint = group.load()?.is_testing(), has_one = admin, + constraint = group.load()?.is_testing(), + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/serum3_liq_force_cancel_orders.rs b/programs/mango-v4/src/instructions/serum3_liq_force_cancel_orders.rs index bd68ccb48..ff8f8cca2 100644 --- a/programs/mango-v4/src/instructions/serum3_liq_force_cancel_orders.rs +++ b/programs/mango-v4/src/instructions/serum3_liq_force_cancel_orders.rs @@ -14,6 +14,9 @@ use crate::state::*; #[derive(Accounts)] pub struct Serum3LiqForceCancelOrders<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] diff --git a/programs/mango-v4/src/instructions/serum3_place_order.rs b/programs/mango-v4/src/instructions/serum3_place_order.rs index 503508886..2f279d102 100644 --- a/programs/mango-v4/src/instructions/serum3_place_order.rs +++ b/programs/mango-v4/src/instructions/serum3_place_order.rs @@ -135,6 +135,9 @@ pub enum Serum3Side { #[derive(Accounts)] pub struct Serum3PlaceOrder<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/serum3_register_market.rs b/programs/mango-v4/src/instructions/serum3_register_market.rs index 36dc790ce..d30353e2e 100644 --- a/programs/mango-v4/src/instructions/serum3_register_market.rs +++ b/programs/mango-v4/src/instructions/serum3_register_market.rs @@ -13,6 +13,7 @@ pub struct Serum3RegisterMarket<'info> { #[account( mut, has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, constraint = group.load()?.serum3_supported() )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/serum3_settle_funds.rs b/programs/mango-v4/src/instructions/serum3_settle_funds.rs index d4deab75d..81fcfc9a0 100644 --- a/programs/mango-v4/src/instructions/serum3_settle_funds.rs +++ b/programs/mango-v4/src/instructions/serum3_settle_funds.rs @@ -13,6 +13,9 @@ use crate::logs::{LoanOriginationFeeInstruction, WithdrawLoanOriginationFeeLog}; #[derive(Accounts)] pub struct Serum3SettleFunds<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/stub_oracle_close.rs b/programs/mango-v4/src/instructions/stub_oracle_close.rs index eb640c529..cb19aa014 100644 --- a/programs/mango-v4/src/instructions/stub_oracle_close.rs +++ b/programs/mango-v4/src/instructions/stub_oracle_close.rs @@ -1,13 +1,14 @@ use anchor_lang::prelude::*; use anchor_spl::token::Token; -use crate::state::*; +use crate::{error::MangoError, state::*}; #[derive(Accounts)] pub struct StubOracleClose<'info> { #[account( - constraint = group.load()?.is_testing(), has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, + constraint = group.load()?.is_testing(), )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/stub_oracle_create.rs b/programs/mango-v4/src/instructions/stub_oracle_create.rs index 25b928ce4..d132d8c73 100644 --- a/programs/mango-v4/src/instructions/stub_oracle_create.rs +++ b/programs/mango-v4/src/instructions/stub_oracle_create.rs @@ -2,12 +2,13 @@ use anchor_lang::prelude::*; use anchor_spl::token::Mint; use fixed::types::I80F48; -use crate::state::*; +use crate::{error::MangoError, state::*}; #[derive(Accounts)] pub struct StubOracleCreate<'info> { #[account( has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/stub_oracle_set.rs b/programs/mango-v4/src/instructions/stub_oracle_set.rs index 6c928416c..6278dd1fd 100644 --- a/programs/mango-v4/src/instructions/stub_oracle_set.rs +++ b/programs/mango-v4/src/instructions/stub_oracle_set.rs @@ -1,12 +1,13 @@ use anchor_lang::prelude::*; use fixed::types::I80F48; -use crate::state::*; +use crate::{error::MangoError, state::*}; #[derive(Accounts)] pub struct StubOracleSet<'info> { #[account( has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/token_add_bank.rs b/programs/mango-v4/src/instructions/token_add_bank.rs index 87d51d952..15acbfdc8 100644 --- a/programs/mango-v4/src/instructions/token_add_bank.rs +++ b/programs/mango-v4/src/instructions/token_add_bank.rs @@ -9,6 +9,7 @@ use crate::state::*; pub struct TokenAddBank<'info> { #[account( has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, constraint = group.load()?.multiple_banks_supported() )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/token_deposit.rs b/programs/mango-v4/src/instructions/token_deposit.rs index ce035600c..5382bc027 100644 --- a/programs/mango-v4/src/instructions/token_deposit.rs +++ b/programs/mango-v4/src/instructions/token_deposit.rs @@ -15,6 +15,9 @@ use crate::logs::{DepositLog, TokenBalanceLog}; // Same as TokenDeposit, but without the owner signing #[derive(Accounts)] pub struct TokenDepositIntoExisting<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group)] @@ -45,6 +48,9 @@ pub struct TokenDepositIntoExisting<'info> { #[derive(Accounts)] pub struct TokenDeposit<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group, has_one = owner)] diff --git a/programs/mango-v4/src/instructions/token_deregister.rs b/programs/mango-v4/src/instructions/token_deregister.rs index 687bc39f6..1fc4a7a4a 100644 --- a/programs/mango-v4/src/instructions/token_deregister.rs +++ b/programs/mango-v4/src/instructions/token_deregister.rs @@ -1,7 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::token::{self, CloseAccount, Token, TokenAccount}; -use crate::{accounts_zerocopy::LoadZeroCopyRef, state::*}; +use crate::{accounts_zerocopy::LoadZeroCopyRef, error::MangoError, state::*}; use anchor_lang::AccountsClose; /// In addition to these accounts, there must be remaining_accounts: @@ -9,8 +9,9 @@ use anchor_lang::AccountsClose; #[derive(Accounts)] pub struct TokenDeregister<'info> { #[account( - constraint = group.load()?.is_testing(), has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, + constraint = group.load()?.is_testing(), )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/token_edit.rs b/programs/mango-v4/src/instructions/token_edit.rs index 74afdeef3..92506db94 100644 --- a/programs/mango-v4/src/instructions/token_edit.rs +++ b/programs/mango-v4/src/instructions/token_edit.rs @@ -16,7 +16,7 @@ use crate::logs::TokenMetaDataLog; #[derive(Accounts)] pub struct TokenEdit<'info> { #[account( - has_one = admin, + has_one = admin )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs b/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs index 993b564af..3f62e07c8 100644 --- a/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs +++ b/programs/mango-v4/src/instructions/token_liq_bankruptcy.rs @@ -22,6 +22,7 @@ use crate::logs::{ pub struct TokenLiqBankruptcy<'info> { #[account( has_one = insurance_vault, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, diff --git a/programs/mango-v4/src/instructions/token_liq_with_token.rs b/programs/mango-v4/src/instructions/token_liq_with_token.rs index 506d66114..9af24dbf1 100644 --- a/programs/mango-v4/src/instructions/token_liq_with_token.rs +++ b/programs/mango-v4/src/instructions/token_liq_with_token.rs @@ -13,6 +13,9 @@ use crate::util::checked_math as cm; #[derive(Accounts)] pub struct TokenLiqWithToken<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account( diff --git a/programs/mango-v4/src/instructions/token_register.rs b/programs/mango-v4/src/instructions/token_register.rs index 4a0a7aade..5fc4f9c56 100644 --- a/programs/mango-v4/src/instructions/token_register.rs +++ b/programs/mango-v4/src/instructions/token_register.rs @@ -19,6 +19,7 @@ const FIRST_BANK_NUM: u32 = 0; pub struct TokenRegister<'info> { #[account( has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, pub admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/token_register_trustless.rs b/programs/mango-v4/src/instructions/token_register_trustless.rs index 49cca4833..f40d995a3 100644 --- a/programs/mango-v4/src/instructions/token_register_trustless.rs +++ b/programs/mango-v4/src/instructions/token_register_trustless.rs @@ -17,6 +17,7 @@ const FIRST_BANK_NUM: u32 = 0; pub struct TokenRegisterTrustless<'info> { #[account( has_one = fast_listing_admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted )] pub group: AccountLoader<'info, Group>, pub fast_listing_admin: Signer<'info>, diff --git a/programs/mango-v4/src/instructions/token_update_index_and_rate.rs b/programs/mango-v4/src/instructions/token_update_index_and_rate.rs index 6ee12a45e..1c6027645 100644 --- a/programs/mango-v4/src/instructions/token_update_index_and_rate.rs +++ b/programs/mango-v4/src/instructions/token_update_index_and_rate.rs @@ -26,6 +26,9 @@ pub mod compute_budget { /// or ComputeBudget instructions. #[derive(Accounts)] pub struct TokenUpdateIndexAndRate<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, // Required for group metadata parsing #[account( diff --git a/programs/mango-v4/src/instructions/token_withdraw.rs b/programs/mango-v4/src/instructions/token_withdraw.rs index 9dfbc6319..b86b98dfd 100644 --- a/programs/mango-v4/src/instructions/token_withdraw.rs +++ b/programs/mango-v4/src/instructions/token_withdraw.rs @@ -15,6 +15,9 @@ use crate::util::checked_math as cm; #[derive(Accounts)] pub struct TokenWithdraw<'info> { + #[account( + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted + )] pub group: AccountLoader<'info, Group>, #[account(mut, has_one = group, has_one = owner)] diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index 4105923fe..89f116ccb 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -45,6 +45,7 @@ pub mod mango_v4 { ctx: Context, admin_opt: Option, fast_listing_admin_opt: Option, + security_admin_opt: Option, testing_opt: Option, version_opt: Option, ) -> Result<()> { @@ -52,11 +53,16 @@ pub mod mango_v4 { ctx, admin_opt, fast_listing_admin_opt, + security_admin_opt, testing_opt, version_opt, ) } + pub fn group_toggle_halt(ctx: Context, halted: bool) -> Result<()> { + instructions::group_toggle_halt(ctx, halted) + } + pub fn group_close(ctx: Context) -> Result<()> { instructions::group_close(ctx) } diff --git a/programs/mango-v4/src/state/group.rs b/programs/mango-v4/src/state/group.rs index 00afe202b..057b11494 100644 --- a/programs/mango-v4/src/state/group.rs +++ b/programs/mango-v4/src/state/group.rs @@ -31,15 +31,19 @@ pub struct Group { pub version: u8, - pub padding2: [u8; 5], + pub halted: u8, + + pub padding2: [u8; 4], pub address_lookup_tables: [Pubkey; 20], - pub reserved: [u8; 1920], + pub security_admin: Pubkey, + + pub reserved: [u8; 1888], } const_assert_eq!( size_of::(), - 32 * 5 + 4 + 4 + 1 + 1 + 6 + 20 * 32 + 1920 + 32 + 4 + 32 * 2 + 4 + 32 * 2 + 4 + 4 + 20 * 32 + 32 + 1888 ); const_assert_eq!(size_of::(), 2736); const_assert_eq!(size_of::() % 8, 0); @@ -60,6 +64,10 @@ impl Group { pub fn perps_supported(&self) -> bool { self.is_testing() || self.version > 1 } + + pub fn is_operational(&self) -> bool { + self.halted == 0 + } } // note: using creator instead of admin, since admin can be changed diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 2409bca80..e7564c10e 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -129,6 +129,7 @@ export class MangoClient { group: Group, admin?: PublicKey, fastListingAdmin?: PublicKey, + securityAdmin?: PublicKey, testing?: number, version?: number, ): Promise { @@ -136,6 +137,7 @@ export class MangoClient { .groupEdit( admin ?? null, fastListingAdmin ?? null, + securityAdmin ?? null, testing ?? null, version ?? null, ) @@ -146,6 +148,19 @@ export class MangoClient { .rpc(); } + public async groupToggleHalt( + group: Group, + halt: boolean, + ): Promise { + return await this.program.methods + .groupToggleHalt(halt) + .accounts({ + group: group.publicKey, + admin: (this.program.provider as AnchorProvider).wallet.publicKey, + }) + .rpc(); + } + public async groupClose(group: Group): Promise { const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey; return await this.program.methods diff --git a/ts/client/src/mango_v4.ts b/ts/client/src/mango_v4.ts index dcacfd3c0..14803afbe 100644 --- a/ts/client/src/mango_v4.ts +++ b/ts/client/src/mango_v4.ts @@ -121,6 +121,12 @@ export type MangoV4 = { "option": "publicKey" } }, + { + "name": "securityAdminOpt", + "type": { + "option": "publicKey" + } + }, { "name": "testingOpt", "type": { @@ -135,6 +141,27 @@ export type MangoV4 = { } ] }, + { + "name": "groupToggleHalt", + "accounts": [ + { + "name": "group", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "halted", + "type": "bool" + } + ] + }, { "name": "groupClose", "accounts": [ @@ -3364,18 +3391,13 @@ export type MangoV4 = { ] }, { - "name": "perpLiqBankruptcy", + "name": "perpLiqQuoteAndBankruptcy", "accounts": [ { "name": "group", "isMut": false, "isSigner": false }, - { - "name": "perpMarket", - "isMut": true, - "isSigner": false - }, { "name": "liqor", "isMut": true, @@ -3391,6 +3413,16 @@ export type MangoV4 = { "isMut": true, "isSigner": false }, + { + "name": "perpMarket", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, { "name": "settleBank", "isMut": true, @@ -3850,12 +3882,16 @@ export type MangoV4 = { "name": "version", "type": "u8" }, + { + "name": "halted", + "type": "u8" + }, { "name": "padding2", "type": { "array": [ "u8", - 5 + 4 ] } }, @@ -3868,12 +3904,16 @@ export type MangoV4 = { ] } }, + { + "name": "securityAdmin", + "type": "publicKey" + }, { "name": "reserved", "type": { "array": [ "u8", - 1920 + 1888 ] } } @@ -4778,6 +4818,13 @@ export type MangoV4 = { { "name": "marketIndex", "type": "u16" + }, + { + "name": "hasZeroFunds", + "docs": [ + "The open orders account has no free or reserved funds" + ], + "type": "bool" } ] } @@ -7042,6 +7089,16 @@ export type MangoV4 = { "name": "socializedLoss", "type": "i128", "index": false + }, + { + "name": "startingLiabDepositIndex", + "type": "i128", + "index": false + }, + { + "name": "endingLiabDepositIndex", + "type": "i128", + "index": false } ] }, @@ -7307,6 +7364,56 @@ export type MangoV4 = { "name": "socializedLoss", "type": "i128", "index": false + }, + { + "name": "startingLongFunding", + "type": "i128", + "index": false + }, + { + "name": "startingShortFunding", + "type": "i128", + "index": false + }, + { + "name": "endingLongFunding", + "type": "i128", + "index": false + }, + { + "name": "endingShortFunding", + "type": "i128", + "index": false + } + ] + }, + { + "name": "PerpLiqQuoteAndBankruptcyLog", + "fields": [ + { + "name": "mangoGroup", + "type": "publicKey", + "index": false + }, + { + "name": "liqee", + "type": "publicKey", + "index": false + }, + { + "name": "liqor", + "type": "publicKey", + "index": false + }, + { + "name": "perpMarketIndex", + "type": "u16", + "index": false + }, + { + "name": "settlement", + "type": "i128", + "index": false } ] }, @@ -7536,6 +7643,36 @@ export type MangoV4 = { "code": 6031, "name": "MarketInReduceOnlyMode", "msg": "market is in reduce only mode" + }, + { + "code": 6032, + "name": "GroupIsHalted", + "msg": "group is halted" + }, + { + "code": 6033, + "name": "PerpHasBaseLots", + "msg": "the perp position has non-zero base lots" + }, + { + "code": 6034, + "name": "HasOpenOrUnsettledSerum3Orders", + "msg": "there are open or unsettled serum3 orders" + }, + { + "code": 6035, + "name": "HasLiquidatableTokenPosition", + "msg": "has liquidatable token position" + }, + { + "code": 6036, + "name": "HasLiquidatablePerpBasePosition", + "msg": "has liquidatable perp base position" + }, + { + "code": 6037, + "name": "HasLiquidatableTrustedPerpPnl", + "msg": "has liquidatable trusted perp pnl" } ] }; @@ -7663,6 +7800,12 @@ export const IDL: MangoV4 = { "option": "publicKey" } }, + { + "name": "securityAdminOpt", + "type": { + "option": "publicKey" + } + }, { "name": "testingOpt", "type": { @@ -7677,6 +7820,27 @@ export const IDL: MangoV4 = { } ] }, + { + "name": "groupToggleHalt", + "accounts": [ + { + "name": "group", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "halted", + "type": "bool" + } + ] + }, { "name": "groupClose", "accounts": [ @@ -10906,18 +11070,13 @@ export const IDL: MangoV4 = { ] }, { - "name": "perpLiqBankruptcy", + "name": "perpLiqQuoteAndBankruptcy", "accounts": [ { "name": "group", "isMut": false, "isSigner": false }, - { - "name": "perpMarket", - "isMut": true, - "isSigner": false - }, { "name": "liqor", "isMut": true, @@ -10933,6 +11092,16 @@ export const IDL: MangoV4 = { "isMut": true, "isSigner": false }, + { + "name": "perpMarket", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + }, { "name": "settleBank", "isMut": true, @@ -11392,12 +11561,16 @@ export const IDL: MangoV4 = { "name": "version", "type": "u8" }, + { + "name": "halted", + "type": "u8" + }, { "name": "padding2", "type": { "array": [ "u8", - 5 + 4 ] } }, @@ -11410,12 +11583,16 @@ export const IDL: MangoV4 = { ] } }, + { + "name": "securityAdmin", + "type": "publicKey" + }, { "name": "reserved", "type": { "array": [ "u8", - 1920 + 1888 ] } } @@ -12320,6 +12497,13 @@ export const IDL: MangoV4 = { { "name": "marketIndex", "type": "u16" + }, + { + "name": "hasZeroFunds", + "docs": [ + "The open orders account has no free or reserved funds" + ], + "type": "bool" } ] } @@ -14584,6 +14768,16 @@ export const IDL: MangoV4 = { "name": "socializedLoss", "type": "i128", "index": false + }, + { + "name": "startingLiabDepositIndex", + "type": "i128", + "index": false + }, + { + "name": "endingLiabDepositIndex", + "type": "i128", + "index": false } ] }, @@ -14849,6 +15043,56 @@ export const IDL: MangoV4 = { "name": "socializedLoss", "type": "i128", "index": false + }, + { + "name": "startingLongFunding", + "type": "i128", + "index": false + }, + { + "name": "startingShortFunding", + "type": "i128", + "index": false + }, + { + "name": "endingLongFunding", + "type": "i128", + "index": false + }, + { + "name": "endingShortFunding", + "type": "i128", + "index": false + } + ] + }, + { + "name": "PerpLiqQuoteAndBankruptcyLog", + "fields": [ + { + "name": "mangoGroup", + "type": "publicKey", + "index": false + }, + { + "name": "liqee", + "type": "publicKey", + "index": false + }, + { + "name": "liqor", + "type": "publicKey", + "index": false + }, + { + "name": "perpMarketIndex", + "type": "u16", + "index": false + }, + { + "name": "settlement", + "type": "i128", + "index": false } ] }, @@ -15078,6 +15322,36 @@ export const IDL: MangoV4 = { "code": 6031, "name": "MarketInReduceOnlyMode", "msg": "market is in reduce only mode" + }, + { + "code": 6032, + "name": "GroupIsHalted", + "msg": "group is halted" + }, + { + "code": 6033, + "name": "PerpHasBaseLots", + "msg": "the perp position has non-zero base lots" + }, + { + "code": 6034, + "name": "HasOpenOrUnsettledSerum3Orders", + "msg": "there are open or unsettled serum3 orders" + }, + { + "code": 6035, + "name": "HasLiquidatableTokenPosition", + "msg": "has liquidatable token position" + }, + { + "code": 6036, + "name": "HasLiquidatablePerpBasePosition", + "msg": "has liquidatable perp base position" + }, + { + "code": 6037, + "name": "HasLiquidatableTrustedPerpPnl", + "msg": "has liquidatable trusted perp pnl" } ] };