Last Man Standing

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

Incorrect `require` in `claimThrone()` prevents all non-king users from participating, breaking core game functionality

Root + Impact

Description

Normal behavior:
Any user can claim the throne by sending at least claimFee ETH to the contract. The previous king is dethroned, the claimFee is updated, and the pot increases.

Problem:
The contract incorrectly checks:

require(msg.sender == currentKing, "Game: You are already the king.");

This rejects any user who is not already the current king, which completely blocks new players from ever participating in the game.

This is a logic inversion: it should reject the current king from re-claiming, not block everyone else.

function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
// ❌ BUG: incorrect condition, blocks all users except current king
require(msg.sender == currentKing, "Game: You are already the king.");
// @> This check is inverted — should be != currentKing

Risk

Likelihood:

  1. The bug triggers immediately on the first user interaction.

  2. Anyone attempting to claim the throne (except address(0) king) will fail.

  3. No user will be able to claim the throne and no round can progress.

Impact:

  1. The game cannot proceed, core logic is entirely blocked.

  2. Pot cannot grow, winner can never be declared.

  3. All user interactions with the contract (claims, prize accumulation, rotation) are permanently disabled.

  4. Essentially renders the contract unusable.

Proof of Concept

  1. Deploys the Game contract.

  2. Alice (not the king) tries to claim the throne with the exact required fee.

  3. Despite being eligible, she fails due to the faulty require(msg.sender == currentKing) check.

  4. The revert proves that nobody can ever successfully claim the throne unless they are already the king, which defeats the whole game logic.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Game.sol"; // adjust import path to your local setup
contract GameClaimThroneTest is Test {
Game public game;
address public alice;
uint256 public initialClaimFee = 1 ether;
uint256 public gracePeriod = 1 days;
uint256 public feeIncrease = 10; // 10%
uint256 public platformFee = 5; // 5%
function setUp() public {
alice = vm.addr(1);
vm.deal(alice, 10 ether);
game = new Game(
initialClaimFee,
gracePeriod,
feeIncrease,
platformFee
);
}
function testClaimThroneFailsForFirstUser() public {
vm.startPrank(alice);
// The game has just been deployed, so currentKing == address(0)
// Alice should be able to claim the throne here, but due to logic bug it will revert
vm.expectRevert("Game: You are already the king.");
game.claimThrone{value: initialClaimFee}();
vm.stopPrank();
}
}

Recommended Mitigation

Fix the inverted logic condition by rejecting the current king, not 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 4 months 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.

Give us feedback!