Last Man Standing

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

Incorrect Access Check in claimThrone() Prevents Gameplay

Root + Impact

Description

Normal behavior:
The claimThrone() function allows no player to claim the throne by sending a bid intially, thereby making the game nonplayable.

Issue:
The following check inside claimThrone() incorrectly prevents anyone from calling the function, which contradicts the intended game logic.

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

This logic makes it impossible for anyone to claim the throne as it is at intially zero address.

function claimThrone() external payable {
// @> Incorrect check prevents valid users from claiming throne
require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
require(msg.value > kingBid, "Need to send more than current bid.");
....
}

Risk

Likelihood:

  • The bug will always occur when a zero address tries to call claimThrone() due to the incorrect equality check for the first time.

  • This check completely halts gameplay after the deployed, preventing participation.

Impact:

  • The game becomes unusable — no one can claim the throne.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test, console2} from "forge-std/Test.sol";
import {Game} from "../src/Game.sol";
contract GameTest is Test {
Game public game;
address public deployer;
address public player1;
// Initial game parameters for testing
uint256 public constant INITIAL_CLAIM_FEE = 0.1 ether; // 0.1 ETH
uint256 public constant GRACE_PERIOD = 1 days; // 1 day in seconds
uint256 public constant FEE_INCREASE_PERCENTAGE = 10; // 10%
uint256 public constant PLATFORM_FEE_PERCENTAGE = 5; // 5%
function setUp() public {
deployer = makeAddr("deployer");
player1 = makeAddr("player1");
vm.deal(deployer, 10 ether);
vm.deal(player1, 10 ether);
vm.prank(deployer);
game = new Game(
INITIAL_CLAIM_FEE,
GRACE_PERIOD,
FEE_INCREASE_PERCENTAGE,
PLATFORM_FEE_PERCENTAGE
);
}
function test_claimmthrone() external{
vm.prank(player1);
game.claimThrone{value: 1 ether}();
assertNotEq(game.currentKing(),player1);
}
}

Recommended Mitigation

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