Skip to content

Commit

Permalink
deposit limit: always allow obv1-selling deposits (#869)
Browse files Browse the repository at this point in the history
Previously serum3_place_order would fail when deposit limits were
exhausted on the payer token side. Now the failure only happens when
payer tokens need to be borrowed.

(cherry picked from commit a4cddf3)
  • Loading branch information
ckamm committed Feb 5, 2024
1 parent 39c5d02 commit d12bcb2
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
21 changes: 14 additions & 7 deletions programs/mango-v4/src/instructions/serum3_place_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,18 +316,13 @@ pub fn serum3_place_order(
)?
};

// Deposit limit check: Placing an order can increase deposit limit use on both
// the payer and receiver bank. Imagine placing a bid for 500 base @ 0.5: it would
// use up 1000 quote and 500 base because either could be deposit on cancel/fill.
// This is why this must happen after update_bank_potential_tokens() and any withdraws.
// Deposit limit check, receiver side:
// Placing an order can always increase the receiver bank deposits on fill.
{
let receiver_bank = receiver_bank_ai.load::<Bank>()?;
receiver_bank
.check_deposit_and_oo_limit()
.with_context(|| std::format!("on {}", receiver_bank.name()))?;
payer_bank
.check_deposit_and_oo_limit()
.with_context(|| std::format!("on {}", payer_bank.name()))?;
}

// Payer bank safety checks like reduce-only, net borrows, vault-to-deposits ratio
Expand All @@ -342,6 +337,18 @@ pub fn serum3_place_order(
);
payer_bank.enforce_max_utilization_on_borrow()?;
payer_bank.check_net_borrows(payer_bank_oracle)?;

// Deposit limit check, payer side:
// The payer bank deposits could increase when cancelling the order later:
// Imagine the account borrowing payer tokens to place the order, repaying the borrows
// and then cancelling the order to create a deposit.
//
// However, if the account only decreases its deposits to place an order it can't
// worsen the situation and should always go through, even if payer deposit limits are
// already exceeded.
payer_bank
.check_deposit_and_oo_limit()
.with_context(|| std::format!("on {}", payer_bank.name()))?;
} else {
payer_bank.enforce_borrows_lte_deposits()?;
}
Expand Down
44 changes: 43 additions & 1 deletion programs/mango-v4/tests/cases/test_serum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,7 @@ async fn test_serum_deposit_limits() -> Result<(), TransportError> {
//
let deposit_amount = 5000; // for 10k tokens over both order_placers
let CommonSetup {
serum_market_cookie,
group_with_tokens,
mut order_placer,
quote_token,
Expand Down Expand Up @@ -1677,11 +1678,15 @@ async fn test_serum_deposit_limits() -> Result<(), TransportError> {
let remaining_quote = {
|| async {
let b: Bank = solana2.get_account(quote_bank).await;
b.remaining_deposits_until_limit().round().to_num::<u64>()
b.remaining_deposits_until_limit().round().to_num::<i64>()
}
};

order_placer.cancel_all().await;
context
.serum
.consume_spot_events(&serum_market_cookie, &[order_placer.open_orders])
.await;

//
// TEST: even when placing all quote tokens into a bid, they still count
Expand All @@ -1705,6 +1710,43 @@ async fn test_serum_deposit_limits() -> Result<(), TransportError> {
assert_mango_error(&r, MangoError::BankDepositLimit.into(), "dep limit".into());
order_placer.try_ask(5.0, 399).await.unwrap(); // not 400 due to rounding

// reset
order_placer.cancel_all().await;
context
.serum
.consume_spot_events(&serum_market_cookie, &[order_placer.open_orders])
.await;
order_placer.settle().await;

//
// TEST: can place a bid even if quote deposit limit is exhausted
//
send_tx(
solana,
TokenEdit {
group: group_with_tokens.group,
admin: group_with_tokens.admin,
mint: quote_token.mint.pubkey,
fallback_oracle: Pubkey::default(),
options: mango_v4::instruction::TokenEdit {
deposit_limit_opt: Some(1),
..token_edit_instruction_default()
},
},
)
.await
.unwrap();
assert!(remaining_quote().await < 0);
assert_eq!(
account_position(solana, order_placer.account, quote_token.bank).await,
5000
);
// borrowing might lead to a deposit increase later
let r = order_placer.try_bid(1.0, 5001, false).await;
assert_mango_error(&r, MangoError::BankDepositLimit.into(), "dep limit".into());
// but just selling deposits is fine
order_placer.try_bid(1.0, 4999, false).await.unwrap();

Ok(())
}

Expand Down

0 comments on commit d12bcb2

Please sign in to comment.