Summary
LendingPool.sol#_repay() function calculates actualRepayAmount wrongly.
Vulnerability Details
LendingPool.sol#_repay() function is as follows.
function _repay(uint256 amount, address onBehalfOf) internal {
if (amount == 0) revert InvalidAmount();
if (onBehalfOf == address(0)) revert AddressCannotBeZero();
UserData storage user = userData[onBehalfOf];
ReserveLibrary.updateReserveState(reserve, rateData);
408 uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
409 uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
412 uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
uint256 scaledAmount = actualRepayAmount.rayDiv(reserve.usageIndex);
418 (uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
419 IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
422 IERC20(reserve.reserveAssetAddress).safeTransferFrom(msg.sender, reserve.reserveRTokenAddress, amountScaled);
reserve.totalUsage = newTotalSupply;
user.scaledDebtBalance -= amountBurned;
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, amountScaled, 0);
emit Repay(msg.sender, onBehalfOf, actualRepayAmount);
}
L412 calculates actualRepayAmount to be used for log.
But this value is calculated wrongly because userScaledDebt is the value scaled down by usageIndex.
Impact
So the log with wrong actualRepayAmount will make frontend to suffer from that.
Tools Used
Manual review
Recommendations
Modify LendingPool.sol#_repay() function as follows.
function _repay(uint256 amount, address onBehalfOf) internal {
if (amount == 0) revert InvalidAmount();
if (onBehalfOf == address(0)) revert AddressCannotBeZero();
UserData storage user = userData[onBehalfOf];
ReserveLibrary.updateReserveState(reserve, rateData);
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
-- uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
-- uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
++ uint256 actualRepayAmount = amount > userDebt ? userDebt : amount;
-- uint256 scaledAmount = actualRepayAmount.rayDiv(reserve.usageIndex);
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) =
-- IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);
++ IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, actualRepayAmount, reserve.usageIndex);
IERC20(reserve.reserveAssetAddress).safeTransferFrom(msg.sender, reserve.reserveRTokenAddress, amountScaled);
reserve.totalUsage = newTotalSupply;
user.scaledDebtBalance -= amountBurned;
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, amountScaled, 0);
emit Repay(msg.sender, onBehalfOf, actualRepayAmount);
}