refund leaves address(0) in the refunded slot, and enterRaffle's duplicate check compares every pair of slots, sentinels included. After two refunds in a round, every new entry reverts with "Duplicate player", and the raffle can never be reset.
refund (src/PuppyRaffle.sol:103) writes players[playerIndex] = address(0);. The duplicate check in enterRaffle (src/PuppyRaffle.sol:86-90) doesn't skip that sentinel, so two refunded slots compare equal:
players is only cleared by a successful selectWinner. That also fails, because it sizes the prize from players.length while the refunded ETH has already left (see H-03). So the round can never close.
Likelihood: Medium — needs two refunds in one round: common, and an attacker can force it for gas only.
Impact: Medium — permanent DoS of the core entry and draw functions. No ETH is stolen; the remaining players can still refund.
Run with forge test --match-test test_PoC_TwoRefundsBlockAllNewEntries -vvv. It passes.
Remove refunded players from the array (swap-and-pop) instead of leaving sentinels, or at minimum skip them in the check:
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.