Description
The Game::claimThrone()` function contains a critical inverted condition that prevents new players from claiming the throne while incorrectly allowing the current king to reclaim it. This flaw fundamentally breaks the core game mechanic where players compete to become king by paying an increasing fee.
* @dev Allows a player to claim the throne by sending the required claim fee.
* If there's a previous king, a small portion of the new claim fee is sent to them.
* A portion also goes to the platform owner, and the rest adds to the pot.
*/
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.");
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
amountToPot = sentAmount - currentPlatformFee;
pot = pot + amountToPot;
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}
The condition require(msg.sender == currentKing, ...) is logically inverted. Instead of preventing the current king from reclaiming (as intended), it only permits the current king to call the function. This violates the game's design where:
-
New players should replace the current king by paying the claim fee
-
The current king should be blocked from reclaiming
-
The first claim should set the initial king
Risk
Likelihood:
Every valid game interaction attempt will trigger this flaw. The game cannot start or progress without patching.
Impact:
Proof of Concept
function test_ClaimThroneFailure() public {
vm.deal(player1, 1 ether);
vm.prank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: 1 ether}();
game.setKing(player1);
vm.prank(player1);
game.claimThrone{value: 1 ether}();
}
Observed Behavior:
-
First claim attempt always reverts
-
Only existing king can successfully call the function
-
Contract enters permanent locked state after deployment
Recommended Mitigation
function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Insufficient ETH");
- require(msg.sender == currentKing, "You are already king");
+ require(msg.sender != currentKing, "Current king cannot reclaim");
// ... rest of logic ...
}
Action Required:
-
Apply code patch immediately
-
Conduct full regression test suite
-
Deploy new contract instance
-
Submit patched code for re-audit