Last Man Standing

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

Anyone can never claim the throne

Root + Impact

Description

  • Description

    • Normal Behavior:
      Any player except for the current king should be able to claim the throne by paying the required fee. The new king replaces the previous one, the fee increases for the next claim, and the competition continues.

    • Issue:
      The claimThrone() function incorrectly requires that the caller is the current king. This means only the current king (or initially, no one) is ever allowed to claim. This prevents all normal gameplay, as no one (other than the king) can ever become king, making the game unplayable.

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."); <@
// ... rest of logic ...
}

As a result, only the current king can claim again and again, defeating the purpose of the game.

Risk

Likelihood:

  • This will occur every time a player who is not the current king attempts to call claimThrone().
    Since currentKing is initialized to address(0), only the first claim is allowed. After that, only the same address can claim again; all others are blocked.

  • In a deployed contract, the game cannot progress past the first claim.

Impact:

  • No new players can join the game after the first claim.

  • The game's competitive mechanic is entirely broken, making the dApp/game non-functional and causing user funds to be potentially stuck.

Proof of Concept

function testClaimThrone_BugOnlyCurrentKingCanClaim() public {
// Initial state: no king, so player1 should be able to claim the throne.
vm.startPrank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
// The claim fee increases for the next claim. Fetch updated fee.
uint256 updatedClaimFee = game.claimFee();
// player2, who is NOT the current king, attempts to claim the throne.
vm.startPrank(player2);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: updatedClaimFee}();
vm.stopPrank();
// player1 (the current king) claims again. Should succeed with buggy logic!
vm.startPrank(player1);
game.claimThrone{value: updatedClaimFee}();
vm.stopPrank();
}

Recommended Mitigation

Replace the faulty comparison so that only addresses not currently the king can claim.

  • This will ensure any player except the current king can claim, restoring intended game flow.

  • Thoroughly review all require checks and re-test with various users.

- 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 16 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.