Core Contracts

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

Incorrect withdrawal address in vault integration leads to fund loss in LendingPool

Summary

In the LendingPool contract, the _ensureLiquidity function withdraws funds from the Curve vault to the wrong address (LendingPool) instead of the RToken address, causing a mismatch between accounting and actual balances, potentially leading to protocol fund loss.

Vulnerability Details

The issue stems from the _ensureLiquidity function in the LendingPool contract. This function is responsible for ensuring sufficient liquidity is available for withdrawals or borrowing by pulling funds from the Curve vault when needed. However, there's a critical flaw in how the withdrawal destination is handled.

Let's look at the vulnerable code:

function _ensureLiquidity(uint256 amount) internal {
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
// @audit the crvUSD is brought into this address and not the reserveRTokenAddress
_withdrawFromVault(requiredAmount);
}
}

The issue propagates through the _withdrawFromVault function:

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

The key problems are:

  1. The vault withdrawal sends funds to address(this) (LendingPool) instead of reserve.reserveRTokenAddress

  2. The protocol's accounting assumes all reserve assets are held in the RToken contract

  3. This creates a discrepancy between where funds are actually held vs. where the protocol thinks they are

This issue is particularly severe because:

  • The ReserveLibrary's functions like deposit() and withdraw() operate under the assumption that all reserve assets are managed through the RToken contract

  • Balance checks and liquidity calculations look at the RToken's balance

  • Interest calculations and rate updates depend on accurate liquidity tracking

PoC

  1. User A deposits 100 crvUSD into the protocol

  2. Protocol deposits 80 crvUSD into Curve vault (20% buffer)

  3. User B attempts to withdraw 90 crvUSD

  4. _ensureLiquidity detects insufficient funds and withdraws 70 crvUSD from vault

  5. Funds go to LendingPool instead of RToken

  6. RToken balance check fails as it only has 20 crvUSD

  7. Transaction reverts despite protocol having sufficient funds

Impact

  • Protocol funds become stuck in the LendingPool contract

  • Users cannot withdraw their funds even when the protocol has sufficient liquidity

  • Accounting system becomes unreliable

  • Interest calculations become inaccurate

  • High severity as it affects core protocol functionality and user funds

Tools Used

Manual code review

Recommendations

  1. Modify _withdrawFromVault to send funds to the RToken contract:

function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, reserve.reserveRTokenAddress, msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}
  1. Add balance reconciliation functions to handle any stuck funds

  2. Add proper validation to ensure funds are always in the correct contract

  3. Add events to track vault withdrawals and their destinations

Updates

Lead Judging Commences

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