closePot() enforces a 90-day window by checking block.timestamp - i_deployedAt < 90 days. Once 90 days have elapsed, this check is permanently true. The function executes the distribution and returns — but it sets no state flag indicating the pot has been closed.
After the first closePot call, remainingRewards is not zeroed and claimants is not cleared. If any tokens arrive at the contract address after closure (fee-on-transfer rebates, direct transfers, or intentional re-funding), a second closePot call will compute a non-zero claimantCut from the stale remainingRewards and re-distribute to all entries in claimants — including any addresses added post-close via claimCut.
Likelihood:
A second closePot call with a zero-balance pot is a no-op (transfers of 0 succeed or claimantCut is 0), so routine operation does not trigger harm without an explicit re-funding event.
The scenario requires either a direct token deposit to the pot address post-close, or a fee-on-transfer token that sends rebates back — both are uncommon but not impossible.
Impact:
Double-distribution pays all entries in claimants (including post-close claimants) from the new balance. The pot has no on-chain finality — its closure is not reflected in any state variable, making it impossible to reason about whether a pot is open or closed without off-chain tracking.
Addresses that called claimCut after the pot was supposed to be closed would incorrectly receive a distribution bonus.
Place this test in test/ and run forge test --match-test testPotCanBeClosedTwice. The test demonstrates that closePot() can be called multiple times because there is no closed-state guard, potentially re-executing the manager cut transfer and emitting duplicate events.
Add a bool private isClosed flag and require !isClosed at the top of closePot(), setting it to true on the first successful execution to prevent re-entry. Setting remainingRewards = 0 alongside the isClosed flag ensures that any re-invocation of closePot() cannot distribute a non-zero balance even if the flag check is somehow bypassed.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.