The LendingPool's liquidity buffer management system has a critical flaw where the buffer calculations and actual token movements are misaligned. The system calculates the buffer based on the RToken contract's balance but attempts to move tokens between the curvePool
and the LendingPool
.
The core issue lies in the mismatch between where the buffer ratio is calculated and where the actual token movements occur:
function _depositIntoVault(uint256 amount) internal {
IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
curveVault.deposit(amount, address(this));
}
function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
}
it("should fail when attempting to borrow more than available RToken liquidity", async function () {
const depositAmount = ethers.parseEther("100");
await contracts.crvUSD.connect(user1).approve(contracts.lendingPool.target, depositAmount);
await contracts.lendingPool.connect(user1).deposit(depositAmount);
const tokenId = HOUSE_TOKEN_ID + 5;
const higherPrice = HOUSE_PRICE * 10n;
await contracts.housePrices.setHousePrice(tokenId, higherPrice);
await contracts.crvUSD.connect(user2).approve(contracts.nft.target, higherPrice);
await contracts.nft.connect(user2).mint(tokenId, higherPrice);
await contracts.nft.connect(user2).approve(contracts.lendingPool.target, tokenId);
await contracts.lendingPool.connect(user2).depositNFT(tokenId);
console.log("\nInitial balances:");
const initialRTokenBalance = await contracts.crvUSD.balanceOf(contracts.rToken.target);
const initialLendingPoolBalance = await contracts.crvUSD.balanceOf(contracts.lendingPool.target);
console.log("RToken balance:", ethers.formatEther(initialRTokenBalance));
console.log("LendingPool balance:", ethers.formatEther(initialLendingPoolBalance));
const borrowAmount = ethers.parseEther("90");
await contracts.lendingPool.connect(user2).borrow(borrowAmount);
console.log("\nBalances after first borrow:");
const rTokenBalance = await contracts.crvUSD.balanceOf(contracts.rToken.target);
const lendingPoolBalance = await contracts.crvUSD.balanceOf(contracts.lendingPool.target);
console.log("RToken balance:", ethers.formatEther(rTokenBalance));
console.log("LendingPool balance:", ethers.formatEther(lendingPoolBalance));
const borrowAmount2 = ethers.parseEther("50");
await expect(
contracts.lendingPool.connect(user2).borrow(borrowAmount2)
).to.be.revertedWithCustomError(
contracts.rToken,
"ERC20InsufficientBalance"
);
const finalRTokenBalance = await contracts.crvUSD.balanceOf(contracts.rToken.target);
const finalLendingPoolBalance = await contracts.crvUSD.balanceOf(contracts.lendingPool.target);
expect(finalRTokenBalance).to.equal(ethers.parseEther("10"));
expect(finalLendingPoolBalance).to.equal(0);
});
HIGH - The protocol's liquidity management system is fundamentally broken, preventing proper management of the liquidity buffer.