Last Man Standing

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

Denial of Service when no One claims the throne

Missing handling for unclaimed throne causes permanent game stall

Description

The contract assumes at least one claim will occur before declareWinner function is called. However, there is no safeguard or fallback if the round has no participants. This results in a permanent soft lock of the current game round and any automation relying on round transitions.

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; // Reset pot after assigning to winner's pending winnings
emit GameEnded(currentKing, pot, block.timestamp, gameRound);
}

Risk

Likelihood:

  • Medium - This depends on player activity. In low participation scenarios such as early launch and abandoned games, this is realistic.

Impact:

  • Breaks core functionality, game cannot end or progress to next round unless at least one claim occurs.

Proof of Concept

This test demonstrates a denial of service condition in the declareWinner function when no one has claimed the throne:

function test_no_one_claims_throne_dos() public {
// No one claims the throne
// Grace period passes
// Reverts as `currentKing` = address(0)
vm.warp(block.timestamp + 87000 seconds);
vm.expectRevert();
game.declareWinner();
// Even owner cant reset game
vm.prank(game.owner());
vm.expectRevert();
game.resetGame();
}

Recommended Mitigation

Add a conditional branch in declareWinner to handle cases where no one has claimed the throne (currentKing == address(0)) by emitting a GameEndedWithoutWinner event and marking the game as ended. This prevents a permanent DoS and allows the game to progress safely.

+ event GameEndedWithoutWinner();
function declareWinner() external gameNotEnded {
+ if ((block.timestamp > lastClaimTime + gracePeriod) && currentKing == address(0)) {
+ gameEnded = true;
+ emit GameEndedWithoutWinner();
+ } else {
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; // Reset pot after assigning to winner's pending winnings
+ emit GameEnded(currentKing, pot, block.timestamp, gameRound);
+ }
}
Updates

Appeal created

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

Game gets stuck if no one claims

Support

FAQs

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

Give us feedback!