Last Man Standing

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

Incorrect require Logic Blocks All Throne Claims

Root + Impact

The root cause lies in a logic inversion in the claimThrone() function. Instead of rejecting calls from the current king, the contract mistakenly rejects everyone else. As a result, no one can claim the throne except the existing king (initially address(0)) — who is disallowed from reclaiming by design. This creates a logic contradiction and breaks the core flow of the game.

The impact is critical: the game becomes non-functional after the deployment, halting all progression. Players are unable to participate, and the owner cannot reset the game until a winner is declared — which can never happen if no one can claim.

Description

Affected code:
Game.sol::claimThrone()

  • Normally, players can claim the throne if they are not already the current king, by paying the current claimFee.

  • The claimThrone() function incorrectly prevents all valid claims by requiring the caller to be the current king, rather than not the current king.

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.");
...
}

Risk

Likelihood:

The likehood is High because:

  • Always occurs after the first throne claim is made by any non-zero address.

  • Prevents all subsequent players from interacting with the game.

Impact:

The impact is high because the issue:

  • Completely halts the core game mechanic.

  • Causes a Denial-of-Service and breaks user flow.

Proof of Concept

Add the test bellow inside the Game.t.sol and run the test use the following script:

forge test --match-path test/Game.t.sol --match-test test_claimThroneCanNeverBeCalled
function test_claimThroneCanNeverBeCalled() public {
vm.expectRevert();
vm.prank(player1);
game.claimThrone{value: 0.1 ether}();
}

Result:

Ran 1 test for test/Game.t.sol:GameTest
[PASS] test_claimThroneCanNeverBeCalled() (gas: 46761)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.75ms (205.98µs CPU time)
Ran 1 test suite in 10.11ms (1.75ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

Inisde the Game.sol::claimThrone() invert the require statement from == to != to ensure only different user can claim the throne from the previous one.

- 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

0xalipede Submitter
9 days ago
inallhonesty Lead Judge
6 days ago
inallhonesty Lead Judge 5 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.