diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index 43bc8420e..4fbabefb0 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -29,6 +29,7 @@ pub use perp_place_order::*; pub use perp_settle_fees::*; pub use perp_settle_pnl::*; pub use perp_update_funding::*; +pub use perp_zero_out::*; pub use serum3_cancel_all_orders::*; pub use serum3_cancel_order::*; pub use serum3_close_open_orders::*; @@ -84,6 +85,7 @@ mod perp_place_order; mod perp_settle_fees; mod perp_settle_pnl; mod perp_update_funding; +mod perp_zero_out; mod serum3_cancel_all_orders; mod serum3_cancel_order; mod serum3_close_open_orders; diff --git a/programs/mango-v4/src/instructions/perp_zero_out.rs b/programs/mango-v4/src/instructions/perp_zero_out.rs new file mode 100644 index 000000000..e93b03579 --- /dev/null +++ b/programs/mango-v4/src/instructions/perp_zero_out.rs @@ -0,0 +1,48 @@ +use anchor_lang::prelude::*; + +use crate::error::*; +use crate::state::*; + +#[derive(Accounts)] +pub struct PerpZeroOutForMarket<'info> { + #[account( + has_one = admin, + constraint = group.load()?.is_operational() @ MangoError::GroupIsHalted, + constraint = group.load()?.is_testing() + )] + pub group: AccountLoader<'info, Group>, + + #[account( + mut, + has_one = group, + constraint = account.load()?.is_operational() @ MangoError::AccountIsFrozen + )] + pub account: AccountLoader<'info, MangoAccountFixed>, + + #[account( + has_one = group, + constraint = perp_market.load()?.perp_market_index == 1 + )] + pub perp_market: AccountLoader<'info, PerpMarket>, + + pub admin: Signer<'info>, +} + +pub fn perp_zero_out_for_market(ctx: Context) -> Result<()> { + let mut account = ctx.accounts.account.load_full_mut()?; + + let perp_market = ctx.accounts.perp_market.load()?; + + let perp_position = account.perp_position_mut(perp_market.perp_market_index)?; + *perp_position = PerpPosition::default(); + + for i in 0..account.header.perp_oo_count() { + let oo = account.perp_order_mut_by_raw_index(i); + if !oo.is_active_for_market(perp_market.perp_market_index) { + continue; + } + *oo = PerpOpenOrder::default(); + } + + Ok(()) +} diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index aaa648cec..4065269a1 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -557,6 +557,10 @@ pub mod mango_v4 { instructions::perp_deactivate_position(ctx) } + pub fn perp_zero_out_for_market(ctx: Context) -> Result<()> { + instructions::perp_zero_out_for_market(ctx) + } + #[allow(clippy::too_many_arguments)] pub fn perp_place_order( ctx: Context, diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 8f81cbfb7..ff389b268 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -1900,6 +1900,23 @@ export class MangoClient { ); } + public async perpZeroOutForMarket( + group: Group, + mangoAccount: MangoAccount, + perpMarketIndex: PerpMarketIndex, + ): Promise { + const perpMarket = group.getPerpMarketByMarketIndex(perpMarketIndex); + return await this.program.methods + .perpZeroOutForMarket() + .accounts({ + group: group.publicKey, + account: mangoAccount.publicKey, + perpMarket: perpMarket.publicKey, + admin: group.admin, + }) + .rpc(); + } + public async perpPlaceOrder( group: Group, mangoAccount: MangoAccount, diff --git a/ts/client/src/mango_v4.ts b/ts/client/src/mango_v4.ts index 7ec1342ff..98f0c103e 100644 --- a/ts/client/src/mango_v4.ts +++ b/ts/client/src/mango_v4.ts @@ -2858,6 +2858,32 @@ export type MangoV4 = { ], "args": [] }, + { + "name": "perpZeroOutForMarket", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "perpMarket", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, { "name": "perpPlaceOrder", "accounts": [ @@ -10748,6 +10774,32 @@ export const IDL: MangoV4 = { ], "args": [] }, + { + "name": "perpZeroOutForMarket", + "accounts": [ + { + "name": "group", + "isMut": false, + "isSigner": false + }, + { + "name": "account", + "isMut": true, + "isSigner": false + }, + { + "name": "perpMarket", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, { "name": "perpPlaceOrder", "accounts": [