The LendingPool contract implements a lending protocol where users can deposit assets and borrow against them. The protocol tracks debt using a debt token, whose totalSupply() represents the total debt scaled by the current usage index.
The calculation flow for interest rates is:
LendingPool.deposit()
→ ReserveLibrary.deposit()
→ ReserveLibrary.updateInterestRatesAndLiquidity()
→ ReserveLibrary.calculateUtilizationRate()
The utilization rate, calculated in the final step, is crucial for determining both borrow and lending rates.
When a user calls deposit() in the LendingPool, the following sequence occurs:
updateReserveState() is called to update indices
The function then calls ReserveLibrary.deposit(), which in turn calls updateInterestRatesAndLiquidity()
Inside updateInterestRatesAndLiquidity(), calculateUtilizationRate() is called with the formula:
However, reserve.totalUsage (used as totalDebt) is not synchronized with the debt token's total supply after updateReserveState(). When the usageIndex increases over time due to interest accrual, the debt token's totalSupply() correctly reflects this increase as it's scaled by the usage index, but reserve.totalUsage remains outdated.
High. The miscalculation of utilization rates leads to:
Incorrect borrow rates being set
Incorrect lending rates for depositors
Protocol-wide economic imbalances
Potential loss of protocol revenue
High. This issue affects every operation that involves rate calculations and occurs whenever there is a time gap between operations where interest has accrued.
Consider this scenario:
Initial state: reserve.totalUsage = 100, usageIndex = 1.0 RAY
Time passes, usageIndex increases to 1.2 RAY
User calls LendingPool.deposit():
updateReserveState() updates usageIndex to 1.2 RAY
Debt token's totalSupply() correctly returns 120 (scaled by new index)
But reserve.totalUsage remains at 100
Flow continues to ReserveLibrary.deposit() → updateInterestRatesAndLiquidity() → calculateUtilizationRate()
calculateUtilizationRate() uses 100/(totalLiquidity + 100) instead of 120/(totalLiquidity + 120)
Result: Incorrect utilization rate leads to wrong interest rate calculations
Update the totalUsage after calling updateReserveState() in each of the core functions (deposit(), withdraw()):
Apply the same pattern to withdraw() function to ensure the total usage is always up to date with the actual debt token supply before any rate calculations are performed.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.