From 9f5a2fd32e11c44a872f01a48e5c96292771e79d Mon Sep 17 00:00:00 2001 From: microwavedcola1 Date: Thu, 5 May 2022 10:25:32 +0200 Subject: [PATCH] add a test for consume events, add debug impl for mango account for easy debugging in tests Signed-off-by: microwavedcola1 --- programs/mango-v4/src/state/mango_account.rs | 106 ++++++++++++++++++ programs/mango-v4/src/state/perp_market.rs | 9 ++ .../tests/program_test/mango_client.rs | 38 +++++++ programs/mango-v4/tests/test_perp.rs | 84 ++++++++++++-- 4 files changed, 228 insertions(+), 9 deletions(-) diff --git a/programs/mango-v4/src/state/mango_account.rs b/programs/mango-v4/src/state/mango_account.rs index febe8a299..790535e5c 100644 --- a/programs/mango-v4/src/state/mango_account.rs +++ b/programs/mango-v4/src/state/mango_account.rs @@ -23,6 +23,7 @@ pub const MAX_PERP_OPEN_ORDERS: usize = 8; pub const FREE_ORDER_SLOT: PerpMarketIndex = PerpMarketIndex::MAX; #[zero_copy] +#[derive(Debug)] pub struct TokenAccount { // TODO: Why did we have deposits and borrows as two different values // if only one of them was allowed to be != 0 at a time? @@ -75,6 +76,21 @@ const_assert_eq!( ); const_assert_eq!(size_of::() % 8, 0); +impl std::fmt::Debug for MangoAccountTokens { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MangoAccountTokens") + .field( + "values", + &self + .values + .iter() + .filter(|value| value.is_active()) + .collect::>(), + ) + .finish() + } +} + impl Default for MangoAccountTokens { fn default() -> Self { Self::new() @@ -153,6 +169,7 @@ impl MangoAccountTokens { } #[zero_copy] +#[derive(Debug)] pub struct Serum3Account { pub open_orders: Pubkey, @@ -201,6 +218,21 @@ const_assert_eq!( ); const_assert_eq!(size_of::() % 8, 0); +impl std::fmt::Debug for MangoAccountSerum3 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MangoAccountSerum3") + .field( + "values", + &self + .values + .iter() + .filter(|value| value.is_active()) + .collect::>(), + ) + .finish() + } +} + impl Default for MangoAccountSerum3 { fn default() -> Self { Self::new() @@ -245,6 +277,7 @@ impl MangoAccountSerum3 { } #[zero_copy] +#[derive(Debug)] pub struct PerpAccount { pub market_index: PerpMarketIndex, pub reserved: [u8; 6], @@ -341,6 +374,55 @@ const_assert_eq!( ); const_assert_eq!(size_of::() % 8, 0); +impl std::fmt::Debug for MangoAccountPerps { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MangoAccountPerps") + .field( + "accounts", + &self + .accounts + .iter() + .filter(|value| value.is_active()) + .collect::>(), + ) + .field( + "order_market", + &self + .order_market + .iter() + .filter(|value| **value != PerpMarketIndex::MAX) + .collect::>(), + ) + .field( + "order_side", + &self + .order_side + .iter() + .zip(self.order_id) + .filter(|value| value.1 != 0) + .map(|value| value.0) + .collect::>(), + ) + .field( + "order_id", + &self + .order_id + .iter() + .filter(|value| **value != 0) + .collect::>(), + ) + .field( + "order_client_id", + &self + .order_client_id + .iter() + .filter(|value| **value != 0) + .collect::>(), + ) + .finish() + } +} + impl MangoAccountPerps { pub fn new() -> Self { Self { @@ -553,6 +635,30 @@ const_assert_eq!( ); const_assert_eq!(size_of::() % 8, 0); +impl std::fmt::Debug for MangoAccount { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MangoAccount") + .field( + "name", + &std::str::from_utf8(&self.name) + .unwrap() + .trim_matches(char::from(0)), + ) + .field("group", &self.group) + .field("owner", &self.owner) + .field("delegate", &self.delegate) + .field("tokens", &self.tokens) + .field("serum3", &self.serum3) + .field("perps", &self.perps) + .field("being_liquidated", &self.being_liquidated) + .field("is_bankrupt", &self.is_bankrupt) + .field("account_num", &self.account_num) + .field("bump", &self.bump) + .field("reserved", &self.reserved) + .finish() + } +} + #[macro_export] macro_rules! account_seeds { ( $account:expr ) => { diff --git a/programs/mango-v4/src/state/perp_market.rs b/programs/mango-v4/src/state/perp_market.rs index 1fc1e3abe..a45c1b209 100644 --- a/programs/mango-v4/src/state/perp_market.rs +++ b/programs/mango-v4/src/state/perp_market.rs @@ -90,6 +90,15 @@ impl PerpMarket { .unwrap() } + pub fn native_price_to_lot(&self, price: I80F48) -> i64 { + price + .checked_mul(I80F48::from_num(self.base_lot_size)) + .unwrap() + .checked_div(I80F48::from_num(self.quote_lot_size)) + .unwrap() + .to_num() + } + /// Is `native_price` an acceptable order for the `side` of this market, given `oracle_price`? pub fn inside_price_limit( &self, diff --git a/programs/mango-v4/tests/program_test/mango_client.rs b/programs/mango-v4/tests/program_test/mango_client.rs index e3deb321e..1f2c11d70 100644 --- a/programs/mango-v4/tests/program_test/mango_client.rs +++ b/programs/mango-v4/tests/program_test/mango_client.rs @@ -1390,6 +1390,44 @@ impl<'keypair> ClientInstruction for PerpPlaceOrderInstruction<'keypair> { vec![self.owner] } } +pub struct PerpConsumeEventsInstruction { + pub group: Pubkey, + pub perp_market: Pubkey, + pub event_queue: Pubkey, + pub mango_accounts: Vec, +} +#[async_trait::async_trait(?Send)] +impl ClientInstruction for PerpConsumeEventsInstruction { + type Accounts = mango_v4::accounts::PerpConsumeEvents; + type Instruction = mango_v4::instruction::PerpConsumeEvents; + async fn to_instruction( + &self, + _loader: impl ClientAccountLoader + 'async_trait, + ) -> (Self::Accounts, instruction::Instruction) { + let program_id = mango_v4::id(); + let instruction = Self::Instruction { limit: 10 }; + let accounts = Self::Accounts { + group: self.group, + perp_market: self.perp_market, + event_queue: self.event_queue, + }; + + let mut instruction = make_instruction(program_id, &accounts, instruction); + instruction + .accounts + .extend(self.mango_accounts.iter().map(|ma| AccountMeta { + pubkey: *ma, + is_signer: false, + is_writable: true, + })); + (accounts, instruction) + } + + fn signers(&self) -> Vec<&Keypair> { + vec![] + } +} + pub struct BenchmarkInstruction {} #[async_trait::async_trait(?Send)] impl ClientInstruction for BenchmarkInstruction { diff --git a/programs/mango-v4/tests/test_perp.rs b/programs/mango-v4/tests/test_perp.rs index 95085f18b..ae2615922 100644 --- a/programs/mango-v4/tests/test_perp.rs +++ b/programs/mango-v4/tests/test_perp.rs @@ -1,11 +1,11 @@ #![cfg(all(feature = "test-bpf"))] +use fixed_macro::types::I80F48; use mango_v4::state::*; +use program_test::*; use solana_program_test::*; use solana_sdk::signature::Keypair; -use program_test::*; - mod program_test; #[tokio::test] @@ -31,7 +31,7 @@ async fn test_perp() -> Result<(), BanksClientError> { .create(solana) .await; - let account = send_tx( + let account_0 = send_tx( solana, CreateAccountInstruction { account_num: 0, @@ -44,6 +44,19 @@ async fn test_perp() -> Result<(), BanksClientError> { .unwrap() .account; + let account_1 = send_tx( + solana, + CreateAccountInstruction { + account_num: 1, + group, + owner, + payer, + }, + ) + .await + .unwrap() + .account; + // // SETUP: Deposit user funds // @@ -54,7 +67,7 @@ async fn test_perp() -> Result<(), BanksClientError> { solana, DepositInstruction { amount: deposit_amount, - account, + account: account_0, token_account: payer_mint_accounts[0], token_authority: payer, }, @@ -66,7 +79,35 @@ async fn test_perp() -> Result<(), BanksClientError> { solana, DepositInstruction { amount: deposit_amount, - account, + account: account_0, + token_account: payer_mint_accounts[1], + token_authority: payer, + }, + ) + .await + .unwrap(); + } + + { + let deposit_amount = 1000; + + send_tx( + solana, + DepositInstruction { + amount: deposit_amount, + account: account_1, + token_account: payer_mint_accounts[0], + token_authority: payer, + }, + ) + .await + .unwrap(); + + send_tx( + solana, + DepositInstruction { + amount: deposit_amount, + account: account_1, token_account: payer_mint_accounts[1], token_authority: payer, }, @@ -122,11 +163,16 @@ async fn test_perp() -> Result<(), BanksClientError> { .await .unwrap(); + let price_lots = { + let perp_market = solana.get_account::(perp_market).await; + perp_market.native_price_to_lot(I80F48!(1)) + }; + send_tx( solana, PerpPlaceOrderInstruction { group, - account, + account: account_0, perp_market, asks, bids, @@ -134,7 +180,7 @@ async fn test_perp() -> Result<(), BanksClientError> { oracle: tokens[0].oracle, owner, side: Side::Bid, - price_lots: 1, + price_lots, max_base_lots: 1, max_quote_lots: i64::MAX, }, @@ -146,7 +192,7 @@ async fn test_perp() -> Result<(), BanksClientError> { solana, PerpPlaceOrderInstruction { group, - account, + account: account_1, perp_market, asks, bids, @@ -154,7 +200,7 @@ async fn test_perp() -> Result<(), BanksClientError> { oracle: tokens[0].oracle, owner, side: Side::Ask, - price_lots: 1, + price_lots, max_base_lots: 1, max_quote_lots: i64::MAX, }, @@ -162,5 +208,25 @@ async fn test_perp() -> Result<(), BanksClientError> { .await .unwrap(); + send_tx( + solana, + PerpConsumeEventsInstruction { + group, + perp_market, + event_queue, + 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.accounts[0].base_position_lots, 1); + assert!(mango_account_0.perps.accounts[0].quote_position_native < -100.019); + + let mango_account_1 = solana.get_account::(account_1).await; + assert_eq!(mango_account_1.perps.accounts[0].base_position_lots, -1); + assert_eq!(mango_account_1.perps.accounts[0].quote_position_native, 100); + Ok(()) }