Core Contracts

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

Healthy positions may be incorrectly liquidated leading to loss of funds

Summary

The liquidation process consists of two steps. First, liquidation is initiated by calculating the user's current health factor. If the health factor is below the threshold, the user's position is flagged for liquidation. If the user does not repay their debt in grace period, the process enters its final phase with a call to LendingPool.sol::finalizeLiquidation(). However, due to a missing check for the current health factor at the time of finalization, users can be incorrectly liquidated even if their position has recovered.

Vulnerability Details

The issue arises because liquidation is not an atomic process. When LendingPool::initiateLiquidation() is called, a user's collateral might be below the required threshold. However, liquidation can only be finalized after a one-week grace period. During this time, the price of the user's collateral may change. If the asset price increases and their position becomes healthy again, they should no longer be eligible for liquidation. However, because the finalization step does not re-evaluate the health factor, liquidation may still proceed incorrectly, resulting in an unfair loss for the user.

Impact

Users with a healthy position may still be liquidated, leading to unnecessary loss of funds.

PoC

Let's consider following scenario:

  1. Bob deposits an NFT worth $1,000 as collateral.

  2. Bob takes a loan of $500.

  3. At some point, Bob’s collateral value drops to $700, triggering the liquidation process.

  4. One week later (after the grace period), the price of Bob’s NFT rises to $2,000, making his position healthy again.

  5. However, since finalizeLiquidation() does not re-evaluate the health factor, Bob's position is still liquidated.

  6. As a result, Bob unfairly loses his funds despite having sufficient collateral at the time of liquidation.

Tools Used

Manual review

Recommendations

Since liquidation is not an atomic operation, consider implementing an additional check in LendingPool.sol::finalizeLiquidation() to prevent liquidation if the user's current health factor has recovered.

For example, add the following check:

uint256 healthFactor = calculateHealthFactor(userAddress);
if (healthFactor >= healthFactorLiquidationThreshold) {
isUnderLiquidation[userAddress] = false;
liquidationStartTime[userAddress] = 0;
};
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.