Root + Impact
Description
-
When a game is successfully ended via declareWinner, the details of the game will be emitted for public record.
-
The declareWinner function mistakenly resets the pot storage value to zero before referring to that same value in the emit message. This means every game will emit that the pot was empty.
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);
}
Risk
Likelihood:
Impact:
Proof of Concept
This test simulates a game round with player1 winning. When the player triggers declareWinner it does emit with a 0 value for the pot.
function test_IncorrectEmitValue() public {
vm.prank(deployer);
game = new Game(INITIAL_CLAIM_FEE, GRACE_PERIOD, FEE_INCREASE_PERCENTAGE, PLATFORM_FEE_PERCENTAGE);
vm.startPrank(player1);
game.claimThrone{value: .15 ether}();
vm.warp(block.timestamp + 2 days);
uint256 now = block.timestamp;
vm.expectEmit();
emit Game.GameEnded(player1, 0, now, game.gameRound());
game.declareWinner();
}
Recommended Mitigation
Make a local reference to the pot storage variable to maintain the correct value after zero reseting the storage value.
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 winnings = pot;
- pendingWinnings[currentKing] = pendingWinnings[currentKing] + pot;
+ pendingWinnings[currentKing] = pendingWinnings[currentKing] + winnings;
pot = 0; // Reset pot after assigning to winner's pending winnings
- emit GameEnded(currentKing, pot, block.timestamp, gameRound);
+ emit GameEnded(currentKing, winnings, block.timestamp, gameRound);
}