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 {
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:
Proof of Concept
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;
uint256 public constant INITIAL_CLAIM_FEE = 0.1 ether;
uint256 public constant GRACE_PERIOD = 1 days;
uint256 public constant FEE_INCREASE_PERCENTAGE = 10;
uint256 public constant PLATFORM_FEE_PERCENTAGE = 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.");