Last Man Standing

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

[L-1] Incorrect `prizeAmount` Emitted in `Game::GameEnded` Event

Root Cause

The pot variable is being reset to 0 before the GameEnded event is emitted.
Since Solidity evaluates arguments at the time of emit, the value of pot passed to the event is 0, even though the actual prize was transferred correctly.


Vulnerable Function

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; // pot is reset before emitting
@> emit GameEnded(currentKing, pot, block.timestamp, gameRound); // emits 0 always
}

Impact

  • Incorrect event data: The GameEnded event always emits 0 as the prize amount.

  • Off-chain consumers (frontends, analytics, The Graph) will show misleading or wrong winner payouts.

  • May break trust or external integrations relying on accurate event logs.


Proof of Concept (PoC)

event GameEnded(address indexed winner, uint256 prizeAmount, uint256 timestamp, uint256 round);
function test_GameEndedEmitsInCorrectPrizeAmount() public {
// Arrange
vm.startPrank(player1);
game.claimThrone{value: game.claimFee()}(); // Assume this adds 1 ether to the pot
vm.warp(block.timestamp + game.gracePeriod() + 1);
// Expect the emit
vm.expectEmit(true, false, false, false);
emit GameEnded(game.currentKing(), game.pot(), block.timestamp, game.gameRound());
// Act
game.declareWinner();
}

Recommended Mitigation:

Store the pot in a temporary variable before modifying it, and use that in both logic and 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;
uint256 prizeAmount = pot;
pendingWinnings[currentKing] = pendingWinnings[currentKing] + prizeAmount;
pot = 0;
emit GameEnded(currentKing, prizeAmount, block.timestamp, gameRound); //correct value emitted
}

Severity: Low

  • It doesn't affect funds or security.

  • But it does affect off-chain trust, data accuracy, and external integrations.

Updates

Appeal created

inallhonesty Lead Judge 9 days 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.