The PuppyRaffle::selectWinner function uses msg.sender as part of the randomness calculation, making the winner selection dependent on who calls the function. This creates a front-running vulnerability where an attacker can observe a selectWinner() transaction in the mempool, calculate if they would win if they call it instead, and front-run the original transaction with a higher gas price to guarantee their victory.
The Problem:
Since msg.sender is part of the randomness:
Different callers produce different winners
Attacker sees pending selectWinner() transaction in mempool
Attacker calculates: "If I call it instead, do I win?"
If yes: Front-run with higher gas to execute first
If no: Do nothing, let original transaction proceed
Likelihood: Medium - Requires mempool monitoring and MEV infrastructure, but increasingly common with bots and MEV searchers.
Impact: Medium - Attackers can steal wins by front-running, but requires them to be a player and cannot guarantee winning every time (depends on who the original caller would have selected).
Attack Scenario:
Setup:
Alice, Bob, and attacker's FrontRunBot are in the raffle
Raffle duration ends
Alice submits selectWinner() with normal gas price (50 gwei)
Mempool Monitoring:
Attacker's bot sees Alice's transaction in mempool
Bot calculates: "If Alice calls it, Bob wins"
Bot calculates: "If I call it, I win!"
Front-Running:
Bot submits frontRun() with higher gas (100 gwei)
Miner picks bot's transaction first (higher fee)
Bot's transaction executes: msg.sender = FrontRunBot → Bot wins
Alice's transaction executes after: Winner already selected, reverts or becomes no-op
Result:
Bot stole the win that should have gone to Bob
Alice wasted gas on failed transaction
Manual review
Remove msg.sender from the randomness calculation. However, this alone doesn't fix the underlying weak randomness issue. The proper fix is to use Chainlink VRF:
Why This Only Partially Helps:
Removes front-running vulnerability (winner no longer depends on caller)
BUT randomness is still predictable (anyone can calculate winner using block.timestamp and block.difficulty)
Attacker can still use prediction attack (see Weak Randomness vulnerability)
Proper Fix: Use Chainlink VRF for verifiable randomness that:
Cannot be predicted before reveal
Cannot be manipulated by caller
Cannot be front-run (randomness provided by oracle after request)
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.