Health: Add soft deposit and borrow limits

This commit is contained in:
Christian Kamm 2022-11-29 09:47:03 +01:00
parent 512eef96ea
commit cf34a5b4b7
15 changed files with 325 additions and 54 deletions

View File

@ -81,6 +81,39 @@ pub fn division_i80f48_f64(a: I80F48, b: I80F48) -> I80F48 {
r r
} }
#[inline(never)]
pub fn mul_f64(a: f64, b: f64) -> f64 {
msg!("mul_f64");
sol_log_compute_units();
let r = a * b;
if r.is_nan() {
panic!("nan"); // here as a side-effect to avoid reordering
}
sol_log_compute_units();
r
}
#[inline(never)]
pub fn mul_i80f48(a: I80F48, b: I80F48) -> I80F48 {
msg!("mul_i80f48");
sol_log_compute_units();
let r = a.checked_mul(b).unwrap();
sol_log_compute_units();
r
}
#[inline(never)]
pub fn i80f48_to_f64(a: I80F48) -> f64 {
msg!("i80f48_to_f64");
sol_log_compute_units();
let r = a.to_num::<f64>();
if r.is_nan() {
panic!("nan"); // here as a side-effect to avoid reordering
}
sol_log_compute_units();
r
}
pub fn benchmark(_ctx: Context<Benchmark>) -> Result<()> { pub fn benchmark(_ctx: Context<Benchmark>) -> Result<()> {
// 101000 // 101000
// 477 // 477
@ -98,6 +131,8 @@ pub fn benchmark(_ctx: Context<Benchmark>) -> Result<()> {
division_i128(a.to_bits(), b.to_bits()); // 100 - 2000 CU division_i128(a.to_bits(), b.to_bits()); // 100 - 2000 CU
division_i80f48_30bit(a, b); // 300 CU division_i80f48_30bit(a, b); // 300 CU
division_i80f48_f64(a, b); // 500 CU division_i80f48_f64(a, b); // 500 CU
mul_i80f48(a >> 64, b >> 64); // 100 CU
i80f48_to_f64(a); // 50 CU
} }
{ {
@ -126,6 +161,12 @@ pub fn benchmark(_ctx: Context<Benchmark>) -> Result<()> {
division_u32(a, b); // 20 CU division_u32(a, b); // 20 CU
} }
{
let a = clock.slot as f64;
let b = clock.unix_timestamp as f64;
mul_f64(a, b); // 0 CU??
}
sol_log_compute_units(); // 100321 -> 101 sol_log_compute_units(); // 100321 -> 101
msg!("msg!"); // 100079+101 -> 203 msg!("msg!"); // 100079+101 -> 203
sol_log_compute_units(); // 100117 sol_log_compute_units(); // 100117

View File

@ -218,7 +218,7 @@ pub fn serum3_liq_force_cancel_orders(
after_base_vault, after_base_vault,
before_base_vault, before_base_vault,
)? )?
.adjust_health_cache(&mut health_cache)?; .adjust_health_cache(&mut health_cache, &base_bank)?;
apply_vault_difference( apply_vault_difference(
ctx.accounts.account.key(), ctx.accounts.account.key(),
&mut account.borrow_mut(), &mut account.borrow_mut(),
@ -227,7 +227,7 @@ pub fn serum3_liq_force_cancel_orders(
after_quote_vault, after_quote_vault,
before_quote_vault, before_quote_vault,
)? )?
.adjust_health_cache(&mut health_cache)?; .adjust_health_cache(&mut health_cache, &quote_bank)?;
// //
// Health check at the end // Health check at the end

View File

@ -340,8 +340,8 @@ pub fn serum3_place_order(
require_gte!(before_vault, after_vault); require_gte!(before_vault, after_vault);
// Charge the difference in vault balance to the user's account // Charge the difference in vault balance to the user's account
let vault_difference = {
let mut payer_bank = ctx.accounts.payer_bank.load_mut()?; let mut payer_bank = ctx.accounts.payer_bank.load_mut()?;
let vault_difference = {
apply_vault_difference( apply_vault_difference(
ctx.accounts.account.key(), ctx.accounts.account.key(),
&mut account.borrow_mut(), &mut account.borrow_mut(),
@ -356,7 +356,7 @@ pub fn serum3_place_order(
// Health check // Health check
// //
if let Some((mut health_cache, pre_health)) = pre_health_opt { if let Some((mut health_cache, pre_health)) = pre_health_opt {
vault_difference.adjust_health_cache(&mut health_cache)?; vault_difference.adjust_health_cache(&mut health_cache, &payer_bank)?;
oo_difference.adjust_health_cache(&mut health_cache, &serum_market)?; oo_difference.adjust_health_cache(&mut health_cache, &serum_market)?;
account.check_health_post(&health_cache, pre_health)?; account.check_health_post(&health_cache, pre_health)?;
} }
@ -408,8 +408,9 @@ pub struct VaultDifference {
} }
impl VaultDifference { impl VaultDifference {
pub fn adjust_health_cache(&self, health_cache: &mut HealthCache) -> Result<()> { pub fn adjust_health_cache(&self, health_cache: &mut HealthCache, bank: &Bank) -> Result<()> {
health_cache.adjust_token_balance(self.token_index, self.native_change)?; assert_eq!(bank.token_index, self.token_index);
health_cache.adjust_token_balance(bank, self.native_change)?;
Ok(()) Ok(())
} }
} }

View File

@ -297,7 +297,7 @@ pub fn token_liq_bankruptcy(
let liab_bank = bank_ais[0].load::<Bank>()?; let liab_bank = bank_ais[0].load::<Bank>()?;
let end_liab_native = liqee_liab.native(&liab_bank); let end_liab_native = liqee_liab.native(&liab_bank);
liqee_health_cache liqee_health_cache
.adjust_token_balance(liab_token_index, cm!(end_liab_native - initial_liab_native))?; .adjust_token_balance(&liab_bank, cm!(end_liab_native - initial_liab_native))?;
// Check liqee health again // Check liqee health again
let liqee_init_health = liqee_health_cache.health(HealthType::Init); let liqee_init_health = liqee_health_cache.health(HealthType::Init);

View File

@ -187,12 +187,10 @@ pub fn token_liq_with_token(
let liqee_assets_native_after = liqee_asset_position.native(asset_bank); let liqee_assets_native_after = liqee_asset_position.native(asset_bank);
// Update the health cache // Update the health cache
liqee_health_cache
.adjust_token_balance(&liab_bank, cm!(liqee_liab_native_after - liqee_liab_native))?;
liqee_health_cache.adjust_token_balance( liqee_health_cache.adjust_token_balance(
liab_token_index, &asset_bank,
cm!(liqee_liab_native_after - liqee_liab_native),
)?;
liqee_health_cache.adjust_token_balance(
asset_token_index,
cm!(liqee_assets_native_after - liqee_asset_native), cm!(liqee_assets_native_after - liqee_asset_native),
)?; )?;

View File

@ -151,7 +151,9 @@ pub fn token_register(
* net_borrows_window_size_ts, * net_borrows_window_size_ts,
net_borrows_limit_native, net_borrows_limit_native,
net_borrows_window_native: 0, net_borrows_window_native: 0,
reserved: [0; 2136], borrow_limit_quote: f64::MAX,
collateral_limit_quote: f64::MAX,
reserved: [0; 2120],
}; };
require_gt!(bank.max_rate, MINIMUM_MAX_RATE); require_gt!(bank.max_rate, MINIMUM_MAX_RATE);

View File

@ -126,7 +126,9 @@ pub fn token_register_trustless(
* net_borrows_window_size_ts, * net_borrows_window_size_ts,
net_borrows_limit_native: 1_000_000, net_borrows_limit_native: 1_000_000,
net_borrows_window_native: 0, net_borrows_window_native: 0,
reserved: [0; 2136], borrow_limit_quote: f64::MAX,
collateral_limit_quote: f64::MAX,
reserved: [0; 2120],
}; };
require_gt!(bank.max_rate, MINIMUM_MAX_RATE); require_gt!(bank.max_rate, MINIMUM_MAX_RATE);

View File

@ -146,8 +146,7 @@ pub fn token_withdraw(ctx: Context<TokenWithdraw>, amount: u64, allow_borrow: bo
// Health check // Health check
// //
if let Some((mut health_cache, pre_health)) = pre_health_opt { if let Some((mut health_cache, pre_health)) = pre_health_opt {
health_cache health_cache.adjust_token_balance(&bank, cm!(native_position_after - native_position))?;
.adjust_token_balance(token_index, cm!(native_position_after - native_position))?;
account.check_health_post(&health_cache, pre_health)?; account.check_health_post(&health_cache, pre_health)?;
} }

View File

@ -113,8 +113,25 @@ pub struct Bank {
pub net_borrows_limit_native: i64, pub net_borrows_limit_native: i64,
pub net_borrows_window_native: i64, pub net_borrows_window_native: i64,
/// Soft borrow limit in native quote
///
/// Once the borrows on the bank exceed this quote value, init_liab_weight is scaled up.
/// Set to f64::MAX to disable.
///
/// See scaled_init_liab_weight().
pub borrow_limit_quote: f64,
/// Limit for collateral of deposits
///
/// Once the deposits in the bank exceed this quote value, init_asset_weight is scaled
/// down to keep the total collateral value constant.
/// Set to f64::MAX to disable.
///
/// See scaled_init_asset_weight().
pub collateral_limit_quote: f64,
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub reserved: [u8; 2136], pub reserved: [u8; 2120],
} }
const_assert_eq!(size_of::<Bank>(), 3112); const_assert_eq!(size_of::<Bank>(), 3112);
const_assert_eq!(size_of::<Bank>() % 8, 0); const_assert_eq!(size_of::<Bank>() % 8, 0);
@ -175,7 +192,9 @@ impl Bank {
net_borrows_window_size_ts: existing_bank.net_borrows_window_size_ts, net_borrows_window_size_ts: existing_bank.net_borrows_window_size_ts,
last_net_borrows_window_start_ts: existing_bank.last_net_borrows_window_start_ts, last_net_borrows_window_start_ts: existing_bank.last_net_borrows_window_start_ts,
net_borrows_window_native: 0, net_borrows_window_native: 0,
reserved: [0; 2136], borrow_limit_quote: f64::MAX,
collateral_limit_quote: f64::MAX,
reserved: [0; 2120],
} }
} }
@ -185,12 +204,14 @@ impl Bank {
.trim_matches(char::from(0)) .trim_matches(char::from(0))
} }
#[inline(always)]
pub fn native_borrows(&self) -> I80F48 { pub fn native_borrows(&self) -> I80F48 {
self.borrow_index * self.indexed_borrows cm!(self.borrow_index * self.indexed_borrows)
} }
#[inline(always)]
pub fn native_deposits(&self) -> I80F48 { pub fn native_deposits(&self) -> I80F48 {
self.deposit_index * self.indexed_deposits cm!(self.deposit_index * self.indexed_deposits)
} }
/// Deposits `native_amount`. /// Deposits `native_amount`.
@ -692,6 +713,47 @@ impl Bank {
pub fn stable_price(&self) -> I80F48 { pub fn stable_price(&self) -> I80F48 {
I80F48::from_num(self.stable_price_model.stable_price) I80F48::from_num(self.stable_price_model.stable_price)
} }
/// Returns the init asset weight, adjusted for the number of deposits on the bank.
///
/// If max_collateral is 0, then the scaled init weight will be 0.
/// Otherwise the weight is unadjusted until max_collateral and then scaled down
/// such that scaled_init_weight * deposits remains constant.
#[inline(always)]
pub fn scaled_init_asset_weight(&self, price: I80F48) -> I80F48 {
if self.collateral_limit_quote == f64::MAX {
return self.init_asset_weight;
}
// The next line is around 500 CU
let deposits_quote = self.native_deposits().to_num::<f64>() * price.to_num::<f64>();
if deposits_quote <= self.collateral_limit_quote {
self.init_asset_weight
} else {
// The next line is around 500 CU
let scale = self.collateral_limit_quote / deposits_quote;
cm!(self.init_asset_weight * I80F48::from_num(scale))
}
}
#[inline(always)]
pub fn scaled_init_liab_weight(&self, price: I80F48) -> I80F48 {
if self.borrow_limit_quote == f64::MAX {
return self.init_liab_weight;
}
// The next line is around 500 CU
let borrows_quote = self.native_borrows().to_num::<f64>() * price.to_num::<f64>();
if borrows_quote <= self.borrow_limit_quote {
self.init_liab_weight
} else if self.borrow_limit_quote == 0.0 {
// TODO: will certainly cause overflow, so it's not exactly what is needed; health should be -MAX?
// maybe handling this case isn't super helpful?
I80F48::MAX
} else {
// The next line is around 500 CU
let scale = borrows_quote / self.borrow_limit_quote;
cm!(self.init_liab_weight * I80F48::from_num(scale))
}
}
} }
#[macro_export] #[macro_export]

View File

@ -5,7 +5,7 @@ use fixed_macro::types::I80F48;
use crate::error::*; use crate::error::*;
use crate::state::{ use crate::state::{
MangoAccountFixed, MangoAccountRef, PerpMarket, PerpMarketIndex, PerpPosition, Bank, MangoAccountFixed, MangoAccountRef, PerpMarket, PerpMarketIndex, PerpPosition,
Serum3MarketIndex, TokenIndex, Serum3MarketIndex, TokenIndex,
}; };
use crate::util::checked_math as cm; use crate::util::checked_math as cm;
@ -365,10 +365,15 @@ impl HealthCache {
.ok_or_else(|| error_msg!("token index {} not found", token_index)) .ok_or_else(|| error_msg!("token index {} not found", token_index))
} }
pub fn adjust_token_balance(&mut self, token_index: TokenIndex, change: I80F48) -> Result<()> { /// Changes the cached user account token balance.
let entry_index = self.token_info_index(token_index)?; pub fn adjust_token_balance(&mut self, bank: &Bank, change: I80F48) -> Result<()> {
let entry_index = self.token_info_index(bank.token_index)?;
let mut entry = &mut self.token_infos[entry_index]; let mut entry = &mut self.token_infos[entry_index];
entry.init_asset_weight =
bank.scaled_init_asset_weight(entry.prices.asset(HealthType::Init));
entry.init_liab_weight = bank.scaled_init_liab_weight(entry.prices.liab(HealthType::Init));
// Work around the fact that -((-x) * y) == x * y does not hold for I80F48: // Work around the fact that -((-x) * y) == x * y does not hold for I80F48:
// We need to make sure that if balance is before * price, then change = -before // We need to make sure that if balance is before * price, then change = -before
// brings it to exactly zero. // brings it to exactly zero.
@ -377,6 +382,20 @@ impl HealthCache {
Ok(()) Ok(())
} }
/// Recomputes the dynamic init weights for the bank's current deposits/borrows.
pub fn recompute_token_weights(&mut self, bank: &Bank) -> Result<()> {
let entry_index = self.token_info_index(bank.token_index)?;
let mut entry = &mut self.token_infos[entry_index];
entry.init_asset_weight =
bank.scaled_init_asset_weight(entry.prices.asset(HealthType::Init));
entry.init_liab_weight = bank.scaled_init_liab_weight(entry.prices.liab(HealthType::Init));
Ok(())
}
/// Changes the cached user account token and serum balances.
///
/// WARNING: You must also call recompute_token_weights() after all bank
/// deposit/withdraw changes!
pub fn adjust_serum3_reserved( pub fn adjust_serum3_reserved(
&mut self, &mut self,
market_index: Serum3MarketIndex, market_index: Serum3MarketIndex,
@ -604,16 +623,17 @@ pub fn new_health_cache(
retriever.bank_and_oracle(&account.fixed.group, i, position.token_index)?; retriever.bank_and_oracle(&account.fixed.group, i, position.token_index)?;
let native = position.native(bank); let native = position.native(bank);
let prices = Prices {
oracle: oracle_price,
stable: bank.stable_price(),
};
token_infos.push(TokenInfo { token_infos.push(TokenInfo {
token_index: bank.token_index, token_index: bank.token_index,
maint_asset_weight: bank.maint_asset_weight, maint_asset_weight: bank.maint_asset_weight,
init_asset_weight: bank.init_asset_weight, init_asset_weight: bank.scaled_init_asset_weight(prices.asset(HealthType::Init)),
maint_liab_weight: bank.maint_liab_weight, maint_liab_weight: bank.maint_liab_weight,
init_liab_weight: bank.init_liab_weight, init_liab_weight: bank.scaled_init_liab_weight(prices.liab(HealthType::Init)),
prices: Prices { prices,
oracle: oracle_price,
stable: bank.stable_price(),
},
balance_native: native, balance_native: native,
}); });
} }
@ -787,6 +807,14 @@ mod tests {
)); ));
} }
#[derive(Default)]
struct BankSettings {
deposits: u64,
borrows: u64,
collateral_limit_quote: u64,
borrow_limit_quote: u64,
}
#[derive(Default)] #[derive(Default)]
struct TestHealth1Case { struct TestHealth1Case {
token1: i64, token1: i64,
@ -796,6 +824,7 @@ mod tests {
oo_1_3: (u64, u64), oo_1_3: (u64, u64),
perp1: (i64, i64, i64, i64), perp1: (i64, i64, i64, i64),
expected_health: f64, expected_health: f64,
bank_settings: [BankSettings; 3],
} }
fn test_health1_runner(testcase: &TestHealth1Case) { fn test_health1_runner(testcase: &TestHealth1Case) {
let buffer = MangoAccount::default_for_tests().try_to_vec().unwrap(); let buffer = MangoAccount::default_for_tests().try_to_vec().unwrap();
@ -830,6 +859,21 @@ mod tests {
DUMMY_NOW_TS, DUMMY_NOW_TS,
) )
.unwrap(); .unwrap();
for (settings, bank) in testcase
.bank_settings
.iter()
.zip([&mut bank1, &mut bank2, &mut bank3].iter_mut())
{
let bank = bank.data();
bank.indexed_deposits = I80F48::from(settings.deposits) / bank.deposit_index;
bank.indexed_borrows = I80F48::from(settings.borrows) / bank.borrow_index;
if settings.collateral_limit_quote > 0 {
bank.collateral_limit_quote = settings.collateral_limit_quote as f64;
}
if settings.borrow_limit_quote > 0 {
bank.borrow_limit_quote = settings.borrow_limit_quote as f64;
}
}
let mut oo1 = TestAccount::<OpenOrders>::new_zeroed(); let mut oo1 = TestAccount::<OpenOrders>::new_zeroed();
let serum3account1 = account.create_serum3_orders(2).unwrap(); let serum3account1 = account.create_serum3_orders(2).unwrap();
@ -995,6 +1039,66 @@ mod tests {
+ 20.0 * 0.8, + 20.0 * 0.8,
..Default::default() ..Default::default()
}, },
TestHealth1Case { // 10, checking collateral limit
token1: 100,
token2: 100,
token3: 100,
bank_settings: [
BankSettings {
deposits: 100,
collateral_limit_quote: 1000,
..BankSettings::default()
},
BankSettings {
deposits: 1500,
collateral_limit_quote: 1000 * 5,
..BankSettings::default()
},
BankSettings {
deposits: 10000,
collateral_limit_quote: 1000 * 10,
..BankSettings::default()
},
],
expected_health:
// token1
0.8 * 100.0
// token2
+ 0.5 * 100.0 * 5.0 * (5000.0 / (1500.0 * 5.0))
// token3
+ 0.5 * 100.0 * 10.0 * (10000.0 / (10000.0 * 10.0)),
..Default::default()
},
TestHealth1Case { // 11, checking borrow limit
token1: -100,
token2: -100,
token3: -100,
bank_settings: [
BankSettings {
borrows: 100,
borrow_limit_quote: 1000,
..BankSettings::default()
},
BankSettings {
borrows: 1500,
borrow_limit_quote: 1000 * 5,
..BankSettings::default()
},
BankSettings {
borrows: 10000,
borrow_limit_quote: 1000 * 10,
..BankSettings::default()
},
],
expected_health:
// token1
-1.2 * 100.0
// token2
- 1.5 * 100.0 * 5.0 * (1500.0 * 5.0 / 5000.0)
// token3
- 1.5 * 100.0 * 10.0 * (10000.0 * 10.0 / 10000.0),
..Default::default()
},
]; ];
for (i, testcase) in testcases.iter().enumerate() { for (i, testcase) in testcases.iter().enumerate() {

View File

@ -6,7 +6,7 @@ use fixed::types::I80F48;
use fixed_macro::types::I80F48; use fixed_macro::types::I80F48;
use crate::error::*; use crate::error::*;
use crate::state::{PerpMarketIndex, TokenIndex}; use crate::state::{Bank, MangoAccountValue, PerpMarketIndex};
use crate::util::checked_math as cm; use crate::util::checked_math as cm;
use super::*; use super::*;
@ -43,6 +43,44 @@ impl HealthCache {
} }
} }
fn cache_after_swap(
&self,
account: &MangoAccountValue,
source_bank: &Bank,
target_bank: &Bank,
amount: I80F48,
price: I80F48,
) -> Self {
let mut source_position = account
.token_position(source_bank.token_index)
.map(|v| v.clone())
.unwrap_or_default();
let mut target_position = account
.token_position(target_bank.token_index)
.map(|v| v.clone())
.unwrap_or_default();
let target_amount = cm!(amount * price);
let mut source_bank = source_bank.clone();
source_bank
.withdraw_with_fee(&mut source_position, amount, 0)
.unwrap();
let mut target_bank = target_bank.clone();
target_bank
.deposit(&mut target_position, target_amount, 0)
.unwrap();
let mut resulting_cache = self.clone();
resulting_cache
.adjust_token_balance(&source_bank, -amount)
.unwrap();
resulting_cache
.adjust_token_balance(&target_bank, target_amount)
.unwrap();
resulting_cache
}
/// How much source native tokens may be swapped for target tokens while staying /// How much source native tokens may be swapped for target tokens while staying
/// above the min_ratio health ratio. /// above the min_ratio health ratio.
/// ///
@ -54,8 +92,9 @@ impl HealthCache {
/// NOTE: keep getMaxSourceForTokenSwap in ts/client in sync with changes here /// NOTE: keep getMaxSourceForTokenSwap in ts/client in sync with changes here
pub fn max_swap_source_for_health_ratio( pub fn max_swap_source_for_health_ratio(
&self, &self,
source: TokenIndex, account: &MangoAccountValue,
target: TokenIndex, source_bank: &Bank,
target_bank: &Bank,
price: I80F48, price: I80F48,
min_ratio: I80F48, min_ratio: I80F48,
) -> Result<I80F48> { ) -> Result<I80F48> {
@ -74,8 +113,8 @@ impl HealthCache {
return Ok(I80F48::ZERO); return Ok(I80F48::ZERO);
} }
let source_index = find_token_info_index(&self.token_infos, source)?; let source_index = find_token_info_index(&self.token_infos, source_bank.token_index)?;
let target_index = find_token_info_index(&self.token_infos, target)?; let target_index = find_token_info_index(&self.token_infos, target_bank.token_index)?;
let source = &self.token_infos[source_index]; let source = &self.token_infos[source_index];
let target = &self.token_infos[target_index]; let target = &self.token_infos[target_index];
@ -89,10 +128,7 @@ impl HealthCache {
} }
let cache_after_swap = |amount: I80F48| { let cache_after_swap = |amount: I80F48| {
let mut adjusted_cache = self.clone(); self.cache_after_swap(account, source_bank, target_bank, amount, price)
adjusted_cache.token_infos[source_index].balance_native -= amount;
adjusted_cache.token_infos[target_index].balance_native += cm!(amount * price);
adjusted_cache
}; };
let health_ratio_after_swap = let health_ratio_after_swap =
|amount| cache_after_swap(amount).health_ratio(HealthType::Init); |amount| cache_after_swap(amount).health_ratio(HealthType::Init);
@ -379,6 +415,15 @@ mod tests {
balance_native: I80F48::ZERO, balance_native: I80F48::ZERO,
}; };
let buffer = MangoAccount::default_for_tests().try_to_vec().unwrap();
let account = MangoAccountValue::from_bytes(&buffer).unwrap();
let group = Pubkey::new_unique();
let (mut bank0, _) = mock_bank_and_oracle(group, 0, 1.0, 0.1, 0.1);
let (mut bank1, _) = mock_bank_and_oracle(group, 1, 5.0, 0.2, 0.2);
let (mut bank2, _) = mock_bank_and_oracle(group, 2, 5.0, 0.3, 0.3);
let banks = [bank0.data(), bank1.data(), bank2.data()];
let health_cache = HealthCache { let health_cache = HealthCache {
token_infos: vec![ token_infos: vec![
TokenInfo { TokenInfo {
@ -407,8 +452,9 @@ mod tests {
assert_eq!( assert_eq!(
health_cache health_cache
.max_swap_source_for_health_ratio( .max_swap_source_for_health_ratio(
0, &account,
1, banks[0],
banks[1],
I80F48::from_num(2.0 / 3.0), I80F48::from_num(2.0 / 3.0),
I80F48::from_num(50.0) I80F48::from_num(50.0)
) )
@ -425,15 +471,17 @@ mod tests {
target: TokenIndex, target: TokenIndex,
ratio: f64, ratio: f64,
price_factor: f64| { price_factor: f64| {
let mut c = c.clone();
let source_price = &c.token_infos[source as usize].prices; let source_price = &c.token_infos[source as usize].prices;
let source_bank = &banks[source as usize];
let target_price = &c.token_infos[target as usize].prices; let target_price = &c.token_infos[target as usize].prices;
let target_bank = &banks[target as usize];
let swap_price = let swap_price =
I80F48::from_num(price_factor) * source_price.oracle / target_price.oracle; I80F48::from_num(price_factor) * source_price.oracle / target_price.oracle;
let source_amount = c let source_amount = c
.max_swap_source_for_health_ratio( .max_swap_source_for_health_ratio(
source, &account,
target, source_bank,
target_bank,
swap_price, swap_price,
I80F48::from_num(ratio), I80F48::from_num(ratio),
) )
@ -441,12 +489,16 @@ mod tests {
if source_amount == I80F48::MAX { if source_amount == I80F48::MAX {
return (f64::MAX, f64::MAX); return (f64::MAX, f64::MAX);
} }
c.adjust_token_balance(source, -source_amount).unwrap(); let after_swap = c.cache_after_swap(
c.adjust_token_balance(target, source_amount * swap_price) &account,
.unwrap(); source_bank,
target_bank,
source_amount,
swap_price,
);
( (
source_amount.to_num::<f64>(), source_amount.to_num::<f64>(),
c.health_ratio(HealthType::Init).to_num::<f64>(), after_swap.health_ratio(HealthType::Init).to_num::<f64>(),
) )
}; };
let check_max_swap_result = |c: &HealthCache, let check_max_swap_result = |c: &HealthCache,

View File

@ -95,6 +95,8 @@ pub fn mock_bank_and_oracle(
bank.data().maint_asset_weight = I80F48::from_num(1.0 - maint_weights); bank.data().maint_asset_weight = I80F48::from_num(1.0 - maint_weights);
bank.data().maint_liab_weight = I80F48::from_num(1.0 + maint_weights); bank.data().maint_liab_weight = I80F48::from_num(1.0 + maint_weights);
bank.data().stable_price_model.reset_to_price(price, 0); bank.data().stable_price_model.reset_to_price(price, 0);
bank.data().collateral_limit_quote = f64::MAX;
bank.data().borrow_limit_quote = f64::MAX;
bank.data().net_borrows_window_size_ts = 1; // dummy bank.data().net_borrows_window_size_ts = 1; // dummy
bank.data().net_borrows_limit_native = i64::MAX; // max since we don't want this to interfere bank.data().net_borrows_limit_native = i64::MAX; // max since we don't want this to interfere
(bank, oracle) (bank, oracle)

View File

@ -13,7 +13,9 @@ mod program_test;
#[tokio::test] #[tokio::test]
async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> { async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
let context = TestContext::new().await; let mut test_builder = TestContextBuilder::new();
test_builder.test().set_compute_max_units(85_000); // TokenLiqWithToken needs 84k
let context = test_builder.start_default().await;
let solana = &context.solana.clone(); let solana = &context.solana.clone();
let admin = TestKeypair::new(); let admin = TestKeypair::new();
@ -275,7 +277,9 @@ async fn test_bankrupt_tokens_socialize_loss() -> Result<(), TransportError> {
#[tokio::test] #[tokio::test]
async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> { async fn test_bankrupt_tokens_insurance_fund() -> Result<(), TransportError> {
let context = TestContext::new().await; let mut test_builder = TestContextBuilder::new();
test_builder.test().set_compute_max_units(85_000); // TokenLiqWithToken needs 84k
let context = test_builder.start_default().await;
let solana = &context.solana.clone(); let solana = &context.solana.clone();
let admin = TestKeypair::new(); let admin = TestKeypair::new();

View File

@ -39,7 +39,7 @@ async fn test_health_compute_tokens() -> Result<(), TransportError> {
create_funded_account(&solana, group, owner, 0, &context.users[1], mints, 1000, 0).await; create_funded_account(&solana, group, owner, 0, &context.users[1], mints, 1000, 0).await;
// TODO: actual explicit CU comparisons. // TODO: actual explicit CU comparisons.
// On 2022-11-18 the final deposit costs 45495 CU and each new token increases it by roughly 1729 CU // On 2022-11-29 the final deposit costs 61568 CU and each new token increases it by roughly 3125 CU
Ok(()) Ok(())
} }
@ -47,7 +47,9 @@ async fn test_health_compute_tokens() -> Result<(), TransportError> {
// Try to reach compute limits in health checks by having many serum markets in an account // Try to reach compute limits in health checks by having many serum markets in an account
#[tokio::test] #[tokio::test]
async fn test_health_compute_serum() -> Result<(), TransportError> { async fn test_health_compute_serum() -> Result<(), TransportError> {
let context = TestContext::new().await; let mut test_builder = TestContextBuilder::new();
test_builder.test().set_compute_max_units(80_000);
let context = test_builder.start_default().await;
let solana = &context.solana.clone(); let solana = &context.solana.clone();
let admin = TestKeypair::new(); let admin = TestKeypair::new();
@ -156,7 +158,7 @@ async fn test_health_compute_serum() -> Result<(), TransportError> {
} }
// TODO: actual explicit CU comparisons. // TODO: actual explicit CU comparisons.
// On 2022-11-18 the final deposit costs 62920 CU and each new market increases it by roughly 4820 CU // On 2022-11-29 the final deposit costs 76029 CU and each new market increases it by roughly 6191 CU
Ok(()) Ok(())
} }
@ -272,7 +274,7 @@ async fn test_health_compute_perp() -> Result<(), TransportError> {
} }
// TODO: actual explicit CU comparisons. // TODO: actual explicit CU comparisons.
// On 2022-11-18 the final deposit costs 50502 CU and each new market increases it by roughly 3037 CU // On 2022-11-29 the final deposit costs 54954 CU and each new market increases it by roughly 3171 CU
Ok(()) Ok(())
} }

View File

@ -173,7 +173,9 @@ async fn test_liq_tokens_force_cancel() -> Result<(), TransportError> {
#[tokio::test] #[tokio::test]
async fn test_liq_tokens_with_token() -> Result<(), TransportError> { async fn test_liq_tokens_with_token() -> Result<(), TransportError> {
let context = TestContext::new().await; let mut test_builder = TestContextBuilder::new();
test_builder.test().set_compute_max_units(85_000); // LiqTokenWithToken needs 79k
let context = test_builder.start_default().await;
let solana = &context.solana.clone(); let solana = &context.solana.clone();
let admin = TestKeypair::new(); let admin = TestKeypair::new();