Last Man Standing

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

Complete DOS at Initial State

Summary

The claimThrone() function will always revert on the first call due to an incorrect equality check.

Description

In the original audit comment, the code shows:

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

At initial state, currentKing is address(0). Since msg.sender can never be address(0) (it's always the caller's address), this require statement will ALWAYS fail on the first call, making the contract completely unusable.
Note: The provided contract code in the main analysis had this corrected to !=, but the original audit comment shows the problematic == operator.

Root Cause

Incorrect comparison operator in the require statement:

// WRONG - causes DOS
require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
// Should be:
require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");

When currentKing is address(0) initially, no external address can ever equal address(0), so the require will always revert.

Impact

  • Complete Contract Failure: No one can ever claim the throne

  • Total Fund Loss: Contract becomes completely unusable after deployment

  • Deployment Waste: Entire contract deployment is wasted

  • Critical Business Logic Failure: Core functionality is broken

Proof of Concept

// This test would demonstrate the DOS if the contract had the == operator
function testDOSAtInitialState() public {
// Deploy contract with the buggy == operator
vm.prank(deployer);
Game buggyGame = new Game(
INITIAL_CLAIM_FEE,
GRACE_PERIOD,
FEE_INCREASE_PERCENTAGE,
PLATFORM_FEE_PERCENTAGE
);
// Initially currentKing is address(0)
assertEq(buggyGame.currentKing(), address(0));
// ANY attempt to claim throne will fail with == operator
vm.prank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
buggyGame.claimThrone{value: INITIAL_CLAIM_FEE}();
// Even different players will fail
vm.prank(player2);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
buggyGame.claimThrone{value: INITIAL_CLAIM_FEE}();
// Contract remains unusable forever
assertEq(buggyGame.currentKing(), address(0));
}
function testCorrectImplementationWorks() public {
// The != operator works correctly
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
assertEq(game.currentKing(), player1);
// But the same player cannot claim again
vm.prank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: INITIAL_CLAIM_FEE * 2}();
}

Recommended Mitigation

Change the equality operator to inequality:

function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
// FIX: Change == to !=
require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");
// Rest of function remains the same...
}
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!