From 6ac9f192877fa569b3c2b79043b344ccf85946de Mon Sep 17 00:00:00 2001 From: microwavedcola1 <89031858+microwavedcola1@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:42:01 +0200 Subject: [PATCH] Perp force close positions in a market (#525) * force close tokens Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * add test Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * reset Signed-off-by: microwavedcola1 * force close perp market Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 * rename Signed-off-by: microwavedcola1 * test Signed-off-by: microwavedcola1 * update Signed-off-by: microwavedcola1 * add back staleness slot check Signed-off-by: microwavedcola1 * Fixes from review Signed-off-by: microwavedcola1 --------- Signed-off-by: microwavedcola1 --- mango_v4.json | 46 +++- programs/mango-v4/src/accounts_ix/mod.rs | 2 + .../accounts_ix/perp_force_close_position.rs | 37 +++ .../mango-v4/src/instructions/ix_gate_set.rs | 1 + programs/mango-v4/src/instructions/mod.rs | 2 + .../src/instructions/perp_create_market.rs | 1 + .../src/instructions/perp_edit_market.rs | 14 ++ .../instructions/perp_force_close_position.rs | 62 +++++ programs/mango-v4/src/lib.rs | 8 + programs/mango-v4/src/state/group.rs | 1 + programs/mango-v4/src/state/perp_market.rs | 8 +- .../mango-v4/tests/cases/test_force_close.rs | 214 +++++++++++++++++- .../mango-v4/tests/cases/test_reduce_only.rs | 2 + .../tests/program_test/mango_client.rs | 42 +++- ts/client/src/client.ts | 1 + ts/client/src/clientIxParamBuilder.ts | 6 + ts/client/src/mango_v4.ts | 92 +++++++- 17 files changed, 533 insertions(+), 6 deletions(-) create mode 100644 programs/mango-v4/src/accounts_ix/perp_force_close_position.rs create mode 100644 programs/mango-v4/src/instructions/perp_force_close_position.rs diff --git a/mango_v4.json b/mango_v4.json index 63be09512..83cd0530a 100644 --- a/mango_v4.json +++ b/mango_v4.json @@ -3092,6 +3092,12 @@ "type": { "option": "string" } + }, + { + "name": "forceCloseOpt", + "type": { + "option": "bool" + } } ] }, @@ -3635,6 +3641,37 @@ ], "args": [] }, + { + "name": "perpForceClosePosition", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "perpMarket", + "isMut": true, + "isSigner": false + }, + { + "name": "accountA", + "isMut": true, + "isSigner": false + }, + { + "name": "accountB", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, { "name": "perpSettleFees", "accounts": [ @@ -5048,12 +5085,16 @@ ], "type": "u8" }, + { + "name": "forceClose", + "type": "u8" + }, { "name": "padding4", "type": { "array": [ "u8", - 7 + 6 ] } }, @@ -7065,6 +7106,9 @@ }, { "name": "TokenForceCloseBorrowsWithToken" + }, + { + "name": "PerpForceClosePosition" } ] } diff --git a/programs/mango-v4/src/accounts_ix/mod.rs b/programs/mango-v4/src/accounts_ix/mod.rs index 7cdd811f3..4404f2c3c 100644 --- a/programs/mango-v4/src/accounts_ix/mod.rs +++ b/programs/mango-v4/src/accounts_ix/mod.rs @@ -23,6 +23,7 @@ pub use perp_consume_events::*; pub use perp_create_market::*; pub use perp_deactivate_position::*; pub use perp_edit_market::*; +pub use perp_force_close_position::*; pub use perp_liq_base_or_positive_pnl::*; pub use perp_liq_force_cancel_orders::*; pub use perp_liq_negative_pnl_or_bankruptcy::*; @@ -80,6 +81,7 @@ mod perp_consume_events; mod perp_create_market; mod perp_deactivate_position; mod perp_edit_market; +mod perp_force_close_position; mod perp_liq_base_or_positive_pnl; mod perp_liq_force_cancel_orders; mod perp_liq_negative_pnl_or_bankruptcy; diff --git a/programs/mango-v4/src/accounts_ix/perp_force_close_position.rs b/programs/mango-v4/src/accounts_ix/perp_force_close_position.rs new file mode 100644 index 000000000..d42910a81 --- /dev/null +++ b/programs/mango-v4/src/accounts_ix/perp_force_close_position.rs @@ -0,0 +1,37 @@ +use crate::error::*; +use crate::state::*; +use anchor_lang::prelude::*; + +#[derive(Accounts)] +pub struct PerpForceClosePosition<'info> { + #[account( + constraint = group.load()?.is_ix_enabled(IxGate::PerpForceClosePosition) @ MangoError::IxIsDisabled, + )] + pub group: AccountLoader<'info, Group>, + + #[account( + mut, + has_one = group, + has_one = oracle, + constraint = perp_market.load()?.is_force_close() + )] + pub perp_market: AccountLoader<'info, PerpMarket>, + + #[account( + mut, + has_one = group, + constraint = account_a.load()?.is_operational() @ MangoError::AccountIsFrozen, + constraint = account_a.key() != account_b.key() + )] + pub account_a: AccountLoader<'info, MangoAccountFixed>, + + #[account( + mut, + has_one = group, + constraint = account_b.load()?.is_operational() @ MangoError::AccountIsFrozen + )] + pub account_b: AccountLoader<'info, MangoAccountFixed>, + + /// CHECK: Oracle can have different account types, constrained by address in perp_market + pub oracle: UncheckedAccount<'info>, +} diff --git a/programs/mango-v4/src/instructions/ix_gate_set.rs b/programs/mango-v4/src/instructions/ix_gate_set.rs index 8fc67d648..9bc3a41b4 100644 --- a/programs/mango-v4/src/instructions/ix_gate_set.rs +++ b/programs/mango-v4/src/instructions/ix_gate_set.rs @@ -66,6 +66,7 @@ pub fn ix_gate_set(ctx: Context, ix_gate: u128) -> Result<()> { log_if_changed(&group, ix_gate, IxGate::TokenWithdraw); log_if_changed(&group, ix_gate, IxGate::AccountBuybackFeesWithMngo); log_if_changed(&group, ix_gate, IxGate::TokenForceCloseBorrowsWithToken); + log_if_changed(&group, ix_gate, IxGate::PerpForceClosePosition); group.ix_gate = ix_gate; diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index 7cdd811f3..4404f2c3c 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -23,6 +23,7 @@ pub use perp_consume_events::*; pub use perp_create_market::*; pub use perp_deactivate_position::*; pub use perp_edit_market::*; +pub use perp_force_close_position::*; pub use perp_liq_base_or_positive_pnl::*; pub use perp_liq_force_cancel_orders::*; pub use perp_liq_negative_pnl_or_bankruptcy::*; @@ -80,6 +81,7 @@ mod perp_consume_events; mod perp_create_market; mod perp_deactivate_position; mod perp_edit_market; +mod perp_force_close_position; mod perp_liq_base_or_positive_pnl; mod perp_liq_force_cancel_orders; mod perp_liq_negative_pnl_or_bankruptcy; diff --git a/programs/mango-v4/src/instructions/perp_create_market.rs b/programs/mango-v4/src/instructions/perp_create_market.rs index a103b8700..2847aed9e 100644 --- a/programs/mango-v4/src/instructions/perp_create_market.rs +++ b/programs/mango-v4/src/instructions/perp_create_market.rs @@ -97,6 +97,7 @@ pub fn perp_create_market( padding3: Default::default(), settle_pnl_limit_window_size_ts, reduce_only: 0, + force_close: 0, padding4: Default::default(), maint_overall_asset_weight: I80F48::from_num(maint_overall_asset_weight), init_overall_asset_weight: I80F48::from_num(init_overall_asset_weight), diff --git a/programs/mango-v4/src/instructions/perp_edit_market.rs b/programs/mango-v4/src/instructions/perp_edit_market.rs index 1563a3e68..09e3a62c7 100644 --- a/programs/mango-v4/src/instructions/perp_edit_market.rs +++ b/programs/mango-v4/src/instructions/perp_edit_market.rs @@ -38,6 +38,7 @@ pub fn perp_edit_market( reset_stable_price: bool, positive_pnl_liquidation_fee_opt: Option, name_opt: Option, + force_close_opt: Option, ) -> Result<()> { let group = ctx.accounts.group.load()?; @@ -330,6 +331,19 @@ pub fn perp_edit_market( require_group_admin = true; }; + if let Some(force_close) = force_close_opt { + if force_close { + require!(perp_market.reduce_only > 0, MangoError::SomeError); + } + msg!( + "Force close: old - {:?}, new - {:?}", + perp_market.force_close, + u8::from(force_close) + ); + perp_market.force_close = u8::from(force_close); + require_group_admin = true; + }; + // account constraint #1 if require_group_admin { require!( diff --git a/programs/mango-v4/src/instructions/perp_force_close_position.rs b/programs/mango-v4/src/instructions/perp_force_close_position.rs new file mode 100644 index 000000000..9d0765ff0 --- /dev/null +++ b/programs/mango-v4/src/instructions/perp_force_close_position.rs @@ -0,0 +1,62 @@ +use anchor_lang::prelude::*; + +use crate::accounts_ix::*; + +use crate::accounts_zerocopy::AccountInfoRef; +use crate::error::MangoError; +use crate::logs::emit_perp_balances; +use crate::state::*; +use fixed::types::I80F48; + +pub fn perp_force_close_position(ctx: Context) -> Result<()> { + let mut perp_market = ctx.accounts.perp_market.load_mut()?; + let perp_market_index = perp_market.perp_market_index; + + let mut account_a = ctx.accounts.account_a.load_full_mut()?; + let mut account_b = ctx.accounts.account_b.load_full_mut()?; + + let account_a_perp_position = account_a.perp_position_mut(perp_market_index)?; + let account_b_perp_position = account_b.perp_position_mut(perp_market_index)?; + + require_gt!( + account_a_perp_position.base_position_lots(), + 0, + MangoError::SomeError + ); + require_gt!( + 0, + account_b_perp_position.base_position_lots(), + MangoError::SomeError + ); + + let base_transfer = account_a_perp_position + .base_position_lots() + .min(account_b_perp_position.base_position_lots().abs()) + .max(0); + let now_slot = Clock::get()?.slot; + let oracle_price = perp_market.oracle_price( + &AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?, + Some(now_slot), + ); + let quote_transfer = I80F48::from(base_transfer * perp_market.base_lot_size) * oracle_price; + + account_a_perp_position.record_trade(&mut perp_market, -base_transfer, quote_transfer); + account_b_perp_position.record_trade(&mut perp_market, base_transfer, -quote_transfer); + + emit_perp_balances( + ctx.accounts.group.key(), + ctx.accounts.account_a.key(), + account_a_perp_position, + &perp_market, + ); + emit_perp_balances( + ctx.accounts.group.key(), + ctx.accounts.account_b.key(), + &account_b_perp_position, + &perp_market, + ); + + // TODO force-close trade log + + Ok(()) +} diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index 45d6dd0f6..36b164f9f 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -677,6 +677,7 @@ pub mod mango_v4 { reset_stable_price: bool, positive_pnl_liquidation_fee_opt: Option, name_opt: Option, + force_close_opt: Option, ) -> Result<()> { #[cfg(feature = "enable-gpl")] instructions::perp_edit_market( @@ -710,6 +711,7 @@ pub mod mango_v4 { reset_stable_price, positive_pnl_liquidation_fee_opt, name_opt, + force_close_opt, )?; Ok(()) } @@ -909,6 +911,12 @@ pub mod mango_v4 { Ok(()) } + pub fn perp_force_close_position(ctx: Context) -> Result<()> { + #[cfg(feature = "enable-gpl")] + instructions::perp_force_close_position(ctx)?; + Ok(()) + } + pub fn perp_settle_fees(ctx: Context, max_settle_amount: u64) -> Result<()> { #[cfg(feature = "enable-gpl")] instructions::perp_settle_fees(ctx, max_settle_amount)?; diff --git a/programs/mango-v4/src/state/group.rs b/programs/mango-v4/src/state/group.rs index 86de31809..4c1a682c7 100644 --- a/programs/mango-v4/src/state/group.rs +++ b/programs/mango-v4/src/state/group.rs @@ -188,6 +188,7 @@ pub enum IxGate { TokenWithdraw = 47, AccountBuybackFeesWithMngo = 48, TokenForceCloseBorrowsWithToken = 49, + PerpForceClosePosition = 50, // NOTE: Adding new variants requires matching changes in ts and the ix_gate_set instruction. } diff --git a/programs/mango-v4/src/state/perp_market.rs b/programs/mango-v4/src/state/perp_market.rs index c57ce1307..93607e14c 100644 --- a/programs/mango-v4/src/state/perp_market.rs +++ b/programs/mango-v4/src/state/perp_market.rs @@ -157,8 +157,9 @@ pub struct PerpMarket { /// If true, users may no longer increase their market exposure. Only actions /// that reduce their position are still allowed. pub reduce_only: u8, + pub force_close: u8, - pub padding4: [u8; 7], + pub padding4: [u8; 6], /// Weights for full perp market health, if positive pub maint_overall_asset_weight: I80F48, @@ -218,6 +219,10 @@ impl PerpMarket { self.reduce_only == 1 } + pub fn is_force_close(&self) -> bool { + self.force_close == 1 + } + pub fn elligible_for_group_insurance_fund(&self) -> bool { self.group_insurance_fund == 1 } @@ -478,6 +483,7 @@ impl PerpMarket { padding3: Default::default(), settle_pnl_limit_window_size_ts: 24 * 60 * 60, reduce_only: 0, + force_close: 0, padding4: Default::default(), maint_overall_asset_weight: I80F48::ONE, init_overall_asset_weight: I80F48::ONE, diff --git a/programs/mango-v4/tests/cases/test_force_close.rs b/programs/mango-v4/tests/cases/test_force_close.rs index a337b45a1..232e536c5 100644 --- a/programs/mango-v4/tests/cases/test_force_close.rs +++ b/programs/mango-v4/tests/cases/test_force_close.rs @@ -1,7 +1,7 @@ use super::*; #[tokio::test] -async fn test_force_close() -> Result<(), TransportError> { +async fn test_force_close_token() -> Result<(), TransportError> { let test_builder = TestContextBuilder::new(); let context = test_builder.start_default().await; let solana = &context.solana.clone(); @@ -228,3 +228,215 @@ async fn test_force_close() -> Result<(), TransportError> { Ok(()) } + +#[tokio::test] +async fn test_force_close_perp() -> Result<(), TransportError> { + let context = TestContext::new().await; + let solana = &context.solana.clone(); + + let admin = TestKeypair::new(); + let owner = context.users[0].key; + let payer = context.users[1].key; + let mints = &context.mints[0..2]; + + // + // SETUP: Create a group and an account + // + + let GroupWithTokens { group, tokens, .. } = GroupWithTokensConfig { + admin, + payer, + mints: mints.to_vec(), + ..GroupWithTokensConfig::default() + } + .create(solana) + .await; + + let deposit_amount = 1000; + let account_0 = create_funded_account( + &solana, + group, + owner, + 0, + &context.users[1], + mints, + deposit_amount, + 0, + ) + .await; + let account_1 = create_funded_account( + &solana, + group, + owner, + 1, + &context.users[1], + mints, + deposit_amount, + 0, + ) + .await; + + // + // TEST: Create a perp market + // + let mango_v4::accounts::PerpCreateMarket { perp_market, .. } = send_tx( + solana, + PerpCreateMarketInstruction { + group, + admin, + payer, + perp_market_index: 0, + quote_lot_size: 10, + base_lot_size: 100, + maint_base_asset_weight: 0.975, + init_base_asset_weight: 0.95, + maint_base_liab_weight: 1.025, + init_base_liab_weight: 1.05, + base_liquidation_fee: 0.012, + maker_fee: -0.0001, + taker_fee: 0.0002, + settle_pnl_limit_factor: -1.0, + settle_pnl_limit_window_size_ts: 24 * 60 * 60, + ..PerpCreateMarketInstruction::with_new_book_and_queue(&solana, &tokens[0]).await + }, + ) + .await + .unwrap(); + + let price_lots = { + let perp_market = solana.get_account::(perp_market).await; + perp_market.native_price_to_lot(I80F48::ONE) + }; + + // + // Place a bid, corresponding ask, and consume event + // + send_tx( + solana, + PerpPlaceOrderInstruction { + account: account_0, + perp_market, + owner, + side: Side::Bid, + price_lots, + max_base_lots: 1, + max_quote_lots: i64::MAX, + reduce_only: false, + client_order_id: 5, + }, + ) + .await + .unwrap(); + check_prev_instruction_post_health(&solana, account_0).await; + + send_tx( + solana, + PerpPlaceOrderInstruction { + account: account_1, + perp_market, + owner, + side: Side::Ask, + price_lots, + max_base_lots: 1, + max_quote_lots: i64::MAX, + reduce_only: false, + client_order_id: 6, + }, + ) + .await + .unwrap(); + check_prev_instruction_post_health(&solana, account_1).await; + + send_tx( + solana, + PerpConsumeEventsInstruction { + perp_market, + mango_accounts: vec![account_0, account_1], + }, + ) + .await + .unwrap(); + + let mango_account_0 = solana.get_account::(account_0).await; + assert_eq!(mango_account_0.perps[0].base_position_lots(), 1); + assert!(assert_equal( + mango_account_0.perps[0].quote_position_native(), + -99.99, + 0.001 + )); + let mango_account_1 = solana.get_account::(account_1).await; + assert_eq!(mango_account_1.perps[0].base_position_lots(), -1); + assert!(assert_equal( + mango_account_1.perps[0].quote_position_native(), + 99.98, + 0.001 + )); + + // Market needs to be in force close + assert!(send_tx( + solana, + PerpForceClosePositionInstruction { + account_a: account_0, + account_b: account_1, + perp_market: perp_market, + }, + ) + .await + .is_err()); + + // + // Set force close and force close position and verify that base position is 0 + // + send_tx( + solana, + PerpMakeReduceOnly { + admin, + group, + perp_market: perp_market, + reduce_only: true, + force_close: true, + }, + ) + .await + .unwrap(); + + // account_a needs to be long, and account_b needs to be short + assert!(send_tx( + solana, + PerpForceClosePositionInstruction { + account_a: account_1, + account_b: account_0, + perp_market: perp_market, + }, + ) + .await + .is_err()); + + send_tx( + solana, + PerpForceClosePositionInstruction { + account_a: account_0, + account_b: account_1, + perp_market: perp_market, + }, + ) + .await + .unwrap(); + + let mango_account_0 = solana.get_account::(account_0).await; + assert_eq!(mango_account_0.perps[0].base_position_lots(), 0); + assert!(assert_equal( + mango_account_0.perps[0].quote_position_native(), + 0.009, + 0.001 + )); + let mango_account_1 = solana.get_account::(account_1).await; + assert_eq!(mango_account_1.perps[0].base_position_lots(), 0); + assert!(assert_equal( + mango_account_1.perps[0].quote_position_native(), + -0.0199, + 0.001 + )); + + Ok(()) +} diff --git a/programs/mango-v4/tests/cases/test_reduce_only.rs b/programs/mango-v4/tests/cases/test_reduce_only.rs index fc21131c3..a5a41b184 100644 --- a/programs/mango-v4/tests/cases/test_reduce_only.rs +++ b/programs/mango-v4/tests/cases/test_reduce_only.rs @@ -364,6 +364,8 @@ async fn test_perp_reduce_only() -> Result<(), TransportError> { group, admin, perp_market, + reduce_only: true, + force_close: false, }, ) .await diff --git a/programs/mango-v4/tests/program_test/mango_client.rs b/programs/mango-v4/tests/program_test/mango_client.rs index 941db1140..fb8d08eb8 100644 --- a/programs/mango-v4/tests/program_test/mango_client.rs +++ b/programs/mango-v4/tests/program_test/mango_client.rs @@ -3006,6 +3006,7 @@ fn perp_edit_instruction_default() -> mango_v4::instruction::PerpEditMarket { reset_stable_price: false, positive_pnl_liquidation_fee_opt: None, name_opt: None, + force_close_opt: None, } } @@ -3092,6 +3093,8 @@ pub struct PerpMakeReduceOnly { pub group: Pubkey, pub admin: TestKeypair, pub perp_market: Pubkey, + pub reduce_only: bool, + pub force_close: bool, } #[async_trait::async_trait(?Send)] @@ -3107,7 +3110,8 @@ impl ClientInstruction for PerpMakeReduceOnly { let perp_market: PerpMarket = account_loader.load(&self.perp_market).await.unwrap(); let instruction = Self::Instruction { - reduce_only_opt: Some(true), + reduce_only_opt: Some(self.reduce_only), + force_close_opt: Some(self.force_close), ..perp_edit_instruction_default() }; @@ -3617,6 +3621,42 @@ impl ClientInstruction for PerpSettlePnlInstruction { } } +pub struct PerpForceClosePositionInstruction { + pub account_a: Pubkey, + pub account_b: Pubkey, + pub perp_market: Pubkey, +} +#[async_trait::async_trait(?Send)] +impl ClientInstruction for PerpForceClosePositionInstruction { + type Accounts = mango_v4::accounts::PerpForceClosePosition; + type Instruction = mango_v4::instruction::PerpForceClosePosition; + async fn to_instruction( + &self, + account_loader: impl ClientAccountLoader + 'async_trait, + ) -> (Self::Accounts, instruction::Instruction) { + let program_id = mango_v4::id(); + let instruction = Self::Instruction {}; + + let perp_market: PerpMarket = account_loader.load(&self.perp_market).await.unwrap(); + + let accounts = Self::Accounts { + group: perp_market.group, + perp_market: self.perp_market, + account_a: self.account_a, + account_b: self.account_b, + oracle: perp_market.oracle, + }; + + let instruction = make_instruction(program_id, &accounts, instruction); + + (accounts, instruction) + } + + fn signers(&self) -> Vec { + vec![] + } +} + pub struct PerpSettleFeesInstruction { pub account: Pubkey, pub perp_market: Pubkey, diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 8aa083516..3cb100183 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -1999,6 +1999,7 @@ export class MangoClient { params.resetStablePrice ?? false, params.positivePnlLiquidationFee, params.name, + params.forceClose, ) .accounts({ group: group.publicKey, diff --git a/ts/client/src/clientIxParamBuilder.ts b/ts/client/src/clientIxParamBuilder.ts index 673438346..8e559d4fb 100644 --- a/ts/client/src/clientIxParamBuilder.ts +++ b/ts/client/src/clientIxParamBuilder.ts @@ -86,6 +86,7 @@ export interface PerpEditParams { resetStablePrice: boolean | null; positivePnlLiquidationFee: number | null; name: string | null; + forceClose: boolean | null; } export const NullPerpEditParams: PerpEditParams = { @@ -118,6 +119,7 @@ export const NullPerpEditParams: PerpEditParams = { resetStablePrice: null, positivePnlLiquidationFee: null, name: null, + forceClose: null, }; // Use with TrueIxGateParams and buildIxGate @@ -175,6 +177,7 @@ export interface IxGateParams { TokenWithdraw: boolean; AccountBuybackFeesWithMngo: boolean; TokenForceCloseBorrowsWithToken: boolean; + PerpForceClosePosition: boolean; } // Default with all ixs enabled, use with buildIxGate @@ -232,6 +235,7 @@ export const TrueIxGateParams: IxGateParams = { TokenWithdraw: true, AccountBuybackFeesWithMngo: true, TokenForceCloseBorrowsWithToken: true, + PerpForceClosePosition: true, }; // build ix gate e.g. buildIxGate(Builder(TrueIxGateParams).TokenDeposit(false).build()).toNumber(), @@ -299,6 +303,8 @@ export function buildIxGate(p: IxGateParams): BN { toggleIx(ixGate, p, 'TokenWithdraw', 47); toggleIx(ixGate, p, 'AccountBuybackFeesWithMngo', 48); toggleIx(ixGate, p, 'TokenForceCloseBorrowsWithToken', 49); + toggleIx(ixGate, p, 'TokenForceCloseBorrowsWithToken', 49); + toggleIx(ixGate, p, 'PerpForceClosePosition', 49); return ixGate; } diff --git a/ts/client/src/mango_v4.ts b/ts/client/src/mango_v4.ts index b4f03f25f..136bc8ef4 100644 --- a/ts/client/src/mango_v4.ts +++ b/ts/client/src/mango_v4.ts @@ -3092,6 +3092,12 @@ export type MangoV4 = { "type": { "option": "string" } + }, + { + "name": "forceCloseOpt", + "type": { + "option": "bool" + } } ] }, @@ -3635,6 +3641,37 @@ export type MangoV4 = { ], "args": [] }, + { + "name": "perpForceClosePosition", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "perpMarket", + "isMut": true, + "isSigner": false + }, + { + "name": "accountA", + "isMut": true, + "isSigner": false + }, + { + "name": "accountB", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, { "name": "perpSettleFees", "accounts": [ @@ -5048,12 +5085,16 @@ export type MangoV4 = { ], "type": "u8" }, + { + "name": "forceClose", + "type": "u8" + }, { "name": "padding4", "type": { "array": [ "u8", - 7 + 6 ] } }, @@ -7065,6 +7106,9 @@ export type MangoV4 = { }, { "name": "TokenForceCloseBorrowsWithToken" + }, + { + "name": "PerpForceClosePosition" } ] } @@ -11896,6 +11940,12 @@ export const IDL: MangoV4 = { "type": { "option": "string" } + }, + { + "name": "forceCloseOpt", + "type": { + "option": "bool" + } } ] }, @@ -12439,6 +12489,37 @@ export const IDL: MangoV4 = { ], "args": [] }, + { + "name": "perpForceClosePosition", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "perpMarket", + "isMut": true, + "isSigner": false + }, + { + "name": "accountA", + "isMut": true, + "isSigner": false + }, + { + "name": "accountB", + "isMut": true, + "isSigner": false + }, + { + "name": "oracle", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, { "name": "perpSettleFees", "accounts": [ @@ -13852,12 +13933,16 @@ export const IDL: MangoV4 = { ], "type": "u8" }, + { + "name": "forceClose", + "type": "u8" + }, { "name": "padding4", "type": { "array": [ "u8", - 7 + 6 ] } }, @@ -15869,6 +15954,9 @@ export const IDL: MangoV4 = { }, { "name": "TokenForceCloseBorrowsWithToken" + }, + { + "name": "PerpForceClosePosition" } ] }