mango-v3/program/tests/program_test/assertions.rs

296 lines
11 KiB
Rust

use crate::*;
use fixed::types::I80F48;
use fixed_macro::types::I80F48;
use mango::state::*;
use solana_program::pubkey::Pubkey;
use std::collections::HashMap;
// Test equality within an epsilon for I80F48 or float
#[allow(dead_code)]
pub const EPSILON: I80F48 = I80F48!(0.001);
#[macro_export]
macro_rules! assert_approx_eq {
($a:expr, $b:expr) => {{
let (a, b) = (&$a, &$b);
assert!(
(*a - *b).abs() < EPSILON,
"assertion failed: `(left !== right)` \
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
*a,
*b,
EPSILON,
(*a - *b).abs()
);
}};
($a:expr, $b:expr, $eps:expr) => {{
let (a, b) = (&$a, &$b);
let eps = $eps;
assert!(
(*a - *b).abs() < eps,
"assertion failed: `(left !== right)` \
(left: `{:?}`, right: `{:?}`, expect diff: `{:?}`, real diff: `{:?}`)",
*a,
*b,
eps,
(*a - *b).abs()
);
}};
}
#[allow(dead_code)]
pub fn assert_deposits(
mango_group_cookie: &MangoGroupCookie,
expected_values: (usize, HashMap<usize, I80F48>),
) {
let (user_index, expected_value) = expected_values;
for (mint_index, expected_deposit) in expected_value.iter() {
let actual_deposit = &mango_group_cookie.mango_accounts[user_index]
.mango_account
.get_native_deposit(
&mango_group_cookie.mango_cache.root_bank_cache[*mint_index],
*mint_index,
)
.unwrap();
println!(
"==\nUser: {}, Mint: {}\nExpected deposit: {}, Actual deposit: {}\n==",
user_index,
mint_index,
expected_deposit.to_string(),
actual_deposit.to_string(),
);
assert!(expected_deposit == actual_deposit);
}
}
#[allow(dead_code)]
pub fn assert_deposits_approx(
mango_group_cookie: &MangoGroupCookie,
expected_values: (usize, HashMap<usize, I80F48>),
epsilon: I80F48,
) {
let (user_index, expected_value) = expected_values;
for (mint_index, expected_deposit) in expected_value.iter() {
let actual_deposit = &mango_group_cookie.mango_accounts[user_index]
.mango_account
.get_native_deposit(
&mango_group_cookie.mango_cache.root_bank_cache[*mint_index],
*mint_index,
)
.unwrap();
println!(
"==\nUser: {}, Mint: {}\nExpected deposit: {}, Actual deposit: {}\n==",
user_index,
mint_index,
expected_deposit.to_string(),
actual_deposit.to_string(),
);
assert_approx_eq!(expected_deposit, actual_deposit, epsilon);
}
}
#[allow(dead_code)]
pub fn assert_open_spot_orders(
mango_group_cookie: &MangoGroupCookie,
user_spot_orders: &Vec<(usize, usize, serum_dex::matching::Side, f64, f64)>,
) {
for i in 0..user_spot_orders.len() {
let (user_index, mint_index, _, _, _) = user_spot_orders[i];
let mango_account = mango_group_cookie.mango_accounts[user_index].mango_account;
assert_ne!(mango_account.spot_open_orders[mint_index], Pubkey::default());
}
}
#[allow(dead_code)]
pub async fn assert_user_spot_orders(
test: &mut MangoProgramTest,
mango_group_cookie: &MangoGroupCookie,
expected_values: (usize, usize, HashMap<&str, I80F48>),
) {
let (mint_index, user_index, expected_value) = expected_values;
let (actual_quote_free, actual_quote_locked, actual_base_free, actual_base_locked) =
test.get_oo_info(&mango_group_cookie, user_index, mint_index).await;
println!(
"User index: {} quote_free {} quote_locked {} base_free {} base_locked {}",
user_index,
actual_quote_free.to_string(),
actual_quote_locked.to_string(),
actual_base_free.to_string(),
actual_base_locked.to_string()
);
if let Some(quote_free) = expected_value.get("quote_free") {
// println!(
// "==\nUser: {}, Mint: {}\nExpected quote_free: {}, Actual quote_free: {}\n==",
// user_index,
// mint_index,
// quote_free.to_string(),
// actual_quote_free.to_string(),
// );
assert!(*quote_free == actual_quote_free);
}
if let Some(quote_locked) = expected_value.get("quote_locked") {
// println!(
// "==\nUser: {}, Mint: {}\nExpected quote_locked: {}, Actual quote_locked: {}\n==",
// user_index,
// mint_index,
// quote_locked.to_string(),
// actual_quote_locked.to_string(),
// );
assert!(*quote_locked == actual_quote_locked);
}
if let Some(base_free) = expected_value.get("base_free") {
println!(
"==\nUser: {}, Mint: {}\nExpected base_free: {}, Actual base_free: {}\n==",
user_index,
mint_index,
base_free.to_string(),
actual_base_free.to_string(),
);
assert!(*base_free == actual_base_free);
}
if let Some(base_locked) = expected_value.get("base_locked") {
println!(
"==\nUser: {}, Mint: {}\nExpected base_locked: {}, Actual base_locked: {}\n==",
user_index,
mint_index,
base_locked.to_string(),
actual_base_locked.to_string(),
);
assert!(*base_locked == actual_base_locked);
}
}
// #[allow(dead_code)]
// pub fn assert_matched_spot_orders(
// mango_group_cookie: &MangoGroupCookie,
// user_spot_orders: &Vec<(usize, usize, serum_dex::matching::Side, f64, f64)>,
// ) {
// let mut balances_map: HashMap<String, (f64, f64)> = HashMap::new();
// for i in 0..user_spot_orders.len() {
// let (user_index, _, arranged_order_side, arranged_order_size, arranged_order_price) = user_spot_orders[i];
// let balances_map_key = format!("{}", user_index);
// let sign = match arranged_order_side {
// serum_dex::matching::Side::Bid => 1.0,
// serum_dex::matching::Side::Ask => -1.0,
// }
// if let Some((base_balance, quote_balance)) = balances_map.get_mut(&balances_map_key) {
// *base_balance += arranged_order_size * arranged_order_price * sign;
// *quote_balance += arranged_order_size * arranged_order_price * (sign * -1.0);
// } else {
// perp_orders_map.insert(perp_orders_map_key.clone(), 0);
// }
// }
// }
#[allow(dead_code)]
pub fn assert_open_perp_orders(
mango_group_cookie: &MangoGroupCookie,
user_perp_orders: &Vec<(usize, usize, mango::matching::Side, f64, f64)>,
starting_order_id: u64,
) {
let mut perp_orders_map: HashMap<String, usize> = HashMap::new();
for i in 0..user_perp_orders.len() {
let (user_index, _, arranged_order_side, _, _) = user_perp_orders[i];
let perp_orders_map_key = format!("{}", user_index);
if let Some(x) = perp_orders_map.get_mut(&perp_orders_map_key) {
*x += 1;
} else {
perp_orders_map.insert(perp_orders_map_key.clone(), 0);
}
let mango_account = mango_group_cookie.mango_accounts[user_index].mango_account;
let client_order_id = mango_account.client_order_ids[perp_orders_map[&perp_orders_map_key]];
let order_side = mango_account.order_side[perp_orders_map[&perp_orders_map_key]];
assert_eq!(client_order_id, starting_order_id + i as u64,);
assert_eq!(order_side, arranged_order_side);
}
}
// #[allow(dead_code)]
// pub fn assert_matched_perp_orders(
// test: &mut MangoProgramTest,
// mango_group_cookie: &MangoGroupCookie,
// user_perp_orders: &Vec<(usize, usize, mango::matching::Side, f64, f64)>,
// ) {
// let mut matched_perp_orders_map: HashMap<String, I80F48> = HashMap::new();
// let (_, _, _, maker_side, _) = user_perp_orders[0];
// for i in 0..user_perp_orders.len() {
// let (user_index, mint_index, arranged_order_side, arranged_order_size, arranged_order_price) = user_perp_orders[i];
// let mango_group = mango_group_cookie.mango_group;
// let perp_market_info = mango_group.perp_markets[mint_index];
//
// let mint = test.with_mint(mint_index);
//
// let order_size = test.base_size_number_to_lots(&self.mint, arranged_order_size);
// let order_price = test.price_number_to_lots(&self.mint, arranged_order_price);
//
// let mut taker = None;
// let mut base_position: I80F48;
// let mut quote_position: I80F48;
//
// let fee = maker_side
//
// if arranged_order_side == mango::matching::Side::Bid {
// base_position = order_size;
// quote_position = -order_size * order_price - (order_size * order_price * perp_market_info.maker_fee);
// } else {
// base_position = -order_size;
// quote_position = order_size * order_price - (order_size * order_price * perp_market_info.taker_fee);
// }
//
// let perp_orders_map_key = format!("{}_{}", user_index, mint_index);
//
// if let Some(x) = perp_orders_map.get_mut(&perp_orders_map_key) {
//
// *x += 1;
// } else {
// perp_orders_map.insert(perp_orders_map_key.clone(), 0);
// }
// }
// }
fn get_net(mango_account: &MangoAccount, bank_cache: &RootBankCache, mint_index: usize) -> I80F48 {
if mango_account.deposits[mint_index].is_positive() {
mango_account.deposits[mint_index].checked_mul(bank_cache.deposit_index).unwrap()
} else if mango_account.borrows[mint_index].is_positive() {
-mango_account.borrows[mint_index].checked_mul(bank_cache.borrow_index).unwrap()
} else {
ZERO_I80F48
}
}
#[allow(dead_code)]
pub async fn assert_vault_net_deposit_diff(
test: &mut MangoProgramTest,
mango_group_cookie: &MangoGroupCookie,
mint_index: usize,
) {
let mango_cache = mango_group_cookie.mango_cache;
let root_bank_cache = mango_cache.root_bank_cache[mint_index];
let (_root_bank_pk, root_bank) =
test.with_root_bank(&mango_group_cookie.mango_group, mint_index).await;
let mut total_net = ZERO_I80F48;
for mango_account in &mango_group_cookie.mango_accounts {
total_net += get_net(&mango_account.mango_account, &root_bank_cache, mint_index);
}
total_net = total_net.checked_round().unwrap();
let mut vault_amount = ZERO_I80F48;
for node_bank_pk in root_bank.node_banks {
if node_bank_pk != Pubkey::default() {
let node_bank = test.load_account::<NodeBank>(node_bank_pk).await;
let balance = test.get_token_balance(node_bank.vault).await;
vault_amount += I80F48::from_num(balance);
}
}
println!("total_net: {}", total_net.to_string());
println!("vault_amount: {}", vault_amount.to_string());
assert!(total_net == vault_amount);
}