Relevant GitHub Links
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L398-L431
Summary
The LendingPool._repay() function incorrectly scales down the repayment amount twice by dividing it by the usage index, causing users to only repay a fraction of their intended debt repayment amount.
Vulnerability Details
In LendingPool._repay(), the repayment amount is incorrectly scaled down twice:
function _repay(uint256 amount, address onBehalfOf) internal {
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
uint256 scaledAmount = actualRepayAmount.rayDiv(reserve.usageIndex);
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
}
For example:
User has 100 tokens of debt with 10% interest (usageIndex = 1.1)
Total debt with interest = 110 tokens
First division: 110/1.1 = 100 (scaled debt)
Second division: 100/1.1 ≈ 90.9
User ends up repaying only ~90.9 tokens instead of intended 110
Impact
This incorrect scaling causes:
Users' debt to persist when it should be fully repaid
Continued interest accrual on the remaining debt
Users paying more in interest than they should
Tools Used
Manual review
Recommendations
Remove the second division by usage index since actualRepayAmount is already a scaled amount:
function _repay(uint256 amount, address onBehalfOf) internal {
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
- uint256 scaledAmount = actualRepayAmount.rayDiv(reserve.usageIndex);
+ uint256 scaledAmount = actualRepayAmount;
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
}