lending: handle rounding consistently (#1883)

* handle rounding consistently

* remove unused method

* slightly increase test compute limits

* reuse existing decimal functions
This commit is contained in:
Jordan Sexton 2021-12-03 17:13:21 -06:00 committed by GitHub
parent c24bc966f1
commit c2b287788b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 13 additions and 24 deletions

View File

@ -51,11 +51,6 @@ impl Rate {
U128::from(WAD)
}
// OPTIMIZE: use const slice when fixed in BPF toolchain
fn half_wad() -> U128 {
U128::from(HALF_WAD)
}
/// Create scaled decimal from percent value
pub fn from_percent(percent: u8) -> Self {
Self(U128::from(percent as u64 * PERCENT_SCALER))
@ -72,16 +67,6 @@ impl Rate {
Self(U128::from(scaled_val))
}
/// Round scaled decimal to u64
pub fn try_round_u64(&self) -> Result<u64, ProgramError> {
let rounded_val = Self::half_wad()
.checked_add(self.0)
.ok_or(LendingError::MathOverflow)?
.checked_div(Self::wad())
.ok_or(LendingError::MathOverflow)?;
Ok(u64::try_from(rounded_val).map_err(|_| LendingError::MathOverflow)?)
}
/// Calculates base^exp
pub fn try_pow(&self, mut exp: u64) -> Result<Rate, ProgramError> {
let mut base = *self;

View File

@ -553,9 +553,8 @@ pub struct CollateralExchangeRate(Rate);
impl CollateralExchangeRate {
/// Convert reserve collateral to liquidity
pub fn collateral_to_liquidity(&self, collateral_amount: u64) -> Result<u64, ProgramError> {
Decimal::from(collateral_amount)
.try_div(self.0)?
.try_round_u64()
self.decimal_collateral_to_liquidity(collateral_amount.into())?
.try_floor_u64()
}
/// Convert reserve collateral to liquidity
@ -568,7 +567,8 @@ impl CollateralExchangeRate {
/// Convert reserve liquidity to collateral
pub fn liquidity_to_collateral(&self, liquidity_amount: u64) -> Result<u64, ProgramError> {
self.0.try_mul(liquidity_amount)?.try_round_u64()
self.decimal_liquidity_to_collateral(liquidity_amount.into())?
.try_floor_u64()
}
/// Convert reserve liquidity to collateral
@ -662,9 +662,9 @@ impl ReserveFees {
if borrow_fee_rate > Rate::zero() && amount > Decimal::zero() {
let need_to_assess_host_fee = host_fee_rate > Rate::zero();
let minimum_fee = if need_to_assess_host_fee {
2 // 1 token to owner, 1 to host
2u64 // 1 token to owner, 1 to host
} else {
1 // 1 token to owner, nothing else
1u64 // 1 token to owner, nothing else
};
let borrow_fee_amount = match fee_calculation {
@ -678,14 +678,18 @@ impl ReserveFees {
}
};
let borrow_fee = borrow_fee_amount.try_round_u64()?.max(minimum_fee);
if Decimal::from(borrow_fee) >= amount {
let borrow_fee_decimal = borrow_fee_amount.max(minimum_fee.into());
if borrow_fee_decimal >= amount {
msg!("Borrow amount is too small to receive liquidity after fees");
return Err(LendingError::BorrowTooSmall.into());
}
let borrow_fee = borrow_fee_decimal.try_round_u64()?;
let host_fee = if need_to_assess_host_fee {
host_fee_rate.try_mul(borrow_fee)?.try_round_u64()?.max(1)
borrow_fee_decimal
.try_mul(host_fee_rate)?
.try_round_u64()?
.max(1u64)
} else {
0
};