Core Contracts

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

CrvUSD cannot be deposited into Curve USD Vault

Summary

CrvUSD cannot be deposited into Curve USD Vault as there is no funds in LendingPool.

Vulnerability Details

_depositIntoVault() is used to deposit liquidity into the Curve vault.

LendingPool::_depositIntoVault():

function _depositIntoVault(uint256 amount) internal {
IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;
}

The function is called when rebalances liquidity between the buffer and the Curve vault, and the deposited amount is based on CrvUSD liquidity in RToken.

LendingPool::_rebalanceLiquidity():

uint256 totalDeposits = reserve.totalLiquidity; // Total liquidity in the system
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio);
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (currentBuffer > desiredBuffer) {
uint256 excess = currentBuffer - desiredBuffer;
// Deposit excess into the Curve vault
@> _depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) {

Curve vault transfers deposit tokens from the caller to the vault, in our case, the tokens are transferred from LendingPool to Curve vault.

Yearn V3 Vault::_deposit():

self._erc20_safe_transfer_from(self.asset, msg.sender, self, assets)

The problem is that LendingPool has no CrvUSD, as the CrvUSD tokens have been transferred to RToken when user deposits.

ReserveLibrary::deposit():

// Transfer asset from caller to the RToken contract
IERC20(reserve.reserveAssetAddress).safeTransferFrom(
msg.sender, // from
reserve.reserveRTokenAddress, // to
amount // amount
);

Impact

CrvUSD cannot be deposited into Curve Vault and _depositIntoVault() will revert, this will blocks liquidity rebalance which is called when user deposits, withdraws and borrows.

POC

Please run forge test --mt testAudit_CannotDepositIntoVault:

// 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_CannotDepositIntoVault() public {
// Set Curve Vault
vm.prank(owner);
lendingPool.setCurveVault(curveUSDVault);
address bob = makeAddr("Bob");
deal(crvUSD, bob, 100e18);
vm.startPrank(bob);
IERC20(crvUSD).approve(address(lendingPool), 100e18);
// Transaction will revert
vm.expectRevert();
lendingPool.deposit(100e18);
vm.stopPrank();
}
}

Tools Used

Manual Review

Recommendations

It is recommended to deposit into Curve Vault from RToken.

Updates

Lead Judging Commences

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

Give us feedback!