Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Invalid

`getBorrowRate` and `getLiquidityRate` Functions Calculate Incorrect Utilization Rate, Leading to Overestimated Borrowing and Deposit Rates

Summary

The getBorrowRate and getLiquidityRate functions do not use the total liquidity with accumulated interest when calculating the utilization rate (they only use the total debt with accumulated interest). This causes the totalLiquidity to be underestimated, which in turn leads to an overestimated utilizationRate. As a result, both the borrowing rate and the deposit rate are overestimated. Higher borrowing rates reduce borrowing demand, and in turn, depositors receive higher returns than they should. Over time, this may impact the sustainability of the protocol.

Vulnerability Details

Core Issue: The getBorrowRate and getLiquidityRate functions calculate the utilization rate with the following logic:

  • totalDebt is correctly calculated using the getNormalizedDebt function, which includes the accumulated borrow interest.

  • However, totalLiquidity is used directly from the storage value, which does not account for accumulated deposit interest. This causes an asymmetry in the calculation.

  • This leads to an incorrect calculation of the utilization rate because the denominator (liquidity) is underestimated, causing the utilization rate to be overestimated. Consequently, the borrow and liquidity rates are also overestimated.

contracts/libraries/pools/ReserveLibrary.sol:getBorrowRate#L433

function getBorrowRate(ReserveData storage reserve,ReserveRateData storage rateData) internal view returns (uint256) {
uint256 totalDebt = getNormalizedDebt(reserve, rateData);
// @audit: `totalLiquidity` does not account for accumulated deposit interest, leading to overestimated borrow rate
uint256 utilizationRate = calculateUtilizationRate(reserve.totalLiquidity, totalDebt);
return calculateBorrowRate(rateData.primeRate, rateData.baseRate, rateData.optimalRate, rateData.maxRate, rateData.optimalUtilizationRate, utilizationRate);
}

contracts/libraries/pools/ReserveLibrary.sol:getLiquidityRate#L445

function getLiquidityRate(ReserveData storage reserve,ReserveRateData storage rateData) internal view returns (uint256) {
uint256 totalDebt = getNormalizedDebt(reserve, rateData);
// @audit: `totalLiquidity` does not account for accumulated deposit interest, leading to overestimated liquidity rate
uint256 utilizationRate = calculateUtilizationRate(reserve.totalLiquidity, totalDebt);
return calculateLiquidityRate(utilizationRate, rateData.currentUsageRate, rateData.protocolFeeRate, totalDebt);
}

Impact

  • Borrowing Rate: The borrowing rate will be overestimated because the utilization rate is calculated using inaccurate liquidity data. This leads to higher borrowing costs than intended.

  • Deposit Rate: The deposit rate may be overestimated since the utilization rate impacts liquidity rate calculations. This could result in depositors receiving higher returns than they should.

  • Error Accumulation: Overestimating the borrow rate can reduce demand for borrowing, while overestimating the deposit rate can lead to misallocated funds. Over time, the error accumulates, affecting both borrowers and depositors.

Tools Used

Manual Code Review

Recommendations

It is recommended to fix getBorrowRate and getLiquidityRate functions. Modify both functions to use getNormalizedIncome() to calculate the current total liquidity, which includes the accumulated deposit interest. This will ensure that the full liquidity is considered when calculating the utilization rate. For example:

function getBorrowRate(ReserveData storage reserve,ReserveRateData storage rateData) internal view returns (uint256) {
uint256 totalDebt = getNormalizedDebt(reserve, rateData);
+ uint256 currentLiquidity = getNormalizedIncome(reserve, rateData); // @audit: Use normalized liquidity with interest
uint256 utilizationRate = calculateUtilizationRate(currentLiquidity, totalDebt); // @audit: Use the correct liquidity
return calculateBorrowRate(rateData.primeRate, rateData.baseRate, rateData.optimalRate, rateData.maxRate, rateData.optimalUtilizationRate, utilizationRate);
}

Similarly, update getLiquidityRate:

function getLiquidityRate(ReserveData storage reserve,ReserveRateData storage rateData) internal view returns (uint256) {
uint256 totalDebt = getNormalizedDebt(reserve, rateData);
+ uint256 currentLiquidity = getNormalizedIncome(reserve, rateData); // @audit: Use normalized liquidity with interest
uint256 utilizationRate = calculateUtilizationRate(currentLiquidity, totalDebt); // @audit: Use the correct liquidity
return calculateLiquidityRate(utilizationRate, rateData.currentUsageRate, rateData.protocolFeeRate, totalDebt);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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