Core Contracts

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

Failure to Withdraw Liquidity to RToken.sol Before Changing Curve Vault Address

Summary

A vulnerability exists in the setCurveVault() function of the LendingPool contract. When switching the Curve crvUSD vault to a new address, the function fails to withdraw the remaining reserve tokens from the old vault and transfer them to RToken.sol. As a result, any liquidity left in the old vault becomes inaccessible to the protocol, leading to capital inefficiency and potential loss of user-accessible liquidity.

Vulnerability Details

The setCurveVault() function updates the address of the Curve crvUSD vault without handling the reserve tokens already deposited in the old vault:

LendingPool.sol#L699-L708

/**
* @notice Sets the address of the Curve crvUSD vault
* @param newVault The address of the new Curve vault contract
*/
function setCurveVault(address newVault) external onlyOwner {
require(newVault != address(0), "Invalid vault address");
address oldVault = address(curveVault);
curveVault = ICurveCrvUSDVault(newVault);
emit CurveVaultUpdated(oldVault, newVault);
}

This function does not withdraw any remaining reserve tokens from the old vault before updating the vault address. Once the vault is switched, the protocol can no longer access these funds, as evidenced below where curveVault is now a new vault address.

As a matter of fact, the following function serving as the only way of getting back some reserve assets from the vault is meant to be internally invoked by either _ensureLiquidity() for its requiredAmount or _rebalanceLiquidity() for its shortage as far as the inputted amount is concerned. :

LendingPool.sol#L809-L812

function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}

There isn't a way where the protocol may manually pre-withdraw the reserve assets balance from the old vault prior to setting a new vault address. Neither is there a way to manually post-withdraw the reserve assets balance following the change of a new vault. This oversight leads to stranded liquidity and loss of user-accessible funds unless a full recovery process has been factored into the setter function.

Impact

  1. Stranded Reserve Tokens:
    Liquidity left in the old vault is no longer usable for withdrawals, borrowing, or yield generation.

  2. User Withdrawal Failures:
    Since RToken.sol relies on sufficient reserve tokens to fulfill withdrawal requests, stranded liquidity could cause partial or failed withdrawals.

  3. Protocol Yield Reduction:
    With a portion of the capital stuck in the old vault, the protocol’s ability to reinvest and generate yield is diminished, affecting long-term performance.

  4. Protocol Vulnerability to Shortages:
    In scenarios of high withdrawal demand, stranded funds may force the protocol to sell or borrow liquidity from external sources under unfavorable conditions.

Tools Used

Manual

Recommendations

Consider the following refactoring:

LendingPool.sol#L699-L708

function setCurveVault(address newVault) external onlyOwner {
require(newVault != address(0), "Invalid vault address");
+ // Withdraw all remaining reserve tokens from the old vault
+ if (address(curveVault) != address(0)) {
+ uint256 remainingBalance = curveVault.balanceOf(address(this));
+ if (remainingBalance > 0) {
+ // Withdraw and transfer reserve tokens to RToken.sol
+ curveVault.withdraw(remainingBalance, address(this), address(this), 0, new address[](0));
+ IERC20(reserve.reserveAssetAddress).safeTransfer(reserve.reserveRTokenAddress, remainingBalance);
+ }
+ }
address oldVault = address(curveVault);
curveVault = ICurveCrvUSDVault(newVault);
emit CurveVaultUpdated(oldVault, newVault);
}
Updates

Lead Judging Commences

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

LendingPool::setCurveVault doesn't withdraw funds from old vault before changing address, permanently locking deposited assets

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

LendingPool::setCurveVault doesn't withdraw funds from old vault before changing address, permanently locking deposited assets

Support

FAQs

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