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 10 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.

Give us feedback!