Core Contracts

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

CrvUSD cannot be withdrawn from Curve USD Vault

Summary

CrvUSD cannot be withdrawn from Curve USD Vault due to incorrect owner argument.

Vulnerability Details

_withdrawFromVault() is used to withdraw liquidity from the Curve vault.

LendingPool::_withdrawFromVault():

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

The problem is that Curve vault's withdraw() is called with owner argument is msg.sender, this will revert the whole transaction.

Yearn V3 Vault::withdraw():

def withdraw(
assets: uint256,
receiver: address,
@> owner: address,
max_loss: uint256 = 0,
strategies: DynArray[address, MAX_QUEUE] = []
) -> uint256:

Impact

CrvUSD cannot be withdrawn from Curve USD Vault.

POC

Please run forge test --mt forge test --mt testAudit_CannotWithdrawFromVault.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Test, console} from "forge-std/Test.sol";
import "../contracts/core/pools/LendingPool/LendingPool.sol";
import "../contracts/core/tokens/RToken.sol";
import "../contracts/core/tokens/DebtToken.sol";
import "../contracts/core/tokens/RAACNFT.sol";
import "../contracts/core/primitives/RAACHousePrices.sol";
contract Audit is Test {
address owner = makeAddr("Owner");
address curveUSDVault;
address crvUSD;
LendingPool lendingPool;
RAACHousePrices raacHousePrices;
RToken rToken;
DebtToken debtToken;
RAACNFT raacNft;
function setUp() public {
vm.createSelectFork("https://eth.llamarpc.com");
curveUSDVault = 0x0655977FEb2f289A4aB78af67BAB0d17aAb84367;
crvUSD = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E;
raacHousePrices = new RAACHousePrices(owner);
rToken = new RToken("RToken", "RToken", owner, address(crvUSD));
debtToken = new DebtToken("DebtToken", "DT", owner);
raacNft = new RAACNFT(address(crvUSD), address(raacHousePrices), owner);
lendingPool = new LendingPool(
address(crvUSD), address(rToken), address(debtToken), address(raacNft), address(raacHousePrices), 0.1e27
);
lendingPool.transferOwnership(owner);
vm.startPrank(owner);
raacHousePrices.setOracle(owner);
raacHousePrices.setHousePrice(1, 100);
rToken.setReservePool(address(lendingPool));
vm.stopPrank();
vm.label(curveUSDVault, "Curve USD Vault");
vm.label(crvUSD, "crvUSD");
vm.label(address(rToken), "RToken");
vm.label(address(debtToken), "DebtToken");
vm.label(address(raacNft), "RAAC NFT");
vm.label(address(lendingPool), "LendingPool");
}
function testAudit_CannotWithdrawFromVault() public {
// Set Curve Vault
vm.prank(owner);
lendingPool.setCurveVault(curveUSDVault);
address bob = makeAddr("Bob");
deal(crvUSD, bob, 100e18 + 1);
// Note: this is to bypass the rebalance deposit issue
deal(crvUSD, address(lendingPool), 9999e18);
vm.startPrank(bob);
IERC20(crvUSD).approve(address(lendingPool), 100e18 + 1);
// Deposit
lendingPool.deposit(100e18);
vm.stopPrank();
// Note: this is to mock rebalance deposit result
deal(crvUSD, address(rToken), 20e18);
// Increase the `liquidityBufferRatio` to withdraw from Curve vault
uint256 liquidityBufferRatio = lendingPool.liquidityBufferRatio();
vm.prank(owner);
lendingPool.setParameter(ILendingPool.OwnerParameter.LiquidityBufferRatio, liquidityBufferRatio * 2);
// Deposit to trigger rebalance (withdraw from Curve vault)
vm.prank(bob);
vm.expectRevert(bytes("insufficient shares to redeem"));
lendingPool.deposit(1);
}
}

Tools Used

Manual Review

Recommendations

when calls Curve vault's withdraw(), owner should be the LendingPool.

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

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

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

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

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.

Give us feedback!