Core Contracts

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

Curve Vault Withdrawals Send Assets to LendingPool Instead of RToken

Summary

When the liquidity is withdrawn from the Curve vault the Receiver address is LendingPool instead of being rToken address

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

Vulnerability Details

The LendingPool has a feature that rebalances liquidity, when the excess tokens are deposited into the curve vault and the shortage is withdrawn from the curve vault.

But originally, LendingPool don't stock those funds in LendingPool contract, but in rToken address

We can see for example when assets are deposited into curve vault, the assets are transferred directly from the RToken contract (reserve.reserveRTokenAddress) to the curve vault, not from the LendingPool contract itself. The RToken contract holds the actual reserve assets and handles the transfers, while the LendingPool contract manages the lending logic and accounting.

function _depositIntoVault(uint256 amount) internal {
IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;
}

When a user deposit, withdraw, borrow, or repay, they always interact with rToken address

The problem is when shortened tokens are withdrawn from curve vault, they are transferred to LendingPool and not rToken address

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

as we can see in ICurveCRVUSDCVault interface, receiver is address(this) (lending pool here) instead of rToken address

function withdraw(
uint256 assets,
address receiver,
address owner,
uint256 maxLoss,
address[] calldata strategies
) external returns (uint256 shares);

Impact

  1. These withdrawn funds become locked in the LendingPool contract since there's no mechanism to transfer them to rToken

  2. When users try to withdraw/borrow after a vault withdrawal, the transaction will fail because rToken won't have the expected funds

  3. This breaks the core accounting system since rToken's balance won't match the protocol's tracked liquidity

Tools Used

Manual review

Recommendations

Put the receiver as a rToken address instead of address(this) in _withdrawFromVault function

Updates

Lead Judging Commences

inallhonesty Lead Judge 3 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 3 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.