Last Man Standing

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

Logic Inversion in `claimThrone()` Prevents Any Participant from Claiming the Throne

Root + Impact

Root Cause: Incorrect require condition reversed → Impact: No one except the zero address can ever become king, effectively blocking game participation — a logic bug reminiscent of real DeFi contract misfires.

Description

  • The claimThrone function is expected to be executed by a non-zero address to vie for the Title "King".

  • The claimThrone() function in Game.sol contains a reversed boolean check. Because currentKing is initialized to address(0) and never updated before the first claim, no externally owned account (msg.sender != address(0)) can ever satisfy this condition. Instead, only the zero address itself (which can never call the function in practice) would pass the check, making the game unplayable. All legitimate users calling claimThrone() revert with the “already the king” message.

Game::claimThrone:

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

Risk

Likelihood: High

  • Expected to prevent the current king from re-claiming the throne. However, Currently preventing anyone except the current king (initially the zero address) from claiming, thus blocking valid entries.

  • This kind of logic inversion where a protective check is written in the exact opposite sense has appeared in DeFi protocols, unintentionally locking out all users, sometimes requiring emergency governance or contract redeployment to correct.

Impact: High

  • Complete Game Lock: No participant can ever become king; the core gameplay is permanently disabled.

  • Revenue Block: All claim fees (claimFee) can never be collected into pot.

  • User Frustration & Reputation Damage: Players see constant reverts, rendering the DApp unusable.

  • Governance Emergency: Requires immediate hotfix or redeployment-costly and damaging to trust.

Real-World Analogues:

  • Aave v2 Permission Bug (2019): An inverted require in the flash loan function prevented any borrowing except by the zero address, necessitating a governance patch.

  • Synthetix Staking Issue (2020): A logic check flipped around the staker’s address blocked all deposits until a manual upgrade, causing stakers to miss epochs.

Tools Used:

  • Foundry Test Suite

  • Chat-GPT AI Assistance (Report Grammar Check & Improvements)

  • Manual Review

Refs & Resources

Proof of Concept

function test_nobody_can_participate_in_the_game() public {
// no one can claim the throne.
vm.startPrank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
// but only the zero address can
hoax(address(0), 10 ether);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
}

step 1: go to test/Game.t.sol file

step 2: paste the above code ⬆️

step 3: run the test suite

forge test --mt test_nobody_can_participate_in_the_game

step 4: See the Output

Scenario

  • Game Initialization

    • currentKing defaults to address(0).

  • User Attempt

    • A real user player1 calls claimThrone{value: claimFee}().

    • Fails the require(msg.sender == currentKing) test -> reverts.

  • Zero Address “Participation”

    • Only msg.sender == address(0) would pass, but EOAs cannot originate from zero address.

Recommended Mitigation

Correct the Boolean Check

Replace:

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

With:

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

Add Unit Tests to Prevent Regression

- Test that `player1` can claim when `currentKing == address(0)`.
- Test that the same `player1` cannot re-claim immediately after becoming king.

Consider Role-Based Access for Edge Cases

- If a zero-address claim must be prevented, explicitly check `currentKing != address(0)` and handle it.
Updates

Appeal created

inallhonesty Lead Judge about 2 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.