Tests: initial perp force-cancel test
This commit is contained in:
parent
810998a2af
commit
2dfada7377
|
@ -34,7 +34,6 @@ pub fn perp_liq_force_cancel_orders(
|
|||
// Check liqee health if liquidation is allowed
|
||||
//
|
||||
let mut health_cache = {
|
||||
let mut account = ctx.accounts.account.load_mut()?;
|
||||
let retriever =
|
||||
new_fixed_order_account_retriever(ctx.remaining_accounts, &account.borrow())?;
|
||||
let health_cache =
|
||||
|
|
|
@ -514,8 +514,11 @@ pub mod mango_v4 {
|
|||
instructions::perp_liq_base_position(ctx, max_base_transfer)
|
||||
}
|
||||
|
||||
pub fn perp_liq_force_cancel_orders(ctx: Context<PerpLiqForceCancelOrders>) -> Result<()> {
|
||||
instructions::perp_liq_force_cancel_orders(ctx)
|
||||
pub fn perp_liq_force_cancel_orders(
|
||||
ctx: Context<PerpLiqForceCancelOrders>,
|
||||
limit: u8,
|
||||
) -> Result<()> {
|
||||
instructions::perp_liq_force_cancel_orders(ctx, limit)
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
|
|
@ -2618,6 +2618,53 @@ impl ClientInstruction for PerpSettleFeesInstruction {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct PerpLiqForceCancelOrdersInstruction {
|
||||
pub account: Pubkey,
|
||||
pub perp_market: Pubkey,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ClientInstruction for PerpLiqForceCancelOrdersInstruction {
|
||||
type Accounts = mango_v4::accounts::PerpLiqForceCancelOrders;
|
||||
type Instruction = mango_v4::instruction::PerpLiqForceCancelOrders;
|
||||
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 { limit: 10 };
|
||||
|
||||
let perp_market: PerpMarket = account_loader.load(&self.perp_market).await.unwrap();
|
||||
let account = account_loader
|
||||
.load_mango_account(&self.account)
|
||||
.await
|
||||
.unwrap();
|
||||
let health_check_metas = derive_health_check_remaining_account_metas(
|
||||
&account_loader,
|
||||
&account,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
let accounts = Self::Accounts {
|
||||
group: account.fixed.group,
|
||||
perp_market: self.perp_market,
|
||||
account: self.account,
|
||||
bids: perp_market.bids,
|
||||
asks: perp_market.asks,
|
||||
};
|
||||
let mut instruction = make_instruction(program_id, &accounts, instruction);
|
||||
instruction.accounts.extend(health_check_metas);
|
||||
|
||||
(accounts, instruction)
|
||||
}
|
||||
|
||||
fn signers(&self) -> Vec<TestKeypair> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BenchmarkInstruction {}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ClientInstruction for BenchmarkInstruction {
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
#![cfg(feature = "test-bpf")]
|
||||
|
||||
use fixed::types::I80F48;
|
||||
use solana_program_test::*;
|
||||
use solana_sdk::transport::TransportError;
|
||||
|
||||
use mango_v4::state::*;
|
||||
use program_test::*;
|
||||
|
||||
use mango_setup::*;
|
||||
|
||||
mod program_test;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_liq_perps_force_cancel() -> Result<(), TransportError> {
|
||||
let test_builder = TestContextBuilder::new();
|
||||
let context = test_builder.start_default().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];
|
||||
let payer_mint_accounts = &context.users[1].token_accounts[0..2];
|
||||
|
||||
//
|
||||
// SETUP: Create a group and an account to fill the vaults
|
||||
//
|
||||
|
||||
let GroupWithTokens { group, tokens, .. } = GroupWithTokensConfig {
|
||||
admin,
|
||||
payer,
|
||||
mints,
|
||||
}
|
||||
.create(solana)
|
||||
.await;
|
||||
//let quote_token = &tokens[0];
|
||||
let base_token = &tokens[1];
|
||||
|
||||
// deposit some funds, to the vaults aren't empty
|
||||
create_funded_account(&solana, group, owner, 0, &context.users[1], mints, 10000, 0).await;
|
||||
|
||||
//
|
||||
// TEST: Create a perp market
|
||||
//
|
||||
let mango_v4::accounts::PerpCreateMarket {
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
..
|
||||
} = send_tx(
|
||||
solana,
|
||||
PerpCreateMarketInstruction {
|
||||
group,
|
||||
admin,
|
||||
oracle: base_token.oracle,
|
||||
asks: context
|
||||
.solana
|
||||
.create_account_for_type::<BookSide>(&mango_v4::id())
|
||||
.await,
|
||||
bids: context
|
||||
.solana
|
||||
.create_account_for_type::<BookSide>(&mango_v4::id())
|
||||
.await,
|
||||
event_queue: {
|
||||
context
|
||||
.solana
|
||||
.create_account_for_type::<EventQueue>(&mango_v4::id())
|
||||
.await
|
||||
},
|
||||
payer,
|
||||
perp_market_index: 0,
|
||||
base_token_index: base_token.index,
|
||||
base_token_decimals: base_token.mint.decimals,
|
||||
quote_lot_size: 10,
|
||||
base_lot_size: 100,
|
||||
maint_asset_weight: 0.8,
|
||||
init_asset_weight: 0.6,
|
||||
maint_liab_weight: 1.2,
|
||||
init_liab_weight: 1.4,
|
||||
liquidation_fee: 0.05,
|
||||
maker_fee: 0.0,
|
||||
taker_fee: 0.0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let price_lots = {
|
||||
let perp_market = solana.get_account::<PerpMarket>(perp_market).await;
|
||||
perp_market.native_price_to_lot(I80F48::ONE)
|
||||
};
|
||||
|
||||
//
|
||||
// SETUP: Make an account and deposit some quote and base
|
||||
//
|
||||
let deposit_amount = 1000;
|
||||
let account = create_funded_account(
|
||||
&solana,
|
||||
group,
|
||||
owner,
|
||||
1,
|
||||
&context.users[1],
|
||||
&mints[0..1],
|
||||
deposit_amount,
|
||||
0,
|
||||
)
|
||||
.await;
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
TokenDepositInstruction {
|
||||
amount: 1,
|
||||
account,
|
||||
token_account: payer_mint_accounts[1],
|
||||
token_authority: payer,
|
||||
bank_index: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//
|
||||
// SETUP: Place a perp order
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
group,
|
||||
account,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: base_token.oracle,
|
||||
owner,
|
||||
side: Side::Ask,
|
||||
price_lots,
|
||||
// health was 1000 * 0.6 = 600; this order is -14*100*(1.4-1) = -560
|
||||
max_base_lots: 14,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
//
|
||||
// SETUP: Change the oracle to make health go negative
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
StubOracleSetInstruction {
|
||||
group,
|
||||
admin,
|
||||
mint: base_token.mint.pubkey,
|
||||
payer,
|
||||
price: "10.0",
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// verify health is bad: can't withdraw
|
||||
assert!(send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
amount: 1,
|
||||
allow_borrow: false,
|
||||
account,
|
||||
owner,
|
||||
token_account: payer_mint_accounts[1],
|
||||
bank_index: 0,
|
||||
}
|
||||
)
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
//
|
||||
// TEST: force cancel orders, making the account healthy again
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpLiqForceCancelOrdersInstruction {
|
||||
account,
|
||||
perp_market,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// can withdraw again
|
||||
send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
amount: 1,
|
||||
allow_borrow: false,
|
||||
account,
|
||||
owner,
|
||||
token_account: payer_mint_accounts[1],
|
||||
bank_index: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue