Normally, a contest is created once and funded once with exactly totalRewards; players then claim against that balance.
The balance check token.balanceOf(msg.sender) < totalRewards guards the payer's wallet, not the pot's funding state. Calling fundContest twice (a retried script, a double click, or any repeated keeper invocation) deposits 2x totalRewards. Since claims pay out at most sum(rewards) and close only distributes remainingRewards (computed from totalRewards, not from the actual balance), the excess has no exit.
Likelihood:
Any retried/duplicated funding transaction (keeper scripts, wallet retries, manual double-entry) silently succeeds — nothing in the flow rejects a second call.
The surplus also acts as fuel for the post-close claim (V5B) and repeated-close extraction (V4), widening those attack windows.
Impact:
Verified: total 4, funded twice -> pot holds 8; players claim 3+1=4; the leftover 4 is permanently locked — a 50% funding loss.
The over-balance desynchronizes the pot from its ledger, enabling the successful post-close claim demonstrated in V5B.
PoC explanation: The test simulates a retried/duplicated funding transaction. Setup: the admin creates a pot with rewards [3, 1] and totalRewards = 4, then calls fundContest(0) TWICE — the second call sails through because the only guard checks the payer's wallet balance, not the pot's funding state. Observation: the pot now holds 8 WETH = 2 × totalRewards. Step: both players claim their full 3 + 1 = 4; at close, remainingRewards is already 0 so closeContest is a no-op. Final assertion: 4 WETH remain in the pot with no exit path — a 50% funding loss from a single duplicated call. This over-balance is also exactly the fuel that turns V5's post-close claim from a revert into a successful payout (test_POC5B). Run with: forge test --match-test test_POC6_DoubleFunding_LocksExcess -vv (PASS; asserts pot balance 8 then residual 4).
The stronger variant is funding atomically inside createContest (single transferFrom), which additionally eliminates the "created but never funded" claim-DoS state.
⚠ Combination note: fixing V6 does NOT eliminate V4 — the repeated-close PoC (V4) runs on a single normal funding. Fixing V6 only removes the fuel for the V5B post-close-claim success path.
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.