20,000 USDC
View results
Submission Details
Severity: medium
Valid

Precision loss allows users to giveLoans to pools with less collateral then required

Summary

The _calculateInterest function is vulnerable to precision loss. Due to dividing twice the function will return zero for both interest and protocolInterest. This could lead to a lender giving their loan to another pool that doesn't have the balance to cover it. Leading to a loss for them and a gain for the future pools as they will have debts greater than their balance.

Vulnerability Details

giveLoan() calls _calculateInterest() which then returns the amount of interest the protocol fee and adds that to the loan (debt) in order to calculate the totalDebt.However, we already determined that these values are vulnerable to precision loss leading them to return 0 in some cases or be smaller than expected. That would lead to an inaccurate totalDebt and loanRatio allowing us to give a loan to a pool that has a higher loan ratio than our current pool which is not the expected behavior of the protocol.

Impact

The effect of this is the loanRatio value will be lower than expected.

// assume the following
// totalDebt = 40 and loan.collateral = 10
// expected calculation 30 / 10 = 3
// actual due to precision loss 20 / 10 = 2
// maxLoanRatio = 2
uint256 loanRatio = (totalDebt * 10 ** 18) / loan.collateral;
// we would expect the next line to revert but
// this would pass allowing us to create a loan with less collateral than needed for this pool
if (loanRatio > pool.maxLoanRatio) revert RatioTooHigh();

This would allow a user to move their loan to a pool in which their loanRatio is greater than the maxloanRatio for that pool. Allowing them to move and create loans to pools with less collateral than the pool requires.

Recommended Steps

Verifying that neither the interest rate nor the fees are zero can help prevent precision loss and trigger a reversal if necessary. Additionally, establishing a minimum loan size could ensure that the left side of the equation consistently exceeds the right side.

interest = (l.interestRate * l.debt * timeElapsed) / 10000 / 365 days;
fees = (lenderFee * interest) / 10000;
if(interest == 0 || fees == 0) revert PrecisionLoss();
interest -= fees;

Another solution is creating a formula that always rounds up. This is in favor of the lender and the protocol as well. Something like this would suffice.

/**
* @param a numerator
* @param b denominator
* @dev Division, rounded up
*/
function roundUpDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
return (a - 1) / b + 1;
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.