Core Contracts

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

Incorrect Error Emission in initiateLiquidation Function Causes Misleading Reverts

Summary

The initiateLiquidation function in the LendingPool contract is designed to begin the liquidation process if a user's health factor falls below a specified threshold. However, the function currently reverts with the error HealthFactorTooLow() when the user's health factor is above the liquidation threshold. This is incorrect according to the intended design; it should instead revert with NotUnderLiquidation(). As a result, callers receive misleading error messages, leading to confusion and potential mis-handling of error cases in the system.

Vulnerability Details

How It Begins

  • Current Logic:
    The function first checks if the user is already under liquidation, then updates the reserve state and calculates the user's health factor. If the health factor is above the liquidation threshold, it reverts with HealthFactorTooLow().

    uint256 healthFactor = calculateHealthFactor(userAddress);
    // @info: wrong error emission: HealthFactorTooLow
    // should be: NotUnderLiquidation()
    if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
  • Intended Behavior:
    When a user’s health factor is above the threshold, the function should revert with NotUnderLiquidation(), signaling that the user is not eligible for liquidation. The current implementation incorrectly uses HealthFactorTooLow(), which misleadingly suggests that the health factor is too low when it is, in fact, healthy.

Proof of Concept

Scenario Example

  1. Setup:

    • A user (e.g., Alice) deposits collateral and borrows funds such that her health factor remains above the liquidation threshold.

  2. Liquidation Initiation Attempt:

    • Bob attempts to initiate liquidation on Alice by calling initiateLiquidation(ALICE).

    • Since Alice's health factor is above the threshold, the function should revert with NotUnderLiquidation().

    • Instead, it reverts with HealthFactorTooLow(), which is misleading and does not correctly communicate the true state of Alice’s account.

  3. Test Suite Verification:

    • A test case confirms that when a healthy user is targeted for liquidation, the revert error emitted is HealthFactorTooLow(). The expected behavior is for the function to revert with NotUnderLiquidation(), indicating that liquidation should not be initiated.

Test Suite Code

function testIncorrectErrorEmissionInInitiateLiquidation() public {
uint256 tokenId = 1;
uint256 housePrice = 1000e18;
vm.startPrank(RAAC_HOUSE_PRICES_OWNER);
// Set oracle for house price
raacHousePrices.setOracle(RAAC_HOUSE_PRICES_ORACLE);
vm.stopPrank();
vm.startPrank(RAAC_HOUSE_PRICES_ORACLE);
// Set house price in USD
raacHousePrices.setHousePrice(tokenId, housePrice);
vm.stopPrank();
// Mint reserve assets for Alice
crvUsdToken.mint(ALICE, 1000e18);
vm.startPrank(ALICE);
crvUsdToken.approve(address(lendingPool), 1000e18);
// Deposit reserve assets into the LendingPool
lendingPool.deposit(1000e18);
vm.stopPrank();
// Alice mints and deposits an NFT as collateral
erc20Mock.mint(ALICE, housePrice);
vm.startPrank(ALICE);
erc20Mock.approve(address(raacNft), housePrice);
raacNft.mint(tokenId, housePrice);
raacNft.approve(address(lendingPool), tokenId);
lendingPool.depositNFT(tokenId);
vm.stopPrank();
// Alice borrows funds ensuring her health factor is above the liquidation threshold
uint256 borrowAmount = 200e18; // set to a value that keeps health factor healthy
vm.startPrank(ALICE);
lendingPool.borrow(borrowAmount);
vm.stopPrank();
uint256 aliceHealthFactor = lendingPool.calculateHealthFactor(ALICE);
require(aliceHealthFactor >= lendingPool.healthFactorLiquidationThreshold(), "Test setup: health factor must be above threshold");
// Bob attempts to initiate liquidation on Alice
vm.startPrank(BOB);
// vm.expectRevert(bytes4(keccak256("NotUnderLiquidation()"))); // Expected error should be NotUnderLiquidation
vm.expectRevert(bytes4(keccak256("HealthFactorTooLow()")));
lendingPool.initiateLiquidation(ALICE);
vm.stopPrank();
}

How to Run the Test

  1. Create a Foundry Project:
    Open your terminal and run:

forge init my-foundry-project
  1. Place Contract Files:
    Place all relevant contract files (e.g., LendingPool.sol, RAACNFT.sol, RAACHousePrices.sol, etc.) into the src directory of your project.

  2. Create Test Directory:
    Create a directory named test adjacent to the src directory, and add the test file (e.g., PoolsTest.t.sol) containing the test suite code provided above.

  3. Run the Test:
    Execute the following command in your terminal:

forge test --mt testIncorrectErrorEmissionInInitiateLiquidation -vv

This command will run the specific test case with verbose output, allowing you to verify that the correct error (NotUnderLiquidation()) is not being emitted when the user's health factor is above the liquidation threshold.

Impact

  • Misleading Error Handling:
    The incorrect error emission (HealthFactorTooLow() instead of NotUnderLiquidation()) leads to confusion for integrators and users, as the error message does not accurately reflect the user’s actual liquidation status.

  • Incorrect System Behavior:
    Downstream systems or automated monitoring tools that depend on specific error codes for handling liquidation logic might misinterpret the state, potentially leading to incorrect responses or failed error handling.

  • Operational Inefficiency:
    Developers and auditors may waste time troubleshooting this misleading error, impacting the overall reliability and maintainability of the protocol.

Tools Used

  • Manual Review

  • Foundry (Forge)

Recommendations

Update the error check in the initiateLiquidation function to revert with NotUnderLiquidation() when the user's health factor is above the liquidation threshold. This change will ensure that the error message accurately reflects that the user is not eligible for liquidation.

Recommended Diff

@@ In LendingPool.sol, function initiateLiquidation:
- if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
+ if (healthFactor >= healthFactorLiquidationThreshold) revert NotUnderLiquidation();
Updates

Lead Judging Commences

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!