Puppy Raffle

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

Missing Input Validation in Constructor Allows Misconfiguration

Description

The PuppyRaffle constructor accepts entranceFee, feeAddress, and raffleDuration parameters without validating them. This allows deployment with invalid configurations that break core protocol functionality.

Expected behavior: Constructor should validate critical parameters to prevent misconfiguration at deployment.

Actual behavior: The constructor accepts zero values and zero addresses without any checks, leading to:

  • Zero entrance fee: Players can enter for free, generating no revenue

  • Zero address for fees: All protocol fees are permanently burned to address(0)

  • Zero duration: Winner can be selected immediately, defeating the purpose of a timed raffle

constructor(uint256 _entranceFee, address _feeAddress, uint256 _raffleDuration) ERC721("Puppy Raffle", "PR") {
entranceFee = _entranceFee; // @> No validation - can be 0
feeAddress = _feeAddress; // @> No validation - can be address(0)
raffleDuration = _raffleDuration; // @> No validation - can be 0
raffleStartTime = block.timestamp;
// ...
}

Risk

Likelihood:

  • Low-Medium - Requires owner mistake during deployment

  • Could happen during testing or rushed deployment

  • No way to fix after deployment (immutable values)

Impact:

  • Zero entrance fee: Protocol generates no revenue

  • Zero address: All fees permanently lost (burned)

  • Zero duration: Raffle can be gamed by immediately selecting winner

  • No recovery possible after deployment

Proof of Concept

Add to test/PuppyRaffleTest.t.sol:

/**
* @notice Demonstrates constructor accepts invalid inputs leading to broken protocol
* @dev Zero values for critical parameters break core functionality
*/
function test_constructorMissingValidation() public {
emit log("=== Test 1: Zero Entrance Fee ===");
PuppyRaffle zeroFeeRaffle = new PuppyRaffle(0, address(99), 1 days);
emit log_named_uint("Entrance fee", zeroFeeRaffle.entranceFee());
address[] memory players = new address[](1);
players[0] = address(10);
zeroFeeRaffle.enterRaffle{value: 0}(players);
emit log("Players can enter for FREE - no revenue!");
emit log("");
emit log("=== Test 2: Zero Address for Fees ===");
PuppyRaffle zeroAddressRaffle = new PuppyRaffle(1 ether, address(0), 1 days);
emit log_named_address("Fee address", zeroAddressRaffle.feeAddress());
emit log("All protocol fees will be BURNED to address(0)");
emit log("");
emit log("=== Test 3: Zero Duration ===");
PuppyRaffle zeroDurationRaffle = new PuppyRaffle(1 ether, address(99), 0);
emit log_named_uint("Duration", zeroDurationRaffle.raffleDuration());
address[] memory playersForZeroDuration = new address[](4);
for (uint256 i = 0; i < 4; i++) {
playersForZeroDuration[i] = address(uint160(i + 1));
}
vm.deal(address(1), 4 ether);
vm.prank(address(1));
zeroDurationRaffle.enterRaffle{value: 4 ether}(playersForZeroDuration);
// Can select winner immediately!
zeroDurationRaffle.selectWinner();
emit log("Winner selected IMMEDIATELY (no waiting period)");
assertEq(zeroFeeRaffle.entranceFee(), 0);
assertEq(zeroAddressRaffle.feeAddress(), address(0));
assertEq(zeroDurationRaffle.raffleDuration(), 0);
}

Run: forge test --match-test test_constructorMissingValidation -vv --offline

Output:

=== Test 1: Zero Entrance Fee ===
Entrance fee: 0
Players can enter for FREE - no revenue!
=== Test 2: Zero Address for Fees ===
Fee address: 0x0000000000000000000000000000000000000000
All protocol fees will be BURNED to address(0)
=== Test 3: Zero Duration ===
Duration: 0
Winner selected IMMEDIATELY (no waiting period)

Recommended Mitigation

Add input validation in the constructor:

constructor(uint256 _entranceFee, address _feeAddress, uint256 _raffleDuration) ERC721("Puppy Raffle", "PR") {
+ require(_entranceFee > 0, "PuppyRaffle: Entrance fee must be greater than 0");
+ require(_feeAddress != address(0), "PuppyRaffle: Fee address cannot be zero address");
+ require(_raffleDuration > 0, "PuppyRaffle: Duration must be greater than 0");
+
entranceFee = _entranceFee;
feeAddress = _feeAddress;
raffleDuration = _raffleDuration;
raffleStartTime = block.timestamp;
// ...
}

This prevents deployment with invalid configurations that would break the protocol.


Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 8 days 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!