Summary
attackers can deposit NFTs, borrow max funds, and transfer funds externally before price updates.
They can then repay when the price drops, leading to unfair profit loops.
Vulnerability Details
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();
ReserveLibrary.updateReserveState(reserve, rateData);
_ensureLiquidity(amount);
uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount;
if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) {
revert NotEnoughCollateralToBorrow();
}
uint256 scaledAmount = amount.rayDiv(reserve.usageIndex);
(bool isFirstMint, uint256 amountMinted, uint256 newTotalSupply) = IDebtToken(reserve.reserveDebtTokenAddress).mint(msg.sender, msg.sender, amount, reserve.usageIndex);
IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount);
user.scaledDebtBalance += scaledAmount;
reserve.totalUsage = newTotalSupply;
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, 0, amount);
_rebalanceLiquidity();
emit Borrow(msg.sender, amount);
}
Impact
Tools Used
Recommendations
Implement a cooldown period for borrowing after repayment.
Limit borrowing frequency per block.
mapping(address => uint256) public lastBorrowTimestamp;
uint256 public constant BORROW_COOLDOWN = 10 minutes;
function borrow(uint256 amount) external nonReentrant whenNotPaused
{ require(block.timestamp > lastBorrowTimestamp[msg.sender] + BORROW_COOLDOWN, "Borrow cooldown active");
lastBorrowTimestamp[msg.sender] = block.timestamp; // Borrow logic }