Normally, every pot has at least one registered winner; createContest should refuse degenerate configurations outright.
With players = [] the constructor loop maps nothing, every claimCut reverts Pot__RewardNotFound, and after 90 days every closeContest computes claimantCut = (remainingRewards - managerCut) / 0 — Panic(0x12). The panic reverts the whole transaction including the manager-cut transfer, so nothing ever leaves the pot. There is no sweep, so the funds are unrecoverable, and operators see a raw panic instead of a domain error.
Likelihood:
Deploying a contest before the winners list is assembled (or a mis-pasted empty array from tooling) sails through creation and funding without any warning; the failure only appears 90 days later at close time.
Impact:
Verified: 1000 WETH funded into an empty-players pot can never be retrieved — the claim path is structurally impossible and the close path panics forever, a 100% loss of the funding.
The failure mode is a bare Panic(0x12) rather than a descriptive revert, which turns an operational mistake into a hard-to-diagnose frozen contract.
PoC explanation: The test walks a fully-funded, empty-players pot through its only two exits and shows both are bricked. Setup: the admin creates a contest with players = [] and rewards = [] — createContest accepts it without any emptiness check — and funds it with 1000 WETH. Step 1 (claim path): any caller's claimCut reverts with Pot__RewardNotFound, asserted via the custom-error selector — no address was ever mapped, so the money is structurally unclaimable. Step 2 (close path): after vm.warp(91 days), closeContest computes claimantCut = (remainingRewards - managerCut) / i_players.length = (1000 - 100) / 0 and aborts with Panic(0x12), asserted via stdError.divisionError — because the panic reverts the whole transaction, even the managerCut transfer inside the same call never lands. Final assertion: the pot still holds all 1000 WETH, a 100% loss surfaced only as a raw panic with no domain error to guide operators. Run with: forge test --match-test test_POC9_EmptyPlayers_DivByZeroAllLocked -vv (PASS; asserts both reverts and the frozen 1000).
⚠ Combination note: the sum(rewards) == totalRewards validation of V7 subsumes this check for any positive budget (sum of an empty array is 0 != totalRewards) — implement the full entrance validation once rather than patching the panic site.
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.