Core Contracts

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

_rebalanceLiquidity() and all functions calling it will be DoS'ed when the curve vault is set.

Bug description

_rebalanceLiquidity() function allows rebalancing liquidity in the Pool depending on the set buffer. It does so by either calling curveVault.deposit() or curveVault.withdraw() when the vault is set.

LendingPool.sol#L773-L790

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);
}

However, the problem is that all of the liquidity of the lending pool is contained within the RToken contract. This can be seen from the _ensureLiquidity() function.

LendingPool.sol#L760

uint256 availableLiquidity = IERC20(reserve.reserveAssetAddress)
.balanceOf(reserve.reserveRTokenAddress);

When _depositIntoVault() is called, it will try to deposit assets from the LendingPool contract, which does not hold any funds, therefore making it always revert.

LendingPool.sol#L799-L803

IERC20(reserve.reserveAssetAddress).approve(
address(curveVault),
amount
);
curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;

Additionally _withdrawFromVault() withdraws assets from the curveVault into LendingPool instead of RToken. And even though currently there's no way to deposit into curveVault, it's something to keep in mind when this issue gets fixed.

What makes it worse, is the fact that once curveVault is set, there's no way to reset it back to address(0) due to the following check.

LendingPool.sol#L704

require(newVault != address(0), "Invalid vault address");

Impact

When the curveVault is set, _ensureLiquidity and all functions calling _rebalanceLiquidity() will be DoS'ed.

Proof of Concept

Please create this contract in the pools/LendingPool directory of the project.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract curveVault {
ERC20 assets;
function deposit(uint256 amount, address from) public {
assets.transferFrom(from, address(this), amount);
}
}

Please add this test to LendingPool.test.js and run it with npx hardhat test --grep "reverts if the vault is set".

describe("sl1", function () {
it("reverts if the vault is set", async function () {
let curveVault;
const CurveVault = await ethers.getContractFactory("curveVault");
curveVault = await CurveVault.deploy();
await lendingPool.setCurveVault(curveVault.getAddress());
const depositAmount = ethers.parseEther("1000");
await expect(lendingPool.connect(user2).deposit(depositAmount)).to.be
.reverted;
});
}

Recommended Mitigation

Add a function in the RToken contract that will deposit assets to the curveVault and call it from the LendingPool contract when rebalancing liquidity. Create the same function for the withdraw functionality and call in the LendingPool when rebalancing liquidity.

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.