Tests: initial perp force-cancel test

This commit is contained in:
Christian Kamm 2022-09-08 13:10:24 +02:00
parent 810998a2af
commit 2dfada7377
4 changed files with 261 additions and 3 deletions

View File

@ -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 =

View File

@ -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

View File

@ -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 {

View File

@ -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(())
}