Last Man Standing

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

Critical Logic Flaw in claimThrone Allows Game to Be Permanently Bricked

Root + Impact

Game gets permanently stuck, no one can ever claim the throne again.

Description

  • The following require statement in the claimThrone function makes the game unplayable from the very beginning:

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

This condition requires that the person trying to claim the throne is already the current king, which completely contradicts the logic of a "king of the hill" game — where new challengers are supposed to dethrone the current king by sending ETH.

In the initial state:

  • currentKing is address(0)

  • So no one can match msg.sender == currentKing

  • Every call to claimThrone() will fail — forever

Risk

Likelihood:

  • Bug triggers on first interaction:
    The game fails on the very first claim, due to the incorrect require(msg.sender == currentKing) check. No setup or complex state change is needed.

  • No special conditions required:
    Any user calling claimThrone() — even with valid ETH — will immediately fail unless they are already the king (which is never true initially).

  • No mitigation possible from off-chain:
    This isn't something the owner or any user can fix by changing parameters or resetting the game.

  • No randomness or race condition involved:
    The bug is deterministic — happens every single time the function is called under expected use.

  • Very easy to detect & reproduce:
    Anyone testing the contract even once will immediately encounter this failure.

Impact:

  • Game can never start — even on the first claim.

  • No one can ever claim the throne, because everyone fails the require(msg.sender == currentKing) check.

  • Game is completely bricked upon deployment.

  • Contract holds ETH but can't distribute or progress rounds.

Proof of Concept

We’ll simulate what happens when someone tries to claim the throne right after deployment — which should normally work (as it’s the start of the game), but fails due to a logic bug in the require() condition line.

This line expects that only the current king can call claimThrone(), which doesn’t make sense in a game where new players are supposed to dethrone the king.

But since currentKing is initialized to address(0) (i.e., no king yet), nobody can ever match it, and this line rejects everyone.

function test_ClaimThroneBricked() public {
// Step 1: Setup a test user with 10 ETH
address user = address(0xABCD);
vm.deal(user, 10 ether); // Give test ETH
// Step 2: Simulate the user calling claimThrone()
vm.prank(user); // Make next call from user's address
// Step 3: Expect revert due to faulty require()
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: 1 ether}();
}

What This Shows:

  1. User has enough ETH, satisfies msg.value >= claimFee.

  2. Still, the call fails — even on first ever throne claim.

  3. Revert reason confirms that the contract is rejecting new players.

  4. The game becomes bricked permanently — no future claim will ever succeed.

Recommended Mitigation

  • Add fuzz tests for throne claiming logic.

  • Include game-start simulation tests on deployment.

The following code ensures that the current king cannot reclaim their own throne — but others can claim it.

- remove this code
require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
+ add this code
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.