Puppy Raffle

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

Potential Front-Running Attack in `selectWinner` and `refund`: a losing player front-runs the draw with `refund` to escape, shrinking the pool

Description

The normal behavior is that selectWinner is called once the raffle ends to pay the winner, and that refund returns a player's fee at any time before that.

The specific issue is a transaction-ordering (front-running) problem independent of the randomness weakness: because refund can be called by any still-active player right up until selectWinner is mined, a player who does not want to risk losing can watch the mempool for a pending selectWinner and front-run it with refund to pull their entrance fee out first. Players therefore only stay in the raffle when it benefits them, draining the pot that honest players expected to compete for.

// refund is available until selectWinner is mined, and both sit in the public mempool
function refund(uint256 playerIndex) public { ... payable(msg.sender).sendValue(entranceFee); players[playerIndex] = address(0); }
​
function selectWinner() external { ... } // @> a pending call is visible; refund can be ordered before it

This is purely about transaction ordering: anyone observing the pending selectWinner transaction can insert a refund ahead of it (higher priority fee) and exit the raffle, so the prize pool that the eventual winner receives is smaller than participants expected, and the raffle's economics are gamed by whoever reacts fastest to the mempool. The same mempool visibility also lets a searcher reorder selectWinner to their advantage.

Risk

Likelihood: Medium

  • The mempool is public and both refund and selectWinner are permissionless; front-running by priority fee is standard MEV behavior.

Impact: High

  • Players can escape losing by front-running the draw with refund, shrinking the prize pool and breaking the fairness/economics of the raffle for honest participants.

Proof of Concept

// 1. selectWinner() is submitted to the mempool.
// 2. A player who expects to lose sees it pending and submits refund(theirIndex) with a higher priority fee.
// 3. refund is mined first -> the player exits with their fee; selectWinner then pays a reduced pool.

Recommended Mitigation

Close refunds once the raffle enters the draw phase (a commit/lock window), and use a VRF-style flow where the draw is settled in a way that cannot be front-run by refund or by reordering selectWinner.

Updates

Lead Judging Commences

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