Core Contracts

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

Withdrawing from the Curve vault can DoS withdrawals of `crvUSD` tokens in Lending pool

Summary

The LendingPool::withdraw(), borrow() functions are designed to extract liquidity from the Curve-crvUSD vault in case there is no sufficient liquidity in the RToken, where essentially all the deposits are stored. However calling curve vault's withdraw function will result in DoS because of insufficient balance errors.

Vulnerability Details

When a user decides to withdraw or borrow some crvUSD tokens, the internal LendingPool::_ensureLiquidity is invoked to provide the required amount in case of deficit:

function _ensureLiquidity(uint256 amount) internal {
...
uint256 availableLiquidity = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (availableLiquidity < amount) {
uint256 requiredAmount = amount - availableLiquidity;
// Withdraw required amount from the Curve vault
_withdrawFromVault(requiredAmount);
}
}
function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}

The problem here is that in case of withdrawal from the Curve vault, the tokens will be transfered to address(this) (LendingPool) (here is a link to the Curve vault withdraw(), to verify that receiver is specified as the Lending pool), but upon user deposit the tokens are directly transfered to the RToken, that's why when determining the current buffer the crvUSD balances of the RToken are checked. Hence this will result in DoS.

Consider the following scenario:

  1. Bob deposits 1000 tokens, if the currentBuffer > desiredBuffer (1000 > 200) the excess amount (800) will be deposited into the Curve vault

  2. Now Bob wants to withdraw the full amount, the RToken will not have enough, so _ensureLiquidity will be called and try to withdraw the required amount from the Curve vault.

  3. The vault will transfer the needed amount to the LendingPool instead of the RToken contract (the root cause of the issue)

  4. And trigger ReserveLibrary::withdraw, which will try to transfer out the 1000 tokens, but the RToken will still have insufficient balance.

  5. Results in DoS, same applies for borrows

Impact

  • Medium, DoS of withdrawals

Tools Used

Manual Review

Recommendations

The receiver should be the RToken contract

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 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.