Core Contracts

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

Lack of Minimum Borrow Amount in `borrow` Function in LendingPool contract

Summary

The borrow function does not enforce a minimum borrow amount, allowing users to borrow very small amounts (e.g., 1 wei). This can lead to the creation of small, uneconomical positions that are difficult or costly to liquidate, increasing the risk to the protocol and other users.

Vulnerability Details

Root Cause:

  • The borrow function does not include a check for a minimum borrow amount.

  • Users can borrow very small amounts, creating positions that are not worth liquidating due to high gas costs relative to the value of the position.

Impact

  • Small Positions: Small borrows can result in positions that are uneconomical to liquidate.

  • Inefficient Liquidation: Liquidators may avoid small positions because the gas costs outweigh the potential rewards.

  • Increased Risk: Small, undercollateralized positions may remain un-liquidated, increasing the risk to the protocol and other users.

PoC

  1. A user borrows a very small amount of reserve assets (e.g., 1 wei) using their NFT collateral.

  2. The user's position becomes undercollateralized, but the liquidation process is not initiated because the gas costs outweigh the potential rewards.

  3. The small position remains un-liquidated, increasing the risk to the protocol and other users.

Tools Used

Manual Review

Recommendations

Implement Minimum Borrow Amount:

  • Add a check to ensure the borrow amount is above a minimum threshold.

  • Example:

uint256 public constant MIN_BORROW_AMOUNT = 1 ether; // Minimum borrow amount in reserve asset units

2. Update borrow Function:

  • Add the minimum borrow amount check to the borrow function.

  • Example:

function borrow(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
if (amount < MIN_BORROW_AMOUNT) revert BorrowAmountTooSmall();
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);
// Transfer borrowed amount to user
IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount);
user.scaledDebtBalance += scaledAmount;
reserve.totalUsage = newTotalSupply;
// Update liquidity and interest rates
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, 0, amount);
// Rebalance liquidity after borrowing
_rebalanceLiquidity();
emit Borrow(msg.sender, amount);
}
Updates

Lead Judging Commences

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

Support

FAQs

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