Core Contracts

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

Unsafe Borrowing Allows Debt to Exceed Collateral Threshold

Summary

The LendingPool contract allows users to borrow against their collateral value, but the current implementation permits borrowing amounts that could exceed the safe liquidation threshold. This creates potential insolvency risks for the protocol.

function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
// ... initial checks ...
// Critical calculation of collateral value
uint256 collateralValue = getUserCollateralValue(msg.sender);
// ... liquidity checks ...
// Debt calculation uses current index which can lead to precision issues
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
// Key vulnerability - validation check is reversed
// Should be: userTotalDebt.percentMul(liquidationThreshold) > collateralValue
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
// Scaled amount calculation after validation creates potential inconsistency
uint256 scaledAmount = amount.rayDiv(reserve.usageIndex);
// State updates happen after validation, could lead to race conditions
user.scaledDebtBalance += scaledAmount;
reserve.totalUsage = newTotalSupply;
// ...
}

The key issues are

  1. Validation check has reversed logic for comparing debt to collateral threshold

  2. State updates occur after validation, creating potential race conditions

  3. Scaling calculations happen in multiple steps rather than atomically

Vulnerability Details

Imagine a home equity line of credit where the bank accidentally lets you borrow more than your house is worth. That's exactly what's happening.

When a user deposits their tokenized real estate (RAACNFT) as collateral, they should only be able to borrow up to 80% of its value, the liquidationThreshold. However,

liquidationThreshold = 80_00 // 80% in basis points
collateralValue = getUserCollateralValue(msg.sender)
currentDebt = getUserDebt(msg.sender)
amount = borrow amount
maxBorrow = (collateralValue * liquidationThreshold) / 10000

Let's walk through how this plays out:

A user deposits a RAACNFT worth 100,000 USDC. The protocol's liquidationThreshold of 80% should limit their borrowing to 80,000 USDC. However, due to insufficient validation in the borrow function, they can make multiple smaller borrows that individually pass checks but collectively exceed this limit.

The core issue lies in how the LendingPool tracks debt accumulation:

function borrow(uint256 amount) external {
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex);
// Current validation misses cumulative debt impact
}
}

This creates a real-world risk where users can accumulate debt beyond their collateral's safe threshold, potentially leaving the protocol with bad debt if house prices decline. It's like a credit card that doesn't track your total credit utilization across multiple purchases.

Impact

The LendingPool's borrow function allows borrowing that could push total debt above the safe threshold due to:

// LendingPool.sol
function borrow(uint256 amount) external {
// Missing check for total system debt capacity
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
// Current check insufficient
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
}

total debt (currentDebt + new borrow amount) must stay below the maxBorrow threshold, calculated as a percentage of collateral value.

Attack Flow

  1. User deposits minimal collateral

  2. Makes multiple borrow calls that individually pass validation

  3. Total debt accumulates beyond the safe threshold

  4. Protocol becomes exposed to liquidation risks

Root Cause The LendingPool's borrow validation focuses on individual transactions rather than maintaining invariants about total debt-to-collateral ratios across multiple borrows.

This could lead to under-collateralized positions, threatening protocol solvency if collateral prices decline or liquidations fail to execute efficiently.

Recommendations

Should validate that newTotalDebt <= collateralValue.percentMul(liquidationThreshold) before any state changes.

Updates

Lead Judging Commences

inallhonesty Lead Judge 3 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.