Last Man Standing

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

GameEnded event emits incorrect pot value

pot is reset before GameEnded emit

Description

  • Normal behavior: When a winner is declared via the declareWinner() function, their prize (stored in the pot) is added to their pendingWinnings, and a GameEnded event is emitted to signal the end of the round. The event is expected to include accurate information about the prize amount won (prizeAmount), which can be used by off-chain services (UIs, indexers, explorers) to reflect the outcome of the game.

  • Issue: The GameEnded event is emitted after the pot has already been reset to zero. As a result, the prizeAmount field in the event always emits 0, even when the winner has won a substantial amount. This creates a discrepancy between the actual contract state and what is communicated through the event.

pot = 0; // // Reset pot after assigning to winner's pending winnings
emit GameEnded(currentKing, pot, block.timestamp, gameRound); // @> emits zero value instead of actual pot

Risk

Likelihood:

  • This will occur every time a winner is declared using declareWinner(), since the pot is reset before the event is emitted.

  • Off-chain systems and users relying on GameEnded event logs will consistently receive incorrect prize information.

Impact:

  • The GameEnded event will always show prizeAmount = 0, even when the winner has earned a large amount, leading to broken or misleading UI, analytics, or historical data tracking.

  • This discrepancy may reduce player trust and interfere with third-party integrations (e.g., The Graph, block explorers, or dApp frontends relying on emitted data instead of state reads).

Proof of Concept

  1. Deploy the Game contract with valid parameters.

  2. Call claimThrone() from any player with enough ETH.

  3. Fast-forward time beyond the gracePeriod.

  4. Call declareWinner().

  5. Observe the GameEnded event that is emitted.

  6. Even though the pot contained a non-zero amount before the call, the emitted prizeAmount will always be 0.

  7. You can verify this by:

    • Checking the value of pendingWinnings[currentKing] before and after the call (should be > 0).

    • Comparing it to the prizeAmount emitted in the event (will be 0).

// Signature of the event to observe
event GameEnded(address indexed winner, uint256 prizeAmount, uint256 timestamp, uint256 round);

Recommended Mitigation

Emit the GameEnded event before resetting the pot to ensure the prizeAmount reflects the actual amount won.

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;
+
+ emit GameEnded(currentKing, pot, block.timestamp, gameRound);
+
pot = 0;
-
- emit GameEnded(currentKing, pot, block.timestamp, gameRound);
}
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.