Last Man Standing

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

Critical Logic Error in `claimThrone` Renders Game Completely Unplayable

Description

The game is designed around the core mechanic of players claiming the throne from one another. The claimThrone() function is the only entry point for a user to become the "King".

However, a critical logical error in a require statement within this function makes it impossible for any player to ever become the king. The check incorrectly requires the caller (msg.sender) to already be the current king, which is an impossible condition for any new claimant, including the very first one when the king is address(0). This flaw completely bricks the contract from the moment of deployment, making the entire game unusable.

// src/Game.sol:
function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
@> require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
uint256 sentAmount = msg.value;
//...

Risk

Likelihood: High

  • This error is triggered by the very first attempt of any player to interact with the game's core function. It is a guaranteed failure condition in every game round.

Impact: Critical

  • Complete Denial of Service: The contract's primary functionality is permanently broken. No game can ever be started or played.

  • Contract is Unusable: The contract fails to fulfill its fundamental purpose. All other functions related to the game's lifecycle (declareWinner, resetGame, etc.) are unreachable because a king can never be established.

  • Gas Waste for All Users: Any user attempting to play the game by calling claimThrone() will have their transaction reverted, causing them to lose gas for no reason.

Proof of Concept

The following Foundry test demonstrates that even the very first player's attempt to claim the throne fails, proving the game is unplayable from the start.

// test/GameUnplayable.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {Game} from "../../src/Game.sol";
contract GameUnplayableTest is Test {
Game public game;
address public deployer;
address public player1;
function setUp() public {
deployer = makeAddr("deployer");
player1 = makeAddr("player1");
vm.deal(deployer, 1 ether);
vm.deal(player1, 1 ether);
vm.startPrank(deployer);
game = new Game(0.01 ether, 1 days, 10, 5);
vm.stopPrank();
}
/// @notice This PoC demonstrates that the game is completely unplayable
/// because no player can ever become the first king.
function test_PoC_NoPlayerCanBecomeKing() public {
uint256 initialFee = game.claimFee();
// Initially, the king is address(0).
assertEq(game.currentKing(), address(0));
// --- Execution & Assertion ---
// Player 1 attempts to become the first king.
// The call is expected to revert because `require(player1 == address(0))` is false.
vm.startPrank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: initialFee}();
vm.stopPrank();
// Verify that no king was ever set. The game state remains unchanged.
assertEq(game.currentKing(), address(0), "No one should have become king.");
}
}

To run this test:

  1. Create a file named GameUnplayable.t.sol in your test directory.

  2. Paste the code above.

  3. Run forge test --match-path test/GameUnplayable.t.sol. The test will [PASS], confirming the revert.

Recommended Mitigation

The logical check must be inverted from == to != to allow a new player to claim the throne, while preventing the current king from reclaiming it from themselves.

// src/Game.sol:
- require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
+ require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");
Updates

Appeal created

inallhonesty Lead Judge 9 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Game::claimThrone `msg.sender == currentKing` check is busted

Support

FAQs

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