Core Contracts

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

LendingPool can not deposit into Curve Vault when rebalancing liquidity

Summary

The LendingPool contract can not deposit into Curve Vault in the process of rebalancing liquidity because of insufficient balance

Vulnerability Details

At the end of the functions LendingPool#deposit(), LendingPool#withdraw(),LendingPool#borrow(), the internal funtion LendingPool#_rebalanceLiquidity() is called to rebalance liquidity between buffer and CurveVault to maintain the desired buffer ratio.

function _rebalanceLiquidity() internal {
// if curve vault is not set, do nothing
if (address(curveVault) == address(0)) {
return;
}
@> 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) {
uint256 shortage = desiredBuffer - currentBuffer;
// Withdraw shortage from the Curve vault
_withdrawFromVault(shortage);
}
emit LiquidityRebalanced(currentBuffer, totalVaultDeposits);
}
function _depositIntoVault(uint256 amount) internal {
IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;
}

As reserve asset tokens are held by reserve.reserveRTokenAddress address, then the logic inside the function LendingPool#_depositIntoVault() will be failed because of insufficient token balance.

PoC

Add this MockCurveVault to the testsuite

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "../primitives/MockContractWithConfig.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MockCurveVault is MockContractWithConfig {
IERC20 token;
function setToken(address _token) public {
token = IERC20(_token);
}
function deposit(uint256 assets, address receiver) external returns (uint256 shares) {
token.transferFrom(msg.sender, address(this), assets);
}
function withdraw(
uint256 assets,
address receiver,
address owner,
uint256 maxLoss,
address[] calldata strategies
) external returns (uint256 shares) {
token.transfer(msg.sender, assets);
}
}

Add the test below to test/unit/core/pools/LendingPool/LendingPool.test.js

describe("Deposit and Withdraw", function () {
it.only("rebalance liquidity failed", async function () {
const depositAmount = ethers.parseEther("100");
// deploy MockCurveVault and set the address
const curveVaultFactory = await ethers.getContractFactory("MockCurveVault");
const curveVault = await curveVaultFactory.deploy();
await curveVault.setToken(await crvusd.getAddress());
await lendingPool.connect(owner).setCurveVault(await curveVault.getAddress());
// deposit
await lendingPool.connect(user1).deposit(depositAmount);
});
...

Run the test and it failed

1) LendingPool
Deposit and Withdraw
rebalance liquidity failed:
Error: VM Exception while processing transaction: reverted with custom error 'ERC20InsufficientBalance("0x0165878A594ca255338adfa4d48449f69242Eb8F", 0, 160000000000000000000)'
at crvUSDToken.burnFrom (contracts/mocks/core/tokens/crvUSDToken.sol:37)
at crvUSDToken._transfer (@openzeppelin/contracts/token/ERC20/ERC20.sol:178)
at crvUSDToken.transferFrom (@openzeppelin/contracts/token/ERC20/ERC20.sol:157)
at MockCurveVault.deposit (contracts/mocks/core/pools/MockCurveVault.sol:18)
at LendingPool._depositIntoVault (contracts/core/pools/LendingPool/LendingPool.sol:827)
at LendingPool._rebalanceLiquidity (contracts/core/pools/LendingPool/LendingPool.sol:808)
at LendingPool.deposit (contracts/core/pools/LendingPool/LendingPool.sol:236)
...

Impact

  • The flows of deposit, withdraw and borrow can be DoS

  • The buffer mechanism does not work

Tools Used

Manual

Recommendations

Retrieve funds from reserveRTokenAddress address before depositing into Curve vault

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.