Puppy Raffle

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

# `enterRaffle` allows `address(0)` entrants, colliding with the refund sentinel

enterRaffle allows address(0) entrants, colliding with the refund sentinel

Severity: Low

Description

  • enterRaffle pushes each supplied address into players without validating it is non-zero.

  • address(0) is the same value refund uses to mark a slot as refunded, so a zero entrant pollutes the duplicate check and, if selected as winner, sends the prize to the zero address and reverts _safeMint.

for (uint256 i = 0; i < newPlayers.length; i++) {
@> players.push(newPlayers[i]); // no check that newPlayers[i] != address(0)
}

Risk

Likelihood:

  • Occurs whenever a caller includes address(0) in the newPlayers array (accidentally or maliciously).

Impact:

  • Zero entrants corrupt duplicate detection and refund bookkeeping, and can cause selectWinner to send the prize to address(0) / revert on mint (compounding M-03).

Proof of Concept

Save as test/ZeroAddressEntrantPoC.t.sol and run forge test --mt testZeroAddressEntrantAccepted. enterRaffle accepts address(0) and stores it as an active player.

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;
import {Test} from "forge-std/Test.sol";
import {PuppyRaffle} from "../src/PuppyRaffle.sol";
contract ZeroAddressEntrantPoC is Test {
PuppyRaffle puppyRaffle;
uint256 entranceFee = 1e18;
function setUp() public {
puppyRaffle = new PuppyRaffle(entranceFee, address(99), 1 days);
}
function testZeroAddressEntrantAccepted() public {
address[] memory players = new address[](1);
players[0] = address(0);
puppyRaffle.enterRaffle{value: entranceFee}(players);
// address(0) is now stored as an active player, colliding with the refund sentinel
assertEq(puppyRaffle.players(0), address(0));
}
}

Recommended Mitigation

Validate every entrant against the zero address inside the entry loop, before it is pushed into players. Placing the check in the loop (rather than a single pre-check) guarantees it covers every element of the newPlayers array, including batched entries. This is the correct layer to fix it because address(0) is a reserved sentinel elsewhere in the contract — refund writes address(0) to mark a slot as refunded — so an address(0) entrant is indistinguishable from a refunded slot. Rejecting it at the source keeps that invariant intact and prevents the downstream consequences: corrupted duplicate detection, a winner resolving to address(0) (burning the prize), and the _safeMint(address(0), ...) revert.

for (uint256 i = 0; i < newPlayers.length; i++) {
+ require(newPlayers[i] != address(0), "PuppyRaffle: zero address not allowed");
players.push(newPlayers[i]);
}

Because the check reverts the whole transaction, no partial/invalid entry is ever recorded, and callers get immediate, explicit feedback rather than a silent corruption that only surfaces at selectWinner time.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours 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!