Core Contracts

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

LendingPool - Broken Liquidity Buffer due to missing rebalancing in repay function

Summary

The LendingPool contract's _repay function doesn't rebalance liquidity after processing repayments. By skipping the _rebalanceLiquidity() call, the protocol can't maintain its (initial)required 20% liquidity buffer, resulting in either idle capital or insufficient liquidity.

Vulnerability Details

The vulnerability exists in the _repay function where liquidity rebalancing is not performed after repayment processing:

function _repay(uint256 amount, address onBehalfOf) internal {
// ... repayment processing ...
// Transfer reserve assets from the caller to the reserve
IERC20(reserve.reserveAssetAddress).safeTransferFrom(
msg.sender,
reserve.reserveRTokenAddress,
amountScaled
);
reserve.totalUsage = newTotalSupply;
user.scaledDebtBalance -= amountBurned;
// Update liquidity and interest rates
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, amountScaled, 0);
//@audit-issue Missing _rebalanceLiquidity() call here
emit Repay(msg.sender, onBehalfOf, actualRepayAmount);
}

The _rebalanceLiquidity function maintains the protocol's liquidity buffer through two critical flows:

function _rebalanceLiquidity() internal {
uint256 totalDeposits = reserve.totalLiquidity;
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio); // 20%
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(
reserve.reserveRTokenAddress
);
if (currentBuffer > desiredBuffer) {
uint256 excess = currentBuffer - desiredBuffer;
// @audit-info deposit excess into vault
_depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) {
// @audit-info shortage happening, witdraw from the vault.
uint256 shortage = desiredBuffer - currentBuffer;
_withdrawFromVault(shortage);
}
}

The _rebalanceLiquidity function manages liquidity distribution between instant-access reserves and CurveVault deposits. It maintains a 20% buffer ratio by either deploying excess funds to CurveVault or withdrawing from it when reserves fall below the threshold.

Impact

By not calling the _rebalanceLiquidity function, the protocol's liquidity buffer mechanism breaks during repayments, creating two problems.

First, excess funds sit idle in the rToken instead of generating yield, and second, the protocol can't guarantee it has enough liquidity to cover withdrawals since it's not maintaining its required buffer ratio between rToken and funds deposited into CurveVault.

Tools Used

Manual Review

Recommendations

Add the _rebalanceLiquidity() call after updating interest rates in the _repay function:

function _repay(uint256 amount, address onBehalfOf) internal {
// ... existing repayment logic ...
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, amountScaled, 0);
+ _rebalanceLiquidity();
emit Repay(msg.sender, onBehalfOf, actualRepayAmount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::finalizeLiquidation or repay doesn't call _rebalanceLiquidity, leaving excess funds idle instead of depositing them in Curve vault for yield

Support

FAQs

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