function closePot() external onlyOwner {
if (block.timestamp - i_deployedAt < 90 days) revert Pot__StillOpenForClaim();
if (remainingRewards > 0) {
uint256 managerCut = remainingRewards / managerCutPercent;
i_token.transfer(msg.sender, managerCut);
uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;
for (uint256 i = 0; i < claimants.length; i++) {
_transferReward(claimants[i], claimantCut);
}
}
}
pragma solidity ^0.8.20;
import {Test, console} from "lib/forge-std/src/Test.sol";
import {ContestManager} from "../src/ContestManager.sol";
import {Pot} from "../src/Pot.sol";
import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {ERC20Mock} from "./ERC20Mock.sol";
contract PoC_RecallableClose is Test {
ContestManager conMan;
ERC20Mock weth;
address user = makeAddr("user");
address player1 = makeAddr("player1");
address player2 = makeAddr("player2");
address[] players = [player1, player2];
uint256[] rewards = [500, 500];
uint256 totalRewards = 1000;
address contest;
function setUp() public {
vm.startPrank(user);
conMan = new ContestManager();
weth = new ERC20Mock("WETH", "WETH", user, 0);
weth.mint(user, 1000 ether);
weth.approve(address(conMan), type(uint256).max);
contest = conMan.createContest(players, rewards, IERC20(weth), totalRewards);
conMan.fundContest(0);
vm.stopPrank();
}
function test_closePot_can_be_called_repeatedly() public {
vm.warp(91 days);
uint256 potBefore = weth.balanceOf(contest);
vm.startPrank(user);
conMan.closeContest(contest);
conMan.closeContest(contest);
conMan.closeContest(contest);
vm.stopPrank();
uint256 drained = potBefore - weth.balanceOf(contest);
assertEq(drained, 300, "Pot drained multiple times by repeated closePot");
assertEq(Pot(contest).getRemainingRewards(), 1000, "remainingRewards never updated");
console.log("Drained over 3 closes:", drained, "(a single close should move 100)");
}
}
+ bool private closed;
+ error Pot__AlreadyClosed();
+
function closePot() external onlyOwner {
if (block.timestamp - i_deployedAt < 90 days) revert Pot__StillOpenForClaim();
+ if (closed) revert Pot__AlreadyClosed();
+ closed = true;
if (remainingRewards > 0) {
uint256 managerCut = remainingRewards / managerCutPercent;
i_token.transfer(msg.sender, managerCut);
uint256 claimantCut = (remainingRewards - managerCut) / claimants.length;
for (uint256 i = 0; i < claimants.length; i++) {
_transferReward(claimants[i], claimantCut);
}
+ remainingRewards = 0;
}
}