We deposit, time has passed, liq.index growth, we withdraw but due to higher value, the amountUnderlying can't be deducted. It means that last user to withdraw will not be able to withdraw his full amount.
pragma solidity ^0.8.19;
import {Test, console} from "lib/forge-std/src/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/tokens/RAACToken.sol";
import "contracts/core/minters/RAACMinter/RAACMinter.sol";
import "contracts/core/pools/StabilityPool/StabilityPool.sol";
import "contracts/core/pools/StabilityPool/StabilityPool.sol";
interface ITokenERC20{
function approve(address spender, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract ArsenTest is Test{
RToken public rToken;
DebtToken public dToken;
RAACNFT public raacNFT;
RAACToken public raacToken;
RAACMinter public raacMinter;
HousePrices public housePrices;
PriceOracle public priceOracle;
LendingPool public lendingPool;
StabilityPool public stabilityPool;
address public attacker = address(8888);
address public user_1 = address(1111);
address public user_2 = address(2222);
address CRV_USD = address(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
string ETH_RPC_URL = "https://mainnet.infura.io/v3/2cef90f9f3f44a1fb5201d8547183d42";
function setUp() public{
uint256 ethFork = vm.createFork(ETH_RPC_URL);
vm.selectFork(ethFork);
housePrices = new HousePrices();
priceOracle = new PriceOracle();
rToken = new RToken(
"rToken",
"RTOKEN",
address(this),
CRV_USD
);
dToken = new DebtToken(
"Debt Token",
"DBTTOKEN",
address(this)
);
raacNFT = new RAACNFT(
CRV_USD,
address(housePrices),
address(this)
);
raacToken = new RAACToken(
address(this),
500,
500
);
lendingPool = new LendingPool(
CRV_USD,
address(rToken),
address(dToken),
address(raacNFT),
address(priceOracle),
100000000000000000000000000
);
stabilityPool = new StabilityPool(address(this));
raacMinter = new RAACMinter(
address(raacToken),
address(stabilityPool),
address(lendingPool),
address(this)
);
stabilityPool.initialize(
address(rToken),
address(dToken),
address(raacToken),
address(raacMinter),
CRV_USD,
address(lendingPool)
);
rToken.setReservePool(address(lendingPool));
dToken.setReservePool(address(lendingPool));
}
function test_deposit() public{
deal(CRV_USD, attacker, 1_000_000e18);
vm.startPrank(attacker);
ITokenERC20(CRV_USD).approve(address(lendingPool), 1_000_000e18);
lendingPool.deposit(1_000_000e18);
assertEq(ITokenERC20(CRV_USD).balanceOf(address(rToken)), 1_000_000e18);
assertEq(rToken.balanceOf(attacker), 1_000_000e18);
vm.stopPrank();
console.log("The liquidity index after the first ever deposit is", lendingPool.getNormalizedIncome());
for(uint i = 1; i < 10; i++){
address user = vm.addr(i);
deal(CRV_USD, user, 10_000e18);
vm.startPrank(user);
ITokenERC20(CRV_USD).approve(address(raacNFT), 10_000e18);
raacNFT.mint(i, 10_000e18);
raacNFT.approve(address(lendingPool), i);
lendingPool.depositNFT(i);
lendingPool.borrow(10_000e18);
vm.stopPrank();
}
vm.warp(block.timestamp + 365 days);
lendingPool.updateState();
console.log("The liquidity index right before the withdrawal is", lendingPool.getNormalizedIncome());
console.log("The balanceOf user in the rToken contract is,", rToken.balanceOf(attacker));
console.log("User tries to withdraw it, but without success.");
vm.startPrank(attacker);
(uint256 userBalance) = rToken.balanceOf(attacker);
vm.expectRevert();
lendingPool.withdraw(2_000_000e18);
vm.stopPrank();
}
}
contract HousePrices{
function tokenToHousePrice(uint256 _tokenId) external view returns (uint256){
return 10_000e18;
}
}
contract PriceOracle{
function getLatestPrice(uint256 _tokenId) external view returns (uint256, uint256){
return(10_000e18, block.timestamp);
}
}