Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lending: handle rounding consistently #1883

Merged
merged 6 commits into from Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 0 additions & 15 deletions token-lending/program/src/math/rate.rs
Expand Up @@ -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))
Expand All @@ -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;
Expand Down
22 changes: 13 additions & 9 deletions token-lending/program/src/state/reserve.rs
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
};
Expand Down