Core Contracts

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

Inaccurate totalVaultDeposits Tracking in LendingPool

Summary

The totalVaultDeposits variable in the LendingPool contract does not accurately reflect the actual balance or value of the Curve vault due to untracked yield, interest, or losses generated by the vault’s strategies. This discrepancy can mislead any action relying on totalVaultDeposits, potentially leading to suboptimal liquidity management , though core operations remain unaffected.

Vulnerability Details

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L96

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/LendingPool/LendingPool.sol#L799

totalVaultDeposits tracks the net amount of assets deposited into or withdrawn from the Curve vault (via _depositIntoVault() and _withdrawFromVault()). But, it does not account for any additional value (e.g., yield, interest, or losses) generated by the vault’s underlying strategies or price movements, as defined by the ICurveCrvUSDVault interface.

function _depositIntoVault(uint256 amount) internal {
IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;
}
function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}
function _rebalanceLiquidity() internal {
if (address(curveVault) == address(0)) {
return;
}
uint256 totalDeposits = reserve.totalLiquidity;
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio);
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (currentBuffer > desiredBuffer) {
uint256 excess = currentBuffer - desiredBuffer;
_depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) {
uint256 shortage = desiredBuffer - currentBuffer;
_withdrawFromVault(shortage);
}
emit LiquidityRebalanced(currentBuffer, totalVaultDeposits);
}

The totalVaultDeposits is incremented or decremented by the exact amount deposited or withdrawn, but it does not reflect:

  • Yield or interest accrued in the Curve vault (e.g., via curveVault.totalAssets() or curveVault.pricePerShare()).

  • Losses or changes in asset value due to vault strategies or market movements.

This discrepancy is exposed in LiquidityRebalanced events and any external systems relying on totalVaultDeposits for monitoring or analytics. However, _rebalanceLiquidity() uses currentBuffer (derived from IERC20.balanceOf(reserve.reserveRTokenAddress)), not totalVaultDeposits, ensuring that rebalancing decisions are based on actual on-hand liquidity, not the vault’s state.

Impact

  • If totalVaultDeposits is used for liquidity reporting, it could misrepresent the protocol’s liquidity position

Tools Used

Manual Review

Recommendations

  • Update totalVaultDeposits to reflect the vault’s actual asset value by querying curveVault.totalAssets()

Updates

Lead Judging Commences

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

LendingPool::totalVaultDeposits can underflow when withdrawing yield-inclusive amounts and vault yield isn't factored into interest rate calculations

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

LendingPool::totalVaultDeposits can underflow when withdrawing yield-inclusive amounts and vault yield isn't factored into interest rate calculations

Support

FAQs

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