Core Contracts

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

Lack of incentives for users to call LendingPool::initiateLiquidation allows extensive delay between when health factor dropped below threshold and when grace period starts

Summary

In the liquidation system of the RAAC Lending Protocol, it allows users to delay liquidation due to lack of incentives for any one to call LendingPool::initiateLiquidation function on a liquidatable address. Since the liquidation grace period starts only when this function is called, users can remain in an unhealthy state for an extended period, artificially extending their grace period and avoiding liquidation longer than intended.

This flaw introduces systemic risk to the lending pool by allowing bad debt to accumulate while reducing incentives for liquidators. If left unaddressed, it could destabilize the protocol by creating an inefficient liquidation process and a potential vector for market manipulation.

Vulnerability Details

In the current implementation of LendingPool::initiateLiquidation, the grace period timer starts when liquidation is initiated rather than when the user first becomes liquidatable (i.e., when their health factor drops below the threshold).

**
* @notice Allows anyone to initiate the liquidation process if a user's health factor is below threshold
* @param userAddress The address of the user to liquidate
*/
function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
// update state
ReserveLibrary.updateReserveState(reserve, rateData);
UserData storage user = userData[userAddress];
uint256 healthFactor = calculateHealthFactor(userAddress);
if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
isUnderLiquidation[userAddress] = true;
liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
}

How the Exploit Works

A user’s health factor drops below the liquidation threshold—making them eligible for liquidation. No one immediately calls initiateLiquidation, so the user remains liquidatable but not yet in liquidation .Days or weeks later, LendingPool::initiateLiquidation is finally called—but this starts the grace period from that moment rather than from when they originally became liquidatable. The user to be liquidated now gets an extended grace period, even though they were ineligible for liquidation much earlier.

This means the user can remain liquidatable indefinitely until liquidation is manually triggered, effectively doubling the grace period.
If liquidations are not executed promptly, the lending pool absorbs risk from unhealthy loans. A user with inside knowledge can delay liquidation and time market events to exploit price fluctuations.

Proof Of Code (POC)

This vulnerability was tested in protocols-tests.js, specifically within the "StabilityPool" test suite. The following test demonstrates how a user remains liquidatable but avoids liquidation for an extended period before liquidation is finally triggered, artificially extending their grace period:

it("no incentive to call initiate liquidity", async function () {
//c for testing purposes
// User deposits NFT and borrows funds
const newTokenId = HOUSE_TOKEN_ID + 2;
await contracts.housePrices.setHousePrice(newTokenId, HOUSE_PRICE);
await contracts.crvUSD
.connect(user2)
.approve(contracts.nft.target, HOUSE_PRICE);
await contracts.nft.connect(user2).mint(newTokenId, HOUSE_PRICE);
await contracts.nft
.connect(user2)
.approve(contracts.lendingPool.target, newTokenId);
await contracts.lendingPool.connect(user2).depositNFT(newTokenId);
await contracts.lendingPool
.connect(user2)
.borrow(ethers.parseEther("80"));
const initialblocktimestamp = (await ethers.provider.getBlock("latest"))
.timestamp;
console.log(`initialblocktimestamp: ${initialblocktimestamp}`);
// The user’s health factor drops below the threshold
await contracts.housePrices.setHousePrice(
newTokenId,
ethers.parseEther("50")
);
// No one calls `initiateLiquidation` for a long time, allowing the user to remain liquidatable but not in liquidation
await time.increase(7 * 24 * 60 * 60); // Simulating 7 days
await ethers.provider.send("evm_mine", []);
// Now someone finally calls `initiateLiquidation`
await contracts.lendingPool
.connect(user3)
.initiateLiquidation(user2.address);
const updatedblocktimestamp = (await ethers.provider.getBlock("latest"))
.timestamp;
console.log(`updatedblocktimestamp: ${updatedblocktimestamp}`);
// Grace period starts now, giving the user even more time to recover when in reality, the user has had way over the grace period to recover
const graceperiod =
await contracts.lendingPool.BASE_LIQUIDATION_GRACE_PERIOD();
assert(updatedblocktimestamp - initialblocktimestamp > graceperiod);
});

Impact

This vulnerability affects the core liquidation mechanism of the protocol, which is essential for maintaining capital efficiency and solvency. Without timely liquidations:

Risky loans remain open for longer, increasing exposure to bad debt.
The protocol loses efficiency, as unhealthy positions are not cleared promptly.
Large-scale abuse could lead to cascading failures if multiple users exploit the delay.

Tools Used

Manual Review, Hardhat

Recommendations

Incentivize Timely Liquidation Calls
To ensure that liquidations are triggered as soon as possible, introduce a small fee incentive for users who successfully find and report liquidatable accounts. With incentives in place, liquidators are incentivized to report bad loans faster, reducing the delay between a user falling below the threshold and being liquidated.

Updates

Lead Judging Commences

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

Appeal created

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

No incentive to liquidate

Support

FAQs

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

Give us feedback!