Last Man Standing

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

Missing Previous King Payout Breaks Game Incentive .

Root Cause: Missing Previous King Payout

Impact: Broken Incentive Model & Unfair ETH Distribution

Description

  • Under normal gameplay, each new throne claimant is expected to reward the previous king with a small portion of their ETH payment. This mechanism is crucial to the game's incentive design, encouraging users to participate, compete, and defend the throne.

  • However, in the current implementation, the payout logic is completely missing. Although a previousKingPayout variable exists, it is never used to transfer funds to the prior king, effectively nullifying the reward system.

// Inside claimThrone()
uint256 previousKingPayout = 0;
// @> Variable declared but no payout logic exists
// @> previousKing does not receive any portion of the incoming ETH

Risk

Likelihood High:

  • This occurs on every call to claimThrone() after the initial claim.

  • The payout is always omitted, since there is no conditional logic or transfer for the previous king.

Impact High:

  • Zero ETH is rewarded to the previous king, removing any economic incentive for users to play competitively.

  • Misleads players based on expectations set in documentation, game design, or UI — resulting in trust issues, reduced engagement, and centralization of funds into the pot or platform.


Proof of Concept

This test demonstrates that a previous king receives no payout when dethroned — violating the intended mechanics.

function testPreviousKingDoesNotReceivePayout() public {
// Player1 becomes king with 1 ether
vm.deal(player1, 5 ether);
vm.prank(player1);
game.claimThrone{value: 1 ether}();
// Save Player1's balance before a new player claims the throne
uint256 balanceBefore = player1.balance;
// Player2 becomes king with 1.1 ether (higher claim fee)
vm.deal(player2, 5 ether);
vm.prank(player2);
game.claimThrone{value: 1.1 ether}();
vm.warp(block.timestamp + GRACE_PERIOD + 1);
game.declareWinner();
// Player1 should receive a payout, but balance remains unchanged
uint256 balanceAfter = player1.balance;
// This assertion fails in the buggy contract
assertGt(balanceAfter, balanceBefore, "Previous king not compensated");
}

Explanation:
The test sets up a two-player scenario where Player1 becomes king, then gets dethroned by Player2. According to the game's rules, Player1 should receive a portion of Player2's payment. However, in the current implementation, Player1's balance remains the same — confirming no payout occurred.


Recommended Mitigation

Update the claimThrone() function to explicitly reward the previous king before updating the currentKing variable. Ensure the reward is calculated as a fixed percentage of the new claim fee (e.g., 10%).

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;
+ address previousKing = currentKing;
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
+ // Secure reward payout to dethroned king
+ if (previousKing != address(0)) {
+ previousKingPayout = (sentAmount * 10) / 100; // 10% incentive
+ pendingWinnings[previousKing] += previousKingPayout;
+ }
// Continue with normal claim logic
currentKing = msg.sender;
// ... other logic ...
emit ThroneClaimed(msg.sender, sentAmount, claimFee, pot, block.timestamp);
}

Explanation:
This mitigation ensures that dethroned kings receive a defined share of the new claim fee by storing it in pendingWinnings. Using pendingWinnings also avoids direct transfers, which helps prevent reentrancy and ensures compatibility with pull-based withdrawal mechanisms withdrawWinnings().


Updates

Appeal created

inallhonesty Lead Judge about 2 months 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.