Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into ckamm/dynamic-cu-request
Browse files Browse the repository at this point in the history
  • Loading branch information
ckamm committed Nov 2, 2023
2 parents edae113 + 941f945 commit d5aad4d
Showing 7 changed files with 223 additions and 19 deletions.
51 changes: 51 additions & 0 deletions programs/mango-v4/src/health/cache.rs
Original file line number Diff line number Diff line change
@@ -1454,6 +1454,7 @@ mod tests {
borrows: u64,
deposit_weight_scale_start_quote: u64,
borrow_weight_scale_start_quote: u64,
deposits_in_serum: i64,
}

#[derive(Default)]
@@ -1509,6 +1510,7 @@ mod tests {
let bank = bank.data();
bank.indexed_deposits = I80F48::from(settings.deposits) / bank.deposit_index;
bank.indexed_borrows = I80F48::from(settings.borrows) / bank.borrow_index;
bank.deposits_in_serum = settings.deposits_in_serum;
if settings.deposit_weight_scale_start_quote > 0 {
bank.deposit_weight_scale_start_quote =
settings.deposit_weight_scale_start_quote as f64;
@@ -1807,6 +1809,55 @@ mod tests {
}),
..Default::default()
},
TestHealth1Case {
// 16, base case for 17
token1: 100,
token2: 100,
token3: 100,
oo_1_2: (0, 100),
oo_1_3: (0, 100),
expected_health:
// tokens
100.0 * 0.8 + 100.0 * 5.0 * 0.5 + 100.0 * 10.0 * 0.5
// oo_1_2 (-> token2)
+ 100.0 * 5.0 * 0.5
// oo_1_3 (-> token1)
+ 100.0 * 10.0 * 0.5,
..Default::default()
},
TestHealth1Case {
// 17, deposits_in_serum counts for deposit weight scaling
token1: 100,
token2: 100,
token3: 100,
oo_1_2: (0, 100),
oo_1_3: (0, 100),
bank_settings: [
BankSettings {
..BankSettings::default()
},
BankSettings {
deposits: 100,
deposit_weight_scale_start_quote: 100 * 5,
deposits_in_serum: 100,
..BankSettings::default()
},
BankSettings {
deposits: 600,
deposit_weight_scale_start_quote: 500 * 10,
deposits_in_serum: 100,
..BankSettings::default()
},
],
expected_health:
// tokens
100.0 * 0.8 + 100.0 * 5.0 * 0.5 * (100.0 / 200.0) + 100.0 * 10.0 * 0.5 * (500.0 / 700.0)
// oo_1_2 (-> token2)
+ 100.0 * 5.0 * 0.5 * (100.0 / 200.0)
// oo_1_3 (-> token1)
+ 100.0 * 10.0 * 0.5 * (500.0 / 700.0),
..Default::default()
},
];

