Core Contracts

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

Incorrect Liquidation Threshold Application

Summary

The liquidation threshold (LT) in the withdrawal and borrow check is applied incorrectly to the user's debt instead of their collateral. This could allow users to withdraw collateral in situations where they should not be allowed to, potentially leading to undercollateralization.

Vulnerability Details

Issue

The current implementation of the withdrawal condition is:

if (collateralValue - nftValue < userDebt.percentMul(liquidationThreshold)) {
revert WithdrawalWouldLeaveUserUnderCollateralized();
}

The current implementation of the debt condition is:

if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
  • Problem: The liquidationThreshold should be applied to the collateral, not the debt.

  • Incorrect Logic: The condition incorrectly compares the remaining collateral to a fraction of the debt, which can allow withdrawals that would leave the user undercollateralized.

  • Correct Logic: The proper check should ensure that the post-withdrawal collateral is still sufficient to cover the debt based on the liquidation threshold:

if ((collateralValue - nftValue).percentMul(liquidationThreshold) < userDebt) {
revert WithdrawalWouldLeaveUserUnderCollateralized();
}

Example of Incorrect Behavior

Parameter Value
collateralValue 10 ETH
nftValue 2 ETH
userDebt 6 ETH
liquidationThreshold 80% (0.8)

Incorrect Calculation:

if (10 - 2 < 6 * 0.8) { // 8 < 4.8 (Incorrect comparison)
revert;
}
  • This incorrectly allows withdrawal since 8 ETH is greater than 4.8 ETH, but the correct threshold should be checked against 6 ETH.

Correct Calculation:

if ((10 - 2) * 0.8 < 6) { // 6.4 < 6 (Correct comparison)
revert;
}
  • This correctly prevents undercollateralized withdrawals.

Poc

run in LendingPool..test.js

it("should allow user to borrow more than allowed", async function () {
const borrowAmount = ethers.parseEther("120");
await lendingPool.connect(user1).borrow(borrowAmount);
const collateral = await lendingPool.getUserCollateralValue(user1.address);
const debtBalance = await debtToken.balanceOf(user1.address);
expect(debtBalance).to.gt(collateral);
});

Impact

  • ** Under-Collateralization:** Users are able to withdraw more collateral than they should, leading to situations where their remaining collateral is insufficient to cover their outstanding debt.

  • Economic Losses: If users are undercollateralized, liquidations may not be able to fully recover the protocol’s losses resulting in bad debt

  • user can steal funds from protocol.

Tools Used

  • Manual Code Review

Recommendations

Apply LT to Collateral Instead of Debt: Modify the condition as follows:

if ((collateralValue - nftValue).percentMul(liquidationThreshold) < userDebt) {
revert WithdrawalWouldLeaveUserUnderCollateralized();
}
Updates

Lead Judging Commences

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

LendingPool::borrow as well as withdrawNFT() reverses collateralization check, comparing collateral < debt*0.8 instead of collateral*0.8 > debt, allowing 125% borrowing vs intended 80%

Support

FAQs

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