Last Man Standing

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

Previous King Payout Mechanism Completely Missing

Summary

The claimThrone() function fails to implement the Documented game mechanic where the previous king should "receive a small payout from the next player's claimFee". The previousKingPayout variable is hardcoded to 0 and never updated, resulting in previous kings receiving no compensation when dethroned, despite this being a core documented feature.

Description

According to the game documentation, the current king has the power to "receive a small payout from the next player's claimFee (if applicable)". However, the implementation in claimThrone() contains a critical flaw:

  1. Variable Initialization: previousKingPayout is initialized to 0 and never modified

  2. No Payout Logic: There is no code to calculate or transfer any amount to the previous king

  3. Misleading Code: The presence of previousKingPayout variable suggests this feature was planned but never implemented

  4. Redundant Check: The defensiveCheck becomes meaningless since previousKingPayout is always 0

function claimThrone() external payable gameNotEnded nonReentrant {
// @Cropped
uint256 previousKingPayout = 0;
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
// @restOfLogic
}

The current flow simply:

function claimThrone() external payable gameNotEnded nonReentrant {
// @Cropped
// 1.Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// 2.deduct it from amount going to pot
amountToPot = sentAmount - currentPlatformFee;
// 3.Updating pot
pot = pot + amountToPot;
// @RestOfLogic
}

Impact

  • Broken Game Mechanics: Core documented functionality is completely missing

  • Economic Incentive Failure: No immediate reward for becoming king reduces player engagement

Proof of Concept

function test_prevKing_wont_Receives_payout_from_the_next_player() public {
// Player1 becomes king
vm.startPrank(player1);
assertEq(player1.balance, 10 ether);
uint expectedBalance = player1.balance - INITIAL_CLAIM_FEE;
game.claimThrone{value: INITIAL_CLAIM_FEE}();
assertEq(player1.balance, expectedBalance);
vm.stopPrank();
assertEq(game.currentKing(), player1);
// Player2 claims throne (should trigger payout to Player1)
vm.startPrank(player2);
uint newClaimFee = INITIAL_CLAIM_FEE + (INITIAL_CLAIM_FEE * game.feeIncreasePercentage()) / 100;
game.claimThrone{value: newClaimFee}();
vm.stopPrank();
assertEq(game.currentKing(), player2);
// BUG: Player1's balance unchanged - should have received payout
assertEq(player1.balance, expectedBalance);
}

Mitigation

  • you can rewrite calimThrone as following

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;
// Calculate and pay previous king (if exists)
+ if (currentKing != address(0)) {
// Example: 5% of claim fee goes to previous king
+ previousKingPayout = (sentAmount * previousKingPayoutPercentage) / 100;
// Transfer to previous king
// This is safe since we're using nonReentrant Modifier
+ if (previousKingPayout > 0) {
+ (bool success, ) = currentKing.call{value: previousKingPayout}("");
+ require(success, "Game: Transfer to previous king failed");
}
}
// Defensive check to ensure platformFee doesn't exceed available amount after previousKingPayout
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining amount goes to the pot
amountToPot = sentAmount - currentPlatformFee;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
// Increase the claim fee for the next player
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}
Updates

Appeal created

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Missing Previous King Payout Functionality

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.