Last Man Standing

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

Impossible Throne Claim Due to Inverted King Check

Root + Impact

Description

  • Normal behavior:
    Any player who is not the current king should be able to claim the throne by paying the required fee. The current king should be prevented from claiming again.

    Specific issue:
    The contract incorrectly checks that only the current king can claim the throne, which is the opposite of the intended logic. As a result, no one can ever claim the throne after deployment, and the game cannot proceed.

// 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."); // @> Bug: should be !=
// ...rest of code...
}

Risk

Likelihood:

  • This will occur every time the contract is deployed and a player attempts to claim the throne, as the initial currentKing is address(0) and no EOA can match it.

  • The bug is present in the main claim function and blocks all gameplay.

Impact:

  • The game is permanently frozen and unusable.

  • All ETH sent to the contract is trapped and cannot be recovered through normal gameplay.

Proof of Concept

The following test script demonstrates the bug. It deploys the contract and attempts to claim the throne as a non-king. The transaction always reverts with the message "Game: You are already the king. No need to re-claim.", proving that no one can ever claim the throne.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {Game} from "../src/Game.sol";
contract GameC1BugTest is Test {
Game public game;
address public player1;
uint256 public constant INITIAL_CLAIM_FEE = 0.1 ether;
uint256 public constant GRACE_PERIOD = 1 days;
uint256 public constant FEE_INCREASE_PERCENTAGE = 10;
uint256 public constant PLATFORM_FEE_PERCENTAGE = 5;
function setUp() public {
player1 = makeAddr("player1");
vm.deal(player1, 1 ether);
// Deploy the buggy contract
game = new Game(
INITIAL_CLAIM_FEE,
GRACE_PERIOD,
FEE_INCREASE_PERCENTAGE,
PLATFORM_FEE_PERCENTAGE
);
}
function test_CannotClaimThrone_BuggyRequire() public {
vm.startPrank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
}
}

Recommended Mitigation

The bug is caused by an inverted logic check in the claimThrone() function. The require statement should prevent the current king from claiming again, not block everyone else.

- 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 about 1 month 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.