Core Contracts

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

The closeLiquidation function incorrectly checks if debt has been fully repaid instead of checking the healthFactor

Summary

The function closeLiquidation doesn't check the health factor of the debt position, but instead checks if the user has repaid the whole debt. This would wrongly liquidate the user if either the price of the collateral has increased or if the user has repaid part of the debt during the grace period.

Vulnerability Details

A user gets liquidated in 2 steps. First anyone can call the initiateLiquidation by specifying the user address to liquidate. Then when the grace period has finished the user can get liquidated. During the grace period the user has to repay the whole debt to prevent being liquidated. There are 2 scenario where the user could be wrongly liquidated due to not checking the health factor in closeLiquidation function even if the health factor is above liquidation
threshold :

First scenario is if during the grace period , the price of collateral increases such that the user health factor is above the healthFactorLiquidationThreshold.

Second scenario is when the user repays part of the debt such that the health factor increases above healthFactorLiquidationThreshold.

The liquidation is initiated by checking the health factor in the function to initiate liquidation.

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

But this isn't implemented when checking to close the liquidation:

function closeLiquidation() external nonReentrant whenNotPaused {
address userAddress = msg.sender;
if (!isUnderLiquidation[userAddress]) revert NotUnderLiquidation();
// update state
ReserveLibrary.updateReserveState(reserve, rateData);
if (block.timestamp > liquidationStartTime[userAddress] + liquidationGracePeriod) {
revert GracePeriodExpired();
}
UserData storage user = userData[userAddress];
uint256 userDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex);
if (userDebt > DUST_THRESHOLD) revert DebtNotZero();
isUnderLiquidation[userAddress] = false;
liquidationStartTime[userAddress] = 0;
emit LiquidationClosed(userAddress);
}

mpact

User can get wrongly liquidated.

Recommendations

Check the health factor in the closeLiquidation function.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 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.