Description
liquidateBorrower() multiplies userDebt
with lendingPool.getNormalizedDebt()
to get scaledUserDebt
even though userDebt
is already a scaled-up version:
File: contracts/core/pools/StabilityPool/StabilityPool.sol
449: function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
450: _update();
451:
452: uint256 userDebt = lendingPool.getUserDebt(userAddress);
453:@---> uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
454:
455: if (userDebt == 0) revert InvalidAmount();
456:
457: uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
458:@---> if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
459:
460:
461: bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
462: if (!approveSuccess) revert ApprovalFailed();
463:
464: lendingPool.updateState();
465:
466:
467: lendingPool.finalizeLiquidation(userAddress);
468:
469: emit BorrowerLiquidated(userAddress, scaledUserDebt);
470: }
and
File: contracts/core/pools/LendingPool/LendingPool.sol
579: function getUserDebt(address userAddress) public view returns (uint256) {
580: UserData storage user = userData[userAddress];
581:@---> return user.scaledDebtBalance.rayMul(reserve.usageIndex);
582: }
Impact
As a result, on L458 even if there's crvUSDBalance
to handle userDebt
, it may revert because it's less than the incorrectly inflated figure of scaledUserDebt
.
Mitigation
File: contracts/core/pools/StabilityPool/StabilityPool.sol
449: function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
450: _update();
451: // Get the user's debt from the LendingPool.
452: uint256 userDebt = lendingPool.getUserDebt(userAddress);
- 453: uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
454:
455: if (userDebt == 0) revert InvalidAmount();
456:
457: uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
- 458: if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
+ 458: if (crvUSDBalance < userDebt) revert InsufficientBalance();
459:
460: // Approve the LendingPool to transfer the debt amount
- 461: bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
+ 461: bool approveSuccess = crvUSDToken.approve(address(lendingPool), userDebt);
462: if (!approveSuccess) revert ApprovalFailed();
463: // Update lending pool state before liquidation
464: lendingPool.updateState();
465:
466: // Call finalizeLiquidation on LendingPool
467: lendingPool.finalizeLiquidation(userAddress);
468:
- 469: emit BorrowerLiquidated(userAddress, scaledUserDebt);
+ 469: emit BorrowerLiquidated(userAddress, userDebt);
470: }