Net borrow limits: Use correct price for check (#527)
This commit is contained in:
parent
b7189696d8
commit
312a60fe4f
|
@ -136,7 +136,7 @@ pub fn account_buyback_fees_with_mngo(
|
|||
dao_fees_token_position,
|
||||
max_buyback_fees,
|
||||
now_ts,
|
||||
mngo_oracle_price,
|
||||
fees_oracle_price,
|
||||
)?;
|
||||
if !in_use {
|
||||
dao_account.deactivate_token_position_and_log(
|
||||
|
|
|
@ -215,6 +215,10 @@ pub(crate) fn liquidation_action(
|
|||
let liqor_perp_position = liqor.perp_position_mut(perp_market_index)?;
|
||||
|
||||
let perp_info = liqee_health_cache.perp_info(perp_market_index)?;
|
||||
let settle_token_oracle_price = liqee_health_cache
|
||||
.token_info(settle_token_index)?
|
||||
.prices
|
||||
.oracle;
|
||||
let oracle_price = perp_info.prices.oracle;
|
||||
let base_lot_size = I80F48::from(perp_market.base_lot_size);
|
||||
let oracle_price_per_lot = base_lot_size * oracle_price;
|
||||
|
@ -451,7 +455,7 @@ pub(crate) fn liquidation_action(
|
|||
liqor_token_position,
|
||||
token_transfer,
|
||||
now_ts,
|
||||
oracle_price,
|
||||
settle_token_oracle_price,
|
||||
)?;
|
||||
liqee_health_cache.adjust_token_balance(&settle_bank, token_transfer)?;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use anchor_spl::token;
|
|||
use fixed::types::I80F48;
|
||||
|
||||
use crate::accounts_ix::*;
|
||||
use crate::accounts_zerocopy::*;
|
||||
use crate::error::*;
|
||||
use crate::health::{compute_health, new_health_cache, HealthType, ScanningAccountRetriever};
|
||||
use crate::logs::{
|
||||
|
@ -72,10 +71,14 @@ pub fn perp_liq_negative_pnl_or_bankruptcy(
|
|||
|
||||
// Get oracle price for market. Price is validated inside
|
||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||
let oracle_price = perp_market.oracle_price(
|
||||
&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?,
|
||||
None, // staleness checked in health
|
||||
)?;
|
||||
let oracle_price = liqee_health_cache
|
||||
.perp_info(perp_market_index)?
|
||||
.prices
|
||||
.oracle;
|
||||
let settle_token_oracle_price = liqee_health_cache
|
||||
.token_info(settle_token_index)?
|
||||
.prices
|
||||
.oracle;
|
||||
|
||||
let now_ts: u64 = Clock::get()?.unix_timestamp.try_into().unwrap();
|
||||
|
||||
|
@ -126,7 +129,7 @@ pub fn perp_liq_negative_pnl_or_bankruptcy(
|
|||
liqee_token_position,
|
||||
settlement,
|
||||
now_ts,
|
||||
oracle_price,
|
||||
settle_token_oracle_price,
|
||||
)?;
|
||||
liqee_health_cache.adjust_token_balance(&settle_bank, -settlement)?;
|
||||
|
||||
|
|
|
@ -28,11 +28,15 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: u64) ->
|
|||
MangoError::InvalidBank
|
||||
);
|
||||
|
||||
// Get oracle price for market. Price is validated inside
|
||||
// Get oracle prices
|
||||
let oracle_price = perp_market.oracle_price(
|
||||
&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?,
|
||||
None, // staleness checked in health
|
||||
)?;
|
||||
let settle_token_oracle_price = settle_bank.oracle_price(
|
||||
&AccountInfoRef::borrow(ctx.accounts.settle_oracle.as_ref())?,
|
||||
None, // staleness checked in health
|
||||
)?;
|
||||
|
||||
// Fetch perp positions for accounts
|
||||
let perp_position = account.perp_position_mut(perp_market.perp_market_index)?;
|
||||
|
@ -92,7 +96,7 @@ pub fn perp_settle_fees(ctx: Context<PerpSettleFees>, max_settle_amount: u64) ->
|
|||
token_position,
|
||||
settlement,
|
||||
Clock::get()?.unix_timestamp.try_into().unwrap(),
|
||||
oracle_price,
|
||||
settle_token_oracle_price,
|
||||
)?;
|
||||
// Update the settled balance on the market itself
|
||||
perp_market.fees_settled += settlement;
|
||||
|
|
|
@ -58,11 +58,15 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
|
|||
MangoError::InvalidBank
|
||||
);
|
||||
|
||||
// Get oracle price for market. Price is validated inside
|
||||
// Get oracle prices
|
||||
let oracle_price = perp_market.oracle_price(
|
||||
&AccountInfoRef::borrow(ctx.accounts.oracle.as_ref())?,
|
||||
None, // staleness checked in health
|
||||
)?;
|
||||
let settle_token_oracle_price = settle_bank.oracle_price(
|
||||
&AccountInfoRef::borrow(ctx.accounts.settle_oracle.as_ref())?,
|
||||
None, // staleness checked in health
|
||||
)?;
|
||||
|
||||
// Fetch perp position and pnl
|
||||
let a_perp_position = account_a.perp_position_mut(perp_market_index)?;
|
||||
|
@ -172,7 +176,12 @@ pub fn perp_settle_pnl(ctx: Context<PerpSettlePnl>) -> Result<()> {
|
|||
// Don't charge loan origination fees on borrows created via settling:
|
||||
// Even small loan origination fees could accumulate if a perp position is
|
||||
// settled back and forth repeatedly.
|
||||
settle_bank.withdraw_without_fee(b_token_position, settlement, now_ts, oracle_price)?;
|
||||
settle_bank.withdraw_without_fee(
|
||||
b_token_position,
|
||||
settlement,
|
||||
now_ts,
|
||||
settle_token_oracle_price,
|
||||
)?;
|
||||
|
||||
emit!(TokenBalanceLog {
|
||||
mango_group: ctx.accounts.group.key(),
|
||||
|
|
|
@ -612,8 +612,8 @@ impl Bank {
|
|||
.unwrap();
|
||||
if net_borrows_quote > self.net_borrow_limit_per_window_quote {
|
||||
return Err(error_msg_typed!(MangoError::BankNetBorrowsLimitReached,
|
||||
"net_borrows_in_window ({:?}) exceeds net_borrow_limit_per_window_quote ({:?}) for last_net_borrows_window_start_ts ({:?}) ",
|
||||
self.net_borrows_in_window, self.net_borrow_limit_per_window_quote, self.last_net_borrows_window_start_ts
|
||||
"net_borrows_in_window ({:?}) valued at ({:?} exceed net_borrow_limit_per_window_quote ({:?}) for last_net_borrows_window_start_ts ({:?}) ",
|
||||
self.net_borrows_in_window, net_borrows_quote, self.net_borrow_limit_per_window_quote, self.last_net_borrows_window_start_ts
|
||||
|
||||
));
|
||||
}
|
||||
|
|
|
@ -153,6 +153,11 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
}
|
||||
};
|
||||
|
||||
let bank_borrow_used = || {
|
||||
let bank = tokens[0].bank;
|
||||
async move { solana.get_account::<Bank>(bank).await.net_borrows_in_window }
|
||||
};
|
||||
|
||||
reset_net_borrows().await;
|
||||
|
||||
//
|
||||
|
@ -198,6 +203,7 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(bank_borrow_used().await, 5003); // loan fees & ceil
|
||||
|
||||
// fails because borrow is greater than remaining margin in net borrow limit
|
||||
// (requires the test to be quick enough to avoid accidentally going to the next borrow limit window!)
|
||||
|
@ -229,6 +235,38 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// depositing reduces usage, but only the repayment part
|
||||
send_tx(
|
||||
solana,
|
||||
TokenDepositInstruction {
|
||||
amount: 7000,
|
||||
account: account_1,
|
||||
owner,
|
||||
token_authority: payer,
|
||||
token_account: payer_mint_accounts[0],
|
||||
bank_index: 0,
|
||||
reduce_only: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(bank_borrow_used().await, 1); // due to rounding
|
||||
|
||||
// give account1 a negative token0 balance again
|
||||
send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
amount: 5000,
|
||||
allow_borrow: true,
|
||||
account: account_1,
|
||||
owner,
|
||||
token_account: payer_mint_accounts[0],
|
||||
bank_index: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
reset_net_borrows().await;
|
||||
|
@ -242,7 +280,7 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
amount: 1000,
|
||||
amount: 999, // borrow limit increases more due to loan fees + ceil
|
||||
allow_borrow: true,
|
||||
account: account_1,
|
||||
owner,
|
||||
|
@ -252,11 +290,12 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(bank_borrow_used().await, 1000);
|
||||
}
|
||||
|
||||
set_bank_stub_oracle_price(solana, group, &tokens[0], admin, 10.0).await;
|
||||
|
||||
// cannot borrow anything: net borrowed 1000 * price 10.0 > limit 6000
|
||||
// cannot borrow anything: net borrowed 1002 * price 10.0 > limit 6000
|
||||
let res = send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
|
@ -277,7 +316,7 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
let res = send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
amount: 201,
|
||||
amount: 200,
|
||||
allow_borrow: true,
|
||||
account: account_1,
|
||||
owner,
|
||||
|
@ -292,7 +331,7 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
send_tx(
|
||||
solana,
|
||||
TokenWithdrawInstruction {
|
||||
amount: 199,
|
||||
amount: 198,
|
||||
allow_borrow: true,
|
||||
account: account_1,
|
||||
owner,
|
||||
|
@ -302,6 +341,7 @@ async fn test_bank_net_borrows_based_borrow_limit() -> Result<(), TransportError
|
|||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(bank_borrow_used().await, 1199);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue