Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

delay between `LendingPool.sol::initiateLiquidation()` and `StabilityPool.sol::liquidateBorrower()` can lead to liquidation of healthy position as well.

Summary

When healthFactor of position is less than threshold, user can initiates the liquidation of position. and there is grace period under which liquidation finalization should be done. But this also lead to liquidation of healthy position as well.

LendingPool.sol::initiateLiquidation() ->

function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
// update state
ReserveLibrary.updateReserveState(reserve, rateData);
UserData storage user = userData[userAddress];
if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
isUnderLiquidation[userAddress] = true;
liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
}

StabilityPool.sol::liquidateBorrower()->

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
// Get the user's debt from the LendingPool.
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();
// Approve the LendingPool to transfer the debt amount
bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
if (!approveSuccess) revert ApprovalFailed();
// Update lending pool state before liquidation
lendingPool.updateState();
// Call finalizeLiquidation on LendingPool
lendingPool.finalizeLiquidation(userAddress);
emit BorrowerLiquidated(userAddress, scaledUserDebt);
}

Vulnerability Details

  1. A position health factor was bad, so initialization is started.

  2. now suppose before finalization, the health factor restored to healthy value.

  3. but in liquidateBorrower() it's not re-checked weather position's health is good or bad.

  4. since, checking isn't done, it means heathy position will also be liquidated.

Impact

Loss to user and bad reputation to protocol as a healthy position is liquidated too.

Tools Used

Manual

Recommendations

Inside liquidateBorrower() recheck the heath factor of position, and perform action accordingly.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::finalizeLiquidation() never checks if debt is still unhealthy

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.