Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: low
Invalid

Game stalls indefinitely if `declareWinner()` is never called

Liveness risk leads to locked funds

Description

The game relies entirely on an external caller to invoke declareWinner after the grace period expires.

  • If no one ever calls this function, the game remains in a "pending" state indefinitely, the prize pot is locked, the current King never receives winnings, and the contract cannot progress to the next round.

  • This introduces a liveness issue, where funds are inaccessible and the game becomes unusable.

@> 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:

  • This will happen in inactive rounds or when players are unaware of the need to finalize the game.

Impact:

  • The prize pot remains trapped in the contract.

  • The owner is blocked from resetting the game, halting future rounds.

Proof of Concept

  • A player claims the throne and no other player joins.

  • Time passes and grace period expires.

  • No one calls declareWinner.

  • The pot remains locked, gameEnded = false, and resetGame() cannot be called.

Recommended Mitigation

Use an automated mechanism like Chainlink Automation to ensure the game concludes reliably

Updates

Appeal created

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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