Summary
StabilityPool.sol#liquidateBorrower() does not interact.
Vulnerability Details
StabilityPool.sol#liquidateBorrower() function is as follows.
function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
@> uint256 userDebt = lendingPool.getUserDebt(userAddress);
@> uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
if (userDebt == 0) revert InvalidAmount();
...
emit BorrowerLiquidated(userAddress, scaledUserDebt);
}
The userDebt is value which has been already scaled up by usageIndex as follows.
File: LendingPool.sol
function getUserDebt(address userAddress) public view returns (uint256) {
UserData storage user = userData[userAddress];
@> return user.scaledDebtBalance.rayMul(reserve.usageIndex);
}
But scaledUserDebt is calculated wrongly by scaling up with lendingPool.getNormalizedDebt() recursively.
This leads to bigger debt amount.
Impact
This vulnerability makes the protocol suffer from bigger funds than normal for liquidating.
Tools Used
Manual review
Recommendations
Modify StabilityPool.sol#liquidateBorrower() function as follows.
function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
uint256 userDebt = lendingPool.getUserDebt(userAddress);
-- uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
if (userDebt == 0) revert InvalidAmount();
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
-- if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
++ if (crvUSDBalance < userDebt) revert InsufficientBalance();
-- bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
++ bool approveSuccess = crvUSDToken.approve(address(lendingPool), userDebt);
if (!approveSuccess) revert ApprovalFailed();
lendingPool.finalizeLiquidation(userAddress);
-- emit BorrowerLiquidated(userAddress, scaledUserDebt);
++ emit BorrowerLiquidated(userAddress, userDebt);
}