Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Game.sol - GameEnded Event Emits Zero Instead of Actual Prize Amount

Description

The declareWinner() function contains a logic error where it zeroes the pot variable before emitting the GameEnded event. This causes the event to always emit 0 as the prize amount instead of the actual winnings, breaking off-chain monitoring and making it impossible to track the true prize amounts won in each game round.

Root Cause

The issue occurs because the pot is reset to 0 before being used in the event emission:

function declareWinner() external gameNotEnded {
// ... checks ...
pendingWinnings[currentKing] = pendingWinnings[currentKing] + pot;
pot = 0; // Reset pot BEFORE event emission
emit GameEnded(currentKing, pot, block.timestamp, gameRound); // pot is now 0!
}

Key issues:

  1. pot is used to track prize amount in the event

  2. pot = 0 happens before emit GameEnded()

  3. The event always emits 0 as the prize amount

  4. Off-chain systems cannot determine actual winnings

Risk

Likelihood: High - This bug occurs every single time a winner is declared. It's deterministic and unavoidable.

Impact: Low - While funds are correctly assigned to pendingWinnings, all external monitoring, analytics, and UI systems receive incorrect data.

Impact

Low severity because:

  • Off-chain monitoring systems show incorrect prize amounts (always 0)

  • No actual funds are at risk (internal accounting is correct)

Proof of Concept

This test demonstrates how the GameEnded event always emits 0 as the prize amount instead of the actual pot value, while internal accounting remains correct:

function test_GameEndedEventEmitsZeroPrize() public {
// Setup game
vm.startPrank(deployer);
game = new Game(0.1 ether, 1 hours, 10, 3);
vm.stopPrank();
// Player claims throne
vm.deal(player1, 1 ether);
vm.prank(player1);
game.claimThrone{value: 0.1 ether}();
// Check pot has accumulated value
uint256 expectedPot = 0.097 ether; // 0.1 ETH - 3% platform fee
assertEq(game.pot(), expectedPot, "Pot should have accumulated funds");
// Wait for grace period to expire
vm.warp(block.timestamp + 1 hours + 1);
// Monitor the event
vm.expectEmit(true, false, false, true);
emit GameEnded(player1, 0, block.timestamp, 1); // Note: prize is 0, not expectedPot!
// Declare winner
game.declareWinner();
// Verify internal accounting is correct
assertEq(game.pendingWinnings(player1), expectedPot, "Winnings correctly assigned");
// But the event emitted 0 as the prize amount!
}

Recommended Mitigation

Move the pot = 0 assignment after the event emission:

function declareWinner() external gameNotEnded {
require(currentKing != address(0), "Game: No one has claimed the throne yet.");
require(
block.timestamp > lastClaimTime + gracePeriod,
"Game: Grace period has not expired yet."
);
gameEnded = true;
pendingWinnings[currentKing] = pendingWinnings[currentKing] + pot;
- pot = 0;
emit GameEnded(currentKing, pot, block.timestamp, gameRound);
+ pot = 0;
}
Updates

Appeal created

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Game::declareWinner emits GameEnded event with pot = 0 always

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.