Puppy Raffle

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

[M-01] Strict Equality on `msg.value` (Usability / Medium Risk)

Description

The PuppyRaffle::enterRaffle function enforces payment via a strict equality check:

function enterRaffle(address[] memory newPlayers) public payable {
@> require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle");
for (uint256 i = 0; i < newPlayers.length; i++) {
players.push(newPlayers[i]);
}
// Check for duplicates
for (uint256 i = 0; i < players.length - 1; i++) {
for (uint256 j = i + 1; j < players.length; j++) {
require(players[i] != players[j], "PuppyRaffle: Duplicate player");
}
}
emit RaffleEnter(newPlayers);
}

Using a strict equality check on msg.value can cause the transaction to revert if the user sends slightly more or less than the exact calculated value. Minor rounding differences or front-end miscalculations can trigger unnecessary failures.

Risk

  • Severity: Medium

  • Type: Usability / Payment correctness

  • Impact: Legitimate transactions may fail, leading to poor user experience or accidental DoS for participants.

Likelihood:

  • Probability of Occurrence: Medium

  • Users or front-end applications may slightly miscalculate the total payment. On high-volume deployments, this can happen frequently.

Impact:

  • Effect: Legitimate transactions may fail due to small overpayments or underpayments. This can frustrate users, reduce participation, and affect the perceived availability of the raffle.

Proof of Concept

Add this function to PuppyRaffleTest.t.sol::PuppyRaffleTest and run forge test --mt testStrictEqualityIsBad:

function testStrictEqualityIsBad() public {
// Generate an array of 5 players
address[] memory players = new address[](5);
// Initialize the arrays using dummy addresses
for (uint256 i = 0; i < 5; i++) {
players[i] = address(i + 1);
}
vm.deal(player, 6 ether);
vm.startPrank(player);
// expected value is 5 ether
// one additional wei is enough for revert
vm.expectRevert(bytes("PuppyRaffle: Must send enough to enter raffle"));
puppyRaffle.enterRaffle{value: 5e18 + 1}(players);
vm.stopPrank();
}

Explanation

  • Setup players: Creates an array of 5 dummy addresses to simulate a small raffle entry.

  • Fund the sender: Assigns the test player 6 ETH to cover the intended payment and any overpayment.

  • Trigger strict equality check: Calls PuppyRaffle::enterRaffle with msg.value = 5 ETH + 1 wei, slightly above the expected total (5 * entranceFee).

  • Expect revert: Uses vm.expectRevert with the exact revert message to assert that the transaction fails due to the strict == comparison.

  • Demonstrates usability issue: Confirms that even a minor overpayment causes a revert, highlighting why strict equality on msg.value is problematic.

Recommended Mitigation

  • Use a greater-than-or-equal check to allow slightly higher payments:

- require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle");
+ require(msg.value >= entranceFee * newPlayers.length, "PuppyRaffle: Insufficient payment");
  • Optionally, refund excess ETH to the sender:

- require(msg.value == entranceFee * newPlayers.length, "PuppyRaffle: Must send enough to enter raffle");
+ uint256 excess = msg.value - entranceFee * newPlayers.length;
+ if (excess > 0) {
+ payable(msg.sender).transfer(excess);
+ }
Updates

Lead Judging Commences

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