Core Contracts

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

Broken Liquidation Check Prevents Protocol Safety Mechanism From Executing

Summary and Impact

The LendingPool contract's liquidation mechanism contains a critical logical error that completely inverts the intended behavior of the protocol's safety system. The initiateLiquidation function's conditional check is backward, causing the function to revert precisely when it should execute and proceed when it should revert.

This fundamentally breaks the protocol's core safety mechanism. Per the documentation, RAAC is designed to "deeply integrate real estate within on-chain finance rails for seamless accessibility, composability, stability and capital efficiency." The liquidation system is essential to maintaining this stability - without it, the entire lending system becomes unsafe as bad debt cannot be cleared.

Looking at the protocol documentation:

  • Borrower: NFT Owner that collateralizes their NFT and borrows CRVUSD against them.

  • Collector: Contracts that receive swap taxes and similar revenue (FeeCollector).

These roles depend on a functioning liquidation system to maintain protocol solvency. The current implementation makes this impossible.


Vulnerability Details

The issue lies in the liquidation check condition in LendingPool.sol:

function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
uint256 healthFactor = calculateHealthFactor(userAddress);
// BUG: Reverts if healthFactor >= threshold rather than < threshold
if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
isUnderLiquidation[userAddress] = true;
liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
}

The condition if (healthFactor >= healthFactorLiquidationThreshold) means:

  1. When health factor is GOOD (>= threshold) -> function reverts

  2. When health factor is BAD (< threshold) -> function continues

  3. However, the revert message is "HealthFactorTooLow", which is exactly backward

I've created a test demonstrating this:

it("Should fail to liquidate undercollateralized positions due to incorrect condition", async function () {
// Create undercollateralized position
await raacNFT.connect(borrower).mint(1, ethers.utils.parseEther("100"));
await raacNFT.connect(borrower).approve(lendingPool.address, 1);
await lendingPool.connect(borrower).depositNFT(1);
await lendingPool.connect(borrower).borrow(ethers.utils.parseEther("80"));
// Drop collateral value by 50%
await priceOracle.setHousePrice(1, ethers.utils.parseEther("50"));
// Try to liquidate - should work but fails
await expect(
lendingPool.connect(liquidator).initiateLiquidation(borrower.address)
).to.be.revertedWith("HealthFactorTooLow");
// Position remains unliquidated despite being underwater
const isUnderLiquidation = await lendingPool.isUnderLiquidation(borrower.address);
expect(isUnderLiquidation).to.be.false;
});

This means:

  1. Underwater positions cannot be liquidated

  2. Bad debt accumulates in the system

  3. Protocol becomes increasingly insolvent

  4. No way to recover collateral from defaulted positions


Tools Used

  • Manual Review

  • Hardhat


Recommendations

The condition should be reversed to match the intended logic:

function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
uint256 healthFactor = calculateHealthFactor(userAddress);
// Correct check for undercollateralization
if (healthFactor < healthFactorLiquidationThreshold) {
isUnderLiquidation[userAddress] = true;
liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
} else {
revert PositionHealthy();
}
}

This aligns with the protocol's documentation and intended safety mechanisms, allowing liquidations to occur when positions become undercollateralized.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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