The LendingPool
contract contains a critical logical error in its liquidation mechanism. Specifically, the initiateLiquidation
function reverts whenever the healthFactor
is greater than or equal to the configured threshold, inverting the expected behavior. As a result, healthy positions (those with a sufficiently high healthFactor
) are incorrectly treated as undercollateralized, while positions that genuinely require liquidation may not be handled properly. This misalignment undermines the protocol’s core risk management, allowing or preventing liquidations under the wrong conditions.
Within the initiateLiquidation
function, the contract checks whether a user’s healthFactor
is greater than or equal to the healthFactorLiquidationThreshold
and then reverts with HealthFactorTooLow()
. This condition is inverted, as a higher or equal healthFactor
typically indicates a safer (more solvent) position rather than one that should be liquidated. Consequently, healthy positions are incorrectly deemed undercollateralized, and genuinely unsafe positions may remain exempt from liquidation. This flawed check disrupts the protocol’s core risk management logic by enabling or disallowing liquidations based on the wrong criteria.
The inverted check in the initiateLiquidation
function undermines the protocol’s core risk management. Healthy positions, which should remain exempt from liquidation, can be improperly flagged as undercollateralized, forcing unnecessary liquidations. Meanwhile, genuinely insolvent borrowers may avoid liquidation. This deviation from the intended collateral management logic jeopardizes the overall stability of the system, leading to potential defaults, loss of confidence, and financial damage for the protocol and its users.
The contract uses the following logic in the initiateLiquidation
function to determine if a user is eligible for liquidation:
The expected behavior is to prevent liquidation if the healthFactor
is above (or equal to) the threshold, because a high health factor normally indicates solvency. However, the revert message (HealthFactorTooLow
) suggests the opposite condition, effectively reversing the intended logic. Consequently, a user with a sufficiently high (healthy) healthFactor
would be inappropriately subject to liquidation.
Focusing on the relevant portion:
( ! ) Correct liquidation logic should require healthFactor < healthFactorLiquidationThreshold
to proceed.
The problem lies in the condition if (healthFactor >= healthFactorLiquidationThreshold)
triggering a revert with a message “HealthFactorTooLow.” In standard lending protocols, a user is subject to liquidation when their health factor is below a critical threshold (e.g., < 1). If the healthFactor
is greater or equal to the threshold, the user should not be liquidated because their collateral is sufficient. Here, however, the contract does the opposite, reverting for solvent users and allowing liquidation only if healthFactor < healthFactorLiquidationThreshold
. This reverses the typical lending logic and can lead to incorrect or inconsistent liquidation events.
A user has a health factor of 2.0
, well above the liquidation zone.
The liquidation threshold (healthFactorLiquidationThreshold
) is set to 1.0
.
Calling initiateLiquidation(userAddress)
causes the condition healthFactor >= 1.0
to trigger a revert with HealthFactorTooLow
. This is contradictory, as that user should not be considered undercollateralized.
As a result, the comparison is inverted, potentially disallowing liquidations that should happen or confusingly allowing them under incorrect conditions.
In this test, the user has a high Health Factor (well above the liquidation threshold), yet the contract still reverts with HealthFactorTooLow
. This confirms that the liquidation logic is inverted: instead of allowing well-collateralized positions to remain safe, the contract incorrectly treats them as undercollateralized.
Add the following test inside the describe("Liquidation", function () {})
block in test/unit/core/pools/LendingPool/LendingPool.test.js
.
The revert message (HealthFactorTooLow()
) does not align with the condition (healthFactor >= healthFactorLiquidationThreshold
).
Logically, the system should revert if healthFactor
is below the threshold, not if it is above or equal.
This confirms a logic error that may undermine the security and correctness of the liquidation process.
Manual Code Review
A thorough, line-by-line inspection of the contract’s code was performed, focusing on the initiateLiquidation
function and related liquidation logic to uncover the inverted condition.
Use a correct comparison in the initiateLiquidation
function to align with typical lending protocols. Specifically, revert when the healthFactor
is below the threshold, rather than above. Adjust the revert message accordingly to match the intended logic:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.