for (i, testcase) in testcases.iter().enumerate() {
46 changes: 40 additions & 6 deletions programs/mango-v4/src/instructions/serum3_place_order.rs
Original file line number Diff line number Diff line change
@@ -372,27 +372,39 @@ fn apply_vault_difference(
}
let native_after = position.native(bank);
let native_change = native_after - native_before;
// amount of tokens transfered to serum3 reserved that were borrowed
let new_borrows = native_change
.max(native_after)
.min(I80F48::ZERO)
.abs()
.to_num::<u64>();
// amount of tokens transferred to serum3 reserved that were taken from deposits
let used_deposits = native_before
.min(-needed_change)
.max(I80F48::ZERO)
.to_num::<u64>();

let indexed_position = position.indexed_position;
let market = account.serum3_orders_mut(serum_market_index).unwrap();
let borrows_without_fee = if bank.token_index == market.base_token_index {
&mut market.base_borrows_without_fee
let borrows_without_fee;
let deposits_reserved;
if bank.token_index == market.base_token_index {
borrows_without_fee = &mut market.base_borrows_without_fee;
deposits_reserved = &mut market.base_deposits_reserved;
} else if bank.token_index == market.quote_token_index {
&mut market.quote_borrows_without_fee
borrows_without_fee = &mut market.quote_borrows_without_fee;
deposits_reserved = &mut market.quote_deposits_reserved;
} else {
return Err(error_msg!(
"assert failed: apply_vault_difference called with bad token index"
));
};

// Only for place: Add to potential borrow amount
let old_value = *borrows_without_fee;
*borrows_without_fee = old_value + new_borrows;
// Only for place: Add to potential borrow amount and reserved deposits
*borrows_without_fee += new_borrows;
*deposits_reserved += used_deposits;
let used_deposits_signed: i64 = used_deposits.try_into().unwrap();
bank.deposits_in_serum += used_deposits_signed;

// Only for settle/liq_force_cancel: Reduce the potential borrow amounts
if needed_change > 0 {
@@ -485,6 +497,28 @@ pub fn apply_settle_changes(
before_quote_vault,
)?;

// Tokens were moved from open orders into banks again: also update the tracking
// for deposits_in_serum on the banks.
{
let serum_orders = account.serum3_orders_mut(serum_market.market_index)?;

let after_base_reserved = after_oo.native_base_reserved();
if after_base_reserved < serum_orders.base_deposits_reserved {
let diff = serum_orders.base_deposits_reserved - after_base_reserved;
serum_orders.base_deposits_reserved = after_base_reserved;
let diff_signed: i64 = diff.try_into().unwrap();
base_bank.deposits_in_serum -= diff_signed;
}

let after_quote_reserved = after_oo.native_quote_reserved();
if after_quote_reserved < serum_orders.quote_deposits_reserved {
let diff = serum_orders.quote_deposits_reserved - after_quote_reserved;
serum_orders.quote_deposits_reserved = after_quote_reserved;
let diff_signed: i64 = diff.try_into().unwrap();
quote_bank.deposits_in_serum -= diff_signed;
}
}

if let Some(health_cache) = health_cache {
base_difference.adjust_health_cache_token_balance(health_cache, &base_bank)?;
quote_difference.adjust_health_cache_token_balance(health_cache, &quote_bank)?;
3 changes: 2 additions & 1 deletion programs/mango-v4/src/instructions/token_register.rs
Original file line number Diff line number Diff line change
@@ -112,7 +112,8 @@ pub fn token_register(
flash_loan_swap_fee_rate: flash_loan_swap_fee_rate,
interest_target_utilization,
interest_curve_scaling: interest_curve_scaling.into(),
reserved: [0; 2080],
deposits_in_serum: 0,
reserved: [0; 2072],
};

if let Ok(oracle_price) =
Original file line number Diff line number Diff line change
@@ -93,10 +93,11 @@ pub fn token_register_trustless(
fees_withdrawn: 0,
token_conditional_swap_taker_fee_rate: 0.0005,
token_conditional_swap_maker_fee_rate: 0.0005,
flash_loan_swap_fee_rate: 0.0005,
flash_loan_swap_fee_rate: 0.0,
interest_target_utilization: 0.5,
interest_curve_scaling: 4.0,
reserved: [0; 2080],
deposits_in_serum: 0,
reserved: [0; 2072],
};

if let Ok(oracle_price) =
19 changes: 12 additions & 7 deletions programs/mango-v4/src/state/bank.rs
Original file line number Diff line number Diff line change
@@ -135,6 +135,7 @@ pub struct Bank {
pub reduce_only: u8,
pub force_close: u8,

#[derivative(Debug = "ignore")]
pub padding: [u8; 6],

// Do separate bookkeping for how many tokens were withdrawn
@@ -156,8 +157,12 @@ pub struct Bank {
/// Except when first migrating to having this field, then 0.0
pub interest_curve_scaling: f64,

// user deposits that were moved into serum open orders
// can be negative due to multibank, then it'd need to be balanced in the keeper
pub deposits_in_serum: i64,

#[derivative(Debug = "ignore")]
pub reserved: [u8; 2080],
pub reserved: [u8; 2072],
}
const_assert_eq!(
size_of::<Bank>(),
@@ -189,8 +194,8 @@ const_assert_eq!(
+ 6
+ 8
+ 4 * 4
+ 8
+ 2080
+ 8 * 2
+ 2072
);
const_assert_eq!(size_of::<Bank>(), 3064);
const_assert_eq!(size_of::<Bank>() % 8, 0);
@@ -225,6 +230,7 @@ impl Bank {
flash_loan_approved_amount: 0,
flash_loan_token_account_initial: u64::MAX,
net_borrows_in_window: 0,
deposits_in_serum: 0,
bump,
bank_num,

@@ -273,7 +279,7 @@ impl Bank {
flash_loan_swap_fee_rate: existing_bank.flash_loan_swap_fee_rate,
interest_target_utilization: existing_bank.interest_target_utilization,
interest_curve_scaling: existing_bank.interest_curve_scaling,
reserved: [0; 2080],
reserved: [0; 2072],
}
}

@@ -927,8 +933,8 @@ impl Bank {
if self.deposit_weight_scale_start_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>();
let all_deposits = self.native_deposits().to_num::<f64>() + self.deposits_in_serum as f64;
let deposits_quote = all_deposits * price.to_num::<f64>();
if deposits_quote <= self.deposit_weight_scale_start_quote {
self.init_asset_weight
} else {
@@ -943,7 +949,6 @@ impl Bank {
if self.borrow_weight_scale_start_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_weight_scale_start_quote {
self.init_liab_weight
16 changes: 13 additions & 3 deletions programs/mango-v4/src/state/mango_account_components.rs
Original file line number Diff line number Diff line change
@@ -145,12 +145,20 @@ pub struct Serum3Orders {
pub highest_placed_bid_inv: f64,
pub lowest_placed_ask: f64,

/// Tracks the amount of deposits that flowed into the serum open orders account.
///
/// The bank still considers these amounts user deposits (see deposits_in_serum)
/// and they need to be deducted from there when they flow back into the bank
/// as real tokens.
pub base_deposits_reserved: u64,
pub quote_deposits_reserved: u64,

#[derivative(Debug = "ignore")]
pub reserved: [u8; 48],
pub reserved: [u8; 32],
}
const_assert_eq!(
size_of::<Serum3Orders>(),
32 + 8 * 2 + 2 * 3 + 2 + 2 * 8 + 48
32 + 8 * 2 + 2 * 3 + 2 + 4 * 8 + 32
);
const_assert_eq!(size_of::<Serum3Orders>(), 120);
const_assert_eq!(size_of::<Serum3Orders>() % 8, 0);
@@ -177,7 +185,9 @@ impl Default for Serum3Orders {
quote_borrows_without_fee: 0,
highest_placed_bid_inv: 0.0,
lowest_placed_ask: 0.0,
reserved: [0; 48],
base_deposits_reserved: 0,
quote_deposits_reserved: 0,
reserved: [0; 32],
}
}
}
102 changes: 102 additions & 0 deletions programs/mango-v4/tests/cases/test_serum.rs
Original file line number Diff line number Diff line change
@@ -342,6 +342,13 @@ async fn test_serum_basics() -> Result<(), TransportError> {
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
assert_eq!(serum_orders.base_borrows_without_fee, 0);
assert_eq!(serum_orders.quote_borrows_without_fee, 0);
assert_eq!(serum_orders.base_deposits_reserved, 0);
assert_eq!(serum_orders.quote_deposits_reserved, 100);

let base_bank = solana.get_account::<Bank>(base_token.bank).await;
assert_eq!(base_bank.deposits_in_serum, 0);
let quote_bank = solana.get_account::<Bank>(quote_token.bank).await;
assert_eq!(quote_bank.deposits_in_serum, 100);

assert!(order_id != 0);

@@ -360,6 +367,18 @@ async fn test_serum_basics() -> Result<(), TransportError> {
assert_eq!(native0, 1000);
assert_eq!(native1, 1000);

let account_data = get_mango_account(solana, account).await;
let serum_orders = account_data.serum3_orders_by_raw_index(0).unwrap();
assert_eq!(serum_orders.base_borrows_without_fee, 0);
assert_eq!(serum_orders.quote_borrows_without_fee, 0);
assert_eq!(serum_orders.base_deposits_reserved, 0);
assert_eq!(serum_orders.quote_deposits_reserved, 0);

let base_bank = solana.get_account::<Bank>(base_token.bank).await;
assert_eq!(base_bank.deposits_in_serum, 0);
let quote_bank = solana.get_account::<Bank>(quote_token.bank).await;
assert_eq!(quote_bank.deposits_in_serum, 0);

// Process events such that the OutEvent deactivates the closed order on open_orders
context
.serum
@@ -1257,6 +1276,89 @@ async fn test_serum_track_bid_ask() -> Result<(), TransportError> {
Ok(())
}

#[tokio::test]
async fn test_serum_track_reserved_deposits() -> Result<(), TransportError> {
let mut test_builder = TestContextBuilder::new();
test_builder.test().set_compute_max_units(150_000); // Serum3PlaceOrder needs lots
let context = test_builder.start_default().await;

//
// SETUP: Create a group, accounts, market etc
//
let deposit_amount = 100000;
let CommonSetup {
serum_market_cookie,
quote_token,
base_token,
mut order_placer,
mut order_placer2,
..
} = common_setup(&context, deposit_amount).await;
let solana = &context.solana.clone();
let quote_bank = quote_token.bank;
let base_bank = base_token.bank;
let account = order_placer.account;

let get_vals = |solana| async move {
let account_data = get_mango_account(solana, account).await;
let orders = account_data.all_serum3_orders().next().unwrap();
let base_bank = solana.get_account::<Bank>(base_bank).await;
let quote_bank = solana.get_account::<Bank>(quote_bank).await;
(
orders.base_deposits_reserved,
base_bank.deposits_in_serum,
orders.quote_deposits_reserved,
quote_bank.deposits_in_serum,
)
};

//
// TEST: place a bid and ask and observe tracking
//

order_placer.bid_maker(0.8, 2000).await.unwrap();
order_placer.ask(1.2, 2000).await.unwrap();
assert_eq!(get_vals(solana).await, (2000, 2000, 1600, 1600));

//
// TEST: match partially on both sides, increasing the on-bank reserved amounts
// because order_placer2 puts funds into the serum oo
//

order_placer2.bid_taker(1.2, 1000).await.unwrap();
context
.serum
.consume_spot_events(
&serum_market_cookie,
&[order_placer.open_orders, order_placer2.open_orders],
)
.await;
assert_eq!(get_vals(solana).await, (2000, 2000, 1600, 2801));
order_placer2.settle_v2(false).await;
assert_eq!(get_vals(solana).await, (2000, 2000, 1600, 1600));

order_placer2.ask(0.8, 1000).await.unwrap();
context
.serum
.consume_spot_events(
&serum_market_cookie,
&[order_placer.open_orders, order_placer2.open_orders],
)
.await;
assert_eq!(get_vals(solana).await, (2000, 3000, 1600, 1600));
order_placer2.settle_v2(false).await;
assert_eq!(get_vals(solana).await, (2000, 2000, 1600, 1600));

//
// TEST: Settlement updates the values
//

order_placer.settle_v2(false).await;
assert_eq!(get_vals(solana).await, (1000, 1000, 800, 800));

Ok(())
}

#[tokio::test]
async fn test_serum_compute() -> Result<(), TransportError> {
let mut test_builder = TestContextBuilder::new();

0 comments on commit d5aad4d

Please sign in to comment.