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;
@> if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
}
Example showing current flawed logic:
uint256 collateralValue = 100e18;
uint256 borrowAmount = 90e18;
@> if (collateralValue < borrowAmount.percentMul(85))
revert NotEnoughCollateralToBorrow();
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:
uint256 collateralValue = 100e18;
uint256 borrowAmount = 90e18;
if (borrowAmount > collateralValue.percentMul(80))
revert NotEnoughCollateralToBorrow();
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 () {
const tokenId = 2;
const nftPrice = ethers.parseEther("100");
await raacHousePrices.setHousePrice(tokenId, nftPrice);
await token.mint(user2.address, nftPrice);
await token.connect(user2).approve(raacNFT.target, nftPrice);
await raacNFT.connect(user2).mint(tokenId, nftPrice);
const depositAmount = ethers.parseEther("100");
await crvusd.connect(user2).approve(lendingPool.target, depositAmount);
await lendingPool.connect(user2).deposit(depositAmount);
await raacNFT.connect(user2).approve(lendingPool.target, tokenId);
await lendingPool.connect(user2).depositNFT(tokenId);
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.