Core Contracts

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

The current implementation of LendingPool cant be integrated with curveUsd Vault.

Summary

The function _rebalanceLiquidity(), and _ensureLiquidity() are called during deposit(), withdraw() and borrow() to ensure enough liquidity for user operations. These functions deposit/withdraw the excess/needed liquidity from the curveUSD vault by using _depositIntoVault() and _withdrawFromVault(), both of which assumes that the input parameter amount is the curveUSD held by the LendingPool. But the lendingPool never holds the underlying asset but instead keep them in the RToken contract. This will cause reverts in the major functions when a curveUSDVault is set and break the core functionalities of the pool.

Vulnerability Details

The amount is determined in _ensureLiquidity() and _rebalanceLiquidity(),

function _ensureLiquidity(uint256 amount) internal {
// if curve vault is not set, do nothing
if (address(curveVault) == address(0)) {
return;
}
uint256 availableLiquidity = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (availableLiquidity < amount) {
uint256 requiredAmount = amount - availableLiquidity;
// Withdraw required amount from the Curve vault
_withdrawFromVault(requiredAmount);
}
}
/**
* @notice Rebalances liquidity between the buffer and the Curve vault to maintain the desired buffer ratio
*/
function _rebalanceLiquidity() internal {
// if curve vault is not set, do nothing
if (address(curveVault) == address(0)) {
return;
}
uint256 totalDeposits = reserve.totalLiquidity; // Total liquidity in the system
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio);
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (currentBuffer > desiredBuffer) {
uint256 excess = currentBuffer - desiredBuffer;
// Deposit excess into the Curve vault
_depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) {
uint256 shortage = desiredBuffer - currentBuffer;
// Withdraw shortage from the Curve vault
_withdrawFromVault(shortage);
}
emit LiquidityRebalanced(currentBuffer, totalVaultDeposits);

Both of them uses the crvUSD balance of the RToken contract, do calculations and pass the amount into the _depositIntoVault() and _withdrawFromVault() functions. The LendingPool will then try to deposit tokens which it does not have, or withdraw tokens into itself with shares it does not have.

Impact

The current implementation of the LendingPool cant interact with the curveUsdVault as it does not hold the liquidity. Setting the curveUsdVault address will break the contract.

Tools Used

Manual Review

Recommendations

Move the _depositIntoVault() and _withdrawFromVault() into the RToken contract, make them pull liquidity in and out of the RToken contracts, put the onlyReservePool modifier on both functions, and have the _ensureLiquidity() and _rebalanceLiquidity() functions call the RToken contract when liquidity is excess or needed.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::_depositIntoVault and _withdrawFromVault don't transfer tokens between RToken and LendingPool, breaking Curve vault interactions

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::_depositIntoVault and _withdrawFromVault don't transfer tokens between RToken and LendingPool, breaking Curve vault interactions

Support

FAQs

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

Give us feedback!