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 10 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 10 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!