Core Contracts

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

Users Can Borrow More Than Collateral Value Due To Incorrect LTV Check In `LendingPool::borrow`

Summary

Due to an incorrect check in LendingPool::borrow, users can borrow more than their collateral value allows which enables immediate liquidation. The percentage math is being applied to the wrong value in the collateral check.

Vulnerability Details

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L344-L346)

function borrow(uint256 amount) external {
uint256 collateralValue = getUserCollateralValue(msg.sender);
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
// Incorrect check allows excessive borrowing
@> if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
}

Example showing current flawed logic:

// User has a NFT worth 100e18
uint256 collateralValue = 100e18;
// User tries to borrow 90e18
uint256 borrowAmount = 90e18;
// liquidationThreshold is 85%
// Current check:
@> if (collateralValue < borrowAmount.percentMul(85)) // 100e18 < 90e18 * 0.85
revert NotEnoughCollateralToBorrow();
// 100e18 < 76.5e18 -> Check passes

This allows a user to borrow 90e18 against 100e18 collateral (90% LTV) which should not be possible with an 85% liquidation threshold.

The correct check should be:

// User has a NFT worth 100e18
uint256 collateralValue = 100e18;
// User tries to borrow 90e18
uint256 borrowAmount = 90e18;
// With max LTV of 80%
// Check fixed should be:
if (borrowAmount > collateralValue.percentMul(80)) // 90e18 > 100e18 * 0.8
revert NotEnoughCollateralToBorrow();
// 90e18 > 80e18 -> Reverts as it should

Proof of Code:
Add this test to LendingPool.test.js

describe("Borrowing Limits", function () {
it.only("should not allow user to borrow more than accepted ratio", async function () {
// Mint NFT for user2
const tokenId = 2;
const nftPrice = ethers.parseEther("100");
// Set price for the second NFT
await raacHousePrices.setHousePrice(tokenId, nftPrice);
// Mint NFT for user2
await token.mint(user2.address, nftPrice);
await token.connect(user2).approve(raacNFT.target, nftPrice);
await raacNFT.connect(user2).mint(tokenId, nftPrice);
// User1 deposits liquidity - need fresh approval since this is a new scenario
const depositAmount = ethers.parseEther("100");
await crvusd.connect(user2).approve(lendingPool.target, depositAmount);
await lendingPool.connect(user2).deposit(depositAmount);
// User2 sets up collateral
await raacNFT.connect(user2).approve(lendingPool.target, tokenId);
await lendingPool.connect(user2).depositNFT(tokenId);
// Attempt to borrow 1:1 against collateral
// This should break the health factor ratio since it's trying to borrow the full value and revert
await expect(
lendingPool.connect(user2).borrow(depositAmount)
).to.be.reverted;
});
});

Impact

  • Users can immediately borrow more than collateral allows

  • Positions can be liquidated right after borrowing

  • Protocol's collateral guarantees are broken

Tools Used

Manual Review

Recommendations

Fix the check in LendingPool::borrow to correctly calculate maximum borrow amount based on collateral value:

function borrow(uint256 amount) external nonReentrant whenNotPaused {
// ...
uint256 collateralValue = getUserCollateralValue(msg.sender);
- if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
+ uint256 maxBorrowAmount = collateralValue.percentMul(liquidationThreshold);
+ if (userTotalDebt > maxBorrowAmount) {
revert NotEnoughCollateralToBorrow();
}
// ...
}

This ensures users can only borrow up to the configured liquidationThreshold percentage of their collateral value.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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.