Root + Impact
Description
Function https://github.com/CodeHawks-Contests/ai-mycut/blob/819134663950999ca5ab29a91eeceddb80274743/src/Pot.sol#L49 contains no state variable indicating that the pot has already been finalized.
function can therefore be called repeatedly.
Each execution performs:
manager fee transfer
redistribution to all claimants
using the same remainingRewards value.
Since remainingRewards is never updated after redistribution, every subsequent call attempts to distribute the same rewards again.
Risk
Likelihood:
Impact:
If sufficient tokens exist inside the contract (accidental transfers, fee-on-transfer behavior, or malicious deposits), claimants can repeatedly receive redistribution rewards.
The function violates the protocol invariant that a contest should only be finalized once.
Proof of Concept
remainingRewards = 1000
claimants = [Alice, Bob]
Owner calls
closePot()
Alice and Bob receive redistribution.
Owner calls
closePot()
again.
Alice and Bob receive redistribution again.
The contract has no protection against repeated execution.
function test_closePotTwice() public {
address contests;
uint256 player2BalB4 = weth.balanceOf(player2);
console.log("Player 2 Initial Balance:", player2BalB4);
vm.startPrank(owner);
contests = _contestManager.createContest(players, rewards, IERC20(ERC20Mock(weth)), 4);
_contestManager.fundContest(0);
vm.stopPrank();
vm.warp(100 days);
vm.startPrank(player2);
Pot(contests).claimCut();
vm.stopPrank();
vm.startPrank(owner);
_contestManager.closeContest(contests);
vm.stopPrank();
vm.startPrank(owner);
_contestManager.closeContest(contests);
vm.stopPrank();
uint256 player2BalAF = weth.balanceOf(player2);
console.log("Player2 Balancing After Pot closed:", player2BalAF);
assertGt(player2BalAF, player2BalB4);
}
Recommended Mitigation
Introduce a finalized flag, before performing any transfers.
bool public closed; + add this code
require(!closed); + add this code
closed = true; + add this code