Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Two refunds leave duplicate address(0) slots, blocking every future enterRaffle (permanent DoS)

Summary

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.

Description

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:

require(players[i] != players[j], "PuppyRaffle: Duplicate player");

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.

Risk

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.

Proof of Concept

function test_PoC_TwoRefundsBlockAllNewEntries() public {
_enter(1, 4);
vm.prank(address(1)); puppyRaffle.refund(0);
vm.prank(address(2)); puppyRaffle.refund(1);
vm.expectRevert("PuppyRaffle: Duplicate player");
_enter(100, 1); // nobody can enter
vm.warp(block.timestamp + duration + 1);
vm.expectRevert("PuppyRaffle: Failed to send prize pool to winner");
puppyRaffle.selectWinner(); // nobody can draw
}

Run with forge test --match-test test_PoC_TwoRefundsBlockAllNewEntries -vvv. It passes.

Recommended Mitigation

Remove refunded players from the array (swap-and-pop) instead of leaving sentinels, or at minimum skip them in the check:

for (uint256 i = 0; i < players.length - 1; i++) {
+ if (players[i] == address(0)) continue;
for (uint256 j = i + 1; j < players.length; j++) {
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 41 minutes ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!