Core Contracts

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

The collateralization rate check in LendingPool is enforced incorrectly, leading to undercollaterized borrow

Summary

The collateralization rate check is not enforced properly. Usually, when a protocol states that collateralzation rate is 80%, this means that for every 100 usd value worth of collateral deposited, the user can borrow up to 80 usd value.

In the protocol, the rate seems to be the other way round. The rate is multiplied on the collateral instead of the debt, the user can borrow more than the collateral, which leads to undercollaterized positions.

Vulnerability Details

In the borrow function, the collateralValue is obtained, followed by the existing debt value. Then, the function checks if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)), revert, which is the core issue here.

function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
if (isUnderLiquidation[msg.sender]) revert CannotBorrowUnderLiquidation();
UserData storage user = userData[msg.sender];
> uint256 collateralValue = getUserCollateralValue(msg.sender);
if (collateralValue == 0) revert NoCollateral();
// Update reserve state before borrowing
ReserveLibrary.updateReserveState(reserve, rateData);
// Ensure sufficient liquidity is available
_ensureLiquidity(amount);
// Fetch user's total debt after borrowing
> uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
// Ensure the user has enough collateral to cover the new debt
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
// Update user's scaled debt balance
uint256 scaledAmount = amount.rayDiv(reserve.usageIndex);
// Mint DebtTokens to the user (scaled amount)
(bool isFirstMint, uint256 amountMinted, uint256 newTotalSupply) = IDebtToken(reserve.reserveDebtTokenAddress).mint(msg.sender, msg.sender, amount, reserve.usageIndex);

Taking a closer look:

// Ensure the user has enough collateral to cover the new debt
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}

Let's say collateralValue is 1000 USD worth. The totaldebt, with the liquidationThreshold of 80%, should not be more than 800 USD worth.

In the check, the totalDebt is multiplied by the liquidation threshold, meaning the totalDebt can exceed 1000 USD, which should ideally be in a liquidation position already.

Impact

Undercollaterized position, users can borrow more than their collateral, leading to bad debt accumulation and ultimately insolvency.

Tools Used

Manual Review

Recommendations

The collateral value should be multiplied by the liquidation threshold instead of the debt value.

This means that the debt cannot go above the collaterization rate (eg 800 in the above example)

if (collateralValue.percentMul(liquidationThreshold) < userTotalDebt) {
revert NotEnoughCollateralToBorrow();
}

If (800 < 801), revert.

Updates

Lead Judging Commences

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