Core Contracts

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

LendingPool::_withdrawFromVault() always revert as it's called with wrong parameters

Summary

The function _withdrawFromVault is called with bad parameters.
As we can see in the curve::withdraw(), the function will always revert as it tries to withdraw to LendingPool, burning msg.sender shares. The issue here is that msg.sender is the EOA user address.

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

The function will try to burn EOA shares instead of RToken address (note that it's not clear in the code who is keeping srcvUSD). So it will always revert, unless the user has stacked crvUSD and approved LendingPool to burn his srcvUSD tokens. But this is not a case we want either.

Vulnerability Details

_withdrawFromVault() is triggered by _ensureLiquidity() and _rebalanceLiquidity() when LendingPool needs liquidity to repay a user, so mainly on the event withdraw().

Note that _withdrawFromVault() will need to have curveVault set, and as said a shortage of liquidity. Also, note that once set, it's impossible to reset curveVault

  1. Protocol launch LendingPool

  2. users deposits NFT/crvUSD into LendingPool, and TVL starts to grow. Note that crvUSD is kept on the RToken contract

  3. Protocol will call setCurveVault().

  4. Per current parameters, 80% of the crvUSD will be transferred to curveVault and 20% will stay on the RToken contract

  5. Users will try to withdraw the token, it will trigger _withdrawFromVault() that will revert

Impact

All crvUSD stacked using LendingPool (80%) will be stuck in the Curve Vault, with no way to withdraw them. Also, it will be impossible to withdraw the 20% buffer that RToken holds.

Tools Used

Manual

Recommendations

I've made the assumption that RToken will be the scrvUSD holder, as currently all crvUSD when deposit/withdraw are sent to RToken contract which mints the equivalent in RToken for accounting.

LendingPool
function _withdrawFromVault(uint256 amount) internal {
- curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
+ rtoken.withdrawFromVault(amount);
totalVaultDeposits -= amount;
}
RToken
+ function withdrawFromVault(uint256 amount) external onlyStabilityPool {
+ curveVault.withdraw(amount, address(this), address(this), 0, new address[](0)) //it will swap scrvUSD in RToken into crvUSD
+ }
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

LendingPool::_withdrawFromVault incorrectly uses msg.sender instead of address(this) as the owner parameter, causing vault withdrawals to fail

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

LendingPool::_withdrawFromVault incorrectly uses msg.sender instead of address(this) as the owner parameter, causing vault withdrawals to fail

Support

FAQs

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