Core Contracts

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

Incorrect Asset Handling in Curve Vault Withdrawals Leading to Liquidity Shortages

Summary

The LendingPool contract contains a logical flaw in its _withdrawFromVault function, where crvUSD tokens withdrawn from the Curve Vault are incorrectly sent to the LendingPool contract (address(this)) instead of the designated reserve.reserveRTokenAddress. This misrouting causes liquidity shortages in the RToken reserve, preventing users from withdrawing or borrowing crvUSD even when funds are available in the protocol.

Vulnerability Details

The _withdrawFromVault function is invoked when the protocol needs to replenish liquidity for user withdrawals or borrowing. When the RToken reserve (reserve.reserveRTokenAddress) lacks sufficient crvUSD, the LendingPool attempts to withdraw from the Curve Vault. However, the current implementation erroneously designates address(this) (the LendingPool contract) as the receiver of the withdrawn crvUSD tokens. This misdirects the funds to the LendingPool instead of the RToken reserve, which is the designated liquidity source for user transactions. Consequently, functions IRToken(reserve.reserveRTokenAddress).transferAsset(msg.sender, amount) fail because the RToken reserve’s balance remains unchanged, even though the protocol has sufficient liquidity deposit in the wrong contract.

/**
* @notice Internal function to withdraw liquidity from the Curve vault
* @param amount The amount to withdraw
*/
function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}

POC (Proof of Concept)

Scenario:

  1. Initial State:

    • RToken contract holds 0 crvUSD.

    • Curve Vault holds 1000 crvUSD allocated to the protocol.

    • User A attempts to borrow 500 crvUSD.

  2. Action Flow:

    • _ensureLiquidity checks RToken balance (0 crvUSD) and triggers _withdrawFromVault(500).

    • Curve Vault sends 500 crvUSD to LendingPool (not RToken).

    • IRToken(reserve.reserveRTokenAddress).transferAsset(...) is called but fails because RToken still has 0 crvUSD.

  3. Result:

    • Borrow transaction reverts with "insufficient balance".

    • 500 crvUSD remains stuck in the LendingPool, unusable for user operations.

Impact

User withdrawals and borrowing Reverts.

Tools Used

Manual Review

Recommendations

Update the receiver address in _withdrawFromVault to direct crvUSD to the RToken reserve:

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

Lead Judging Commences

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