Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Severity: high
Valid

L-01: Zero address winner may occur due to refunded entries not being handled correctly

Root + Impact

Description

  • Normal behavior: Refunding should remove or deactivate participants without allowing invalid winners.

  • Specific issue: refund() intentionally creates address(0) holes (L103), but selectWinner() draws from players[] without filtering and pays/mints to that address (L130, L151, L153)

@> refund(): PuppyRaffle.sol:L103 creates address(0) holes
@>103: players[playerIndex] = address(0);
@> selectWinner(): PuppyRaffle.sol:L130 reads winner without checking for address(0)
@>130: address winner = players[winnerIndex];
@> selectWinner(): payout/mint to winner without a non-zero guard
@>151: (bool success,) = winner.call{value: prizePool}("");
@>153: _safeMint(winner, tokenId);

Risk

Likelihood:

  • Reason 1 Refund() introduces invalid entries (address(0)) (L103).

  • Reason 2 selectWinner() does not validate or skip invalid entries before payout/mint.

Impact:

  • Impact 1 When this will occur: During winner selection in rounds where one or more refunds have occurred

  • Impact 2 Prize payout and minting can fail or produce invalid outcomes

  • Impact 3 The round can revert and stall, harming protocol liveness.

Proof of Concept

Goal: Make the draw select address(0) as winner.
Relevant lines:
@> PuppyRaffle.sol:L103 creates a blank slot.
@> PuppyRaffle.sol:L130 selects winner = players[winnerIndex] without filtering.
@> PuppyRaffle.sol:L151/L153 uses winner directly for payout and mint.
PoC:
1) Ensure at least 4 entries exist (PuppyRaffle.sol:L127).
2) One participant calls refund(playerIndex), zeroing a slot at L103.
3) Call selectWinner(). winnerIndex at L129 can point to the zeroed slot.
4) winner becomes address(0) at L130; the payout/mint at L151/L153 can revert or behave unexpectedly

Recommended Mitigation

• Remove refunded players using swap-and-pop instead of creating holes.
• Maintain and draw from an activePlayers list that excludes address(0).
• Add a require(winner != address(0)) check before payout/mint as a defensive measure
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 12 days ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-01] Potential Loss of Funds During Prize Pool Distribution

## Description In the `selectWinner` function, when a player has refunded and their address is replaced with address(0), the prize money may be sent to address(0), resulting in fund loss. ## Vulnerability Details In the `refund` function if a user wants to refund his money then he will be given his money back and his address in the array will be replaced with `address(0)`. So lets say `Alice` entered in the raffle and later decided to refund her money then her address in the `player` array will be replaced with `address(0)`. And lets consider that her index in the array is `7th` so currently there is `address(0)` at `7th index`, so when `selectWinner` function will be called there isn't any kind of check that this 7th index can't be the winner so if this `7th` index will be declared as winner then all the prize will be sent to him which will actually lost as it will be sent to `address(0)` ## Impact Loss of funds if they are sent to address(0), posing a financial risk. ## Recommendations Implement additional checks in the `selectWinner` function to ensure that prize money is not sent to `address(0)`

Support

FAQs

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

Give us feedback!