Core Contracts

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

Unsafe Liquidation Threshold Decreases Can Trigger Immediate Position Liquidations

Relevant Context

The LendingPool contract uses a liquidation threshold to determine when positions become eligible for liquidation. This threshold represents the minimum collateralization ratio that users must maintain. The liquidationThreshold parameter is configurable by the contract owner through the setParameter() function.

Finding Description

The setParameter() function allows immediate decreases to the liquidationThreshold without any time delay or restrictions. The threshold is used in calculateHealthFactor() to determine if a position is liquidatable:

uint256 collateralThreshold = collateralValue.percentMul(liquidationThreshold);
return (collateralThreshold * 1e18) / userDebt;

A decrease in liquidationThreshold directly reduces the calculated health factor of all positions. If the threshold is decreased significantly, positions that were previously well-collateralized could become instantly liquidatable without giving users time to adjust their positions.

Impact Explanation

High. A sudden decrease in the liquidation threshold could force multiple user positions into liquidation simultaneously, potentially leading to significant losses for users who have no time to add collateral or reduce their debt.

Likelihood Explanation

Low. Liquidation threshold adjustments are typically done conservatively and gradually to maintain protocol stability. Most users maintain collateral ratios well above the minimum threshold as a safety buffer, so small to moderate threshold decreases would affect only a limited number of positions.

Proof of Concept

  1. Initial liquidationThreshold is 80% (8000 basis points)

  2. User deposits NFT worth 100 ETH and borrows 71 ETH

  3. Current health factor calculation:

    collateralThreshold = 100 ETH * 80% = 80 ETH
    healthFactor = (80 ETH * 1e18) / 71 ETH ≈ 1.13e18
  4. With healthFactorLiquidationThreshold = 1e18, the position is healthy (1.13e18 > 1e18)

  5. Owner calls setParameter(OwnerParameter.LiquidationThreshold, 70_00) to decrease threshold to 70%

  6. New health factor calculation:

    collateralThreshold = 100 ETH * 70% = 70 ETH
    healthFactor = (70 ETH * 1e18) / 71 ETH ≈ 0.986e18
  7. Position becomes liquidatable as the health factor (0.986e18) is below the healthFactorLiquidationThreshold (1e18)

  8. Anyone can successfully call initiateLiquidation() on the position since the check if (healthFactor >= healthFactorLiquidationThreshold) will pass

Recommendation

Implement safety measures for liquidation threshold updates:

function setParameter(OwnerParameter param, uint256 newValue) external override onlyOwner {
if (param == OwnerParameter.LiquidationThreshold) {
require(newValue <= 100_00, "Invalid liquidation threshold");
+ require(newValue >= liquidationThreshold, "Can only increase threshold");
liquidationThreshold = newValue;
emit LiquidationParametersUpdated(liquidationThreshold, healthFactorLiquidationThreshold, liquidationGracePeriod);
}
// ... rest of the function
}

If threshold decreases are necessary, implement a timelock mechanism that delays the effect of the decrease, giving users time to adjust their positions.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!