Perp force close positions in a market (#525)
* force close tokens Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * add test Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * reset Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * force close perp market Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * rename Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * test Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * update Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * add back staleness slot check Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> * Fixes from review Signed-off-by: microwavedcola1 <microwavedcola@gmail.com> --------- Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
55bfcc3a76
commit
6ac9f19287
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>,
|
||||
}
|
|
@ -66,6 +66,7 @@ pub fn ix_gate_set(ctx: Context<IxGateSet>, 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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -38,6 +38,7 @@ pub fn perp_edit_market(
|
|||
reset_stable_price: bool,
|
||||
positive_pnl_liquidation_fee_opt: Option<f32>,
|
||||
name_opt: Option<String>,
|
||||
force_close_opt: Option<bool>,
|
||||
) -> 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!(
|
||||
|
|
|
@ -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<PerpForceClosePosition>) -> 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(())
|
||||
}
|
|
@ -677,6 +677,7 @@ pub mod mango_v4 {
|
|||
reset_stable_price: bool,
|
||||
positive_pnl_liquidation_fee_opt: Option<f32>,
|
||||
name_opt: Option<String>,
|
||||
force_close_opt: Option<bool>,
|
||||
) -> 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<PerpForceClosePosition>) -> Result<()> {
|
||||
#[cfg(feature = "enable-gpl")]
|
||||
instructions::perp_force_close_position(ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: u64) -> Result<()> {
|
||||
#[cfg(feature = "enable-gpl")]
|
||||
instructions::perp_settle_fees(ctx, max_settle_amount)?;
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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::<PerpMarket>(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::<MangoAccount>(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::<MangoAccount>(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::<MangoAccount>(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::<MangoAccount>(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(())
|
||||
}
|
||||
|
|
|
@ -364,6 +364,8 @@ async fn test_perp_reduce_only() -> Result<(), TransportError> {
|
|||
group,
|
||||
admin,
|
||||
perp_market,
|
||||
reduce_only: true,
|
||||
force_close: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -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<TestKeypair> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerpSettleFeesInstruction {
|
||||
pub account: Pubkey,
|
||||
pub perp_market: Pubkey,
|
||||
|
|
|
@ -1999,6 +1999,7 @@ export class MangoClient {
|
|||
params.resetStablePrice ?? false,
|
||||
params.positivePnlLiquidationFee,
|
||||
params.name,
|
||||
params.forceClose,
|
||||
)
|
||||
.accounts({
|
||||
group: group.publicKey,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue