Last Man Standing

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

Game.sol - Missing Previous King Payout Feature Despite README Claims

Description

The README documentation explicitly states that the current king "Receives a small payout from the next player's claimFee (if applicable)" when dethroned. However, the contract implementation completely lacks this feature. The previousKingPayout variable is hardcoded to 0 and never used, meaning previous kings receive no compensation when overthrown, contrary to the documented game mechanics.

Root Cause

The implementation shows a placeholder for the payout logic that was never completed:

function claimThrone() external payable gameNotEnded nonReentrant {
// ... validation checks ...
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0; // Always 0, never calculated or paid
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// Platform fee calculation
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// Defensive check that can never trigger since previousKingPayout = 0
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout; // Dead code
}
// No payout logic to previous king exists
// currentKing gets nothing when dethroned
}

Key issues:

  1. previousKingPayout is hardcoded to 0

  2. No ETH transfer to the previous king

  3. Defensive check becomes meaningless (always false)

  4. Documentation promises a feature that doesn't exist

Risk

Likelihood: High - Every single throne claim fails to pay the previous king as documented.

Impact: Medium - Players join expecting payouts when dethroned but receive nothing, affecting game economics and player trust.

Impact

Medium severity because:

  • Breaks documented game mechanics and player expectations

  • Changes economic incentives - no compensation for losing throne

Proof of Concept

This test demonstrates that previous kings receive no payout when dethroned:

function test_MissingPreviousKingPayout() public {
// Setup game
vm.startPrank(deployer);
game = new Game(0.1 ether, 1 hours, 10, 3);
vm.stopPrank();
// Player1 becomes first king
address player1 = address(0x1);
vm.deal(player1, 1 ether);
vm.prank(player1);
game.claimThrone{value: 0.1 ether}();
uint256 player1BalanceBefore = player1.balance;
assertEq(game.currentKing(), player1);
// Player2 dethrones player1
address player2 = address(0x2);
vm.deal(player2, 1 ether);
vm.prank(player2);
game.claimThrone{value: 0.11 ether}(); // 10% increase
// Player1 should have received a payout per README
// But balance remains unchanged
assertEq(player1.balance, player1BalanceBefore, "No payout received");
assertEq(game.currentKing(), player2, "Player2 is new king");
// Verify no ETH was sent to player1
// README promises payout but implementation has none
}

Recommended Mitigation

Implement the promised payout feature:

function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent");
require(msg.sender != currentKing, "Game: Already king");
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0;
+ // Calculate payout to previous king (e.g., 10% of claim fee)
+ if (currentKing != address(0)) {
+ previousKingPayout = (claimFee * 10) / 100; // 10% of required fee
+
+ // Send payout to previous king
+ (bool success, ) = payable(currentKing).call{value: previousKingPayout}("");
+ require(success, "Failed to pay previous king");
+ }
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// Calculate platform fee on remaining amount
- currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
+ currentPlatformFee = ((sentAmount - previousKingPayout) * platformFeePercentage) / 100;
- // Defensive check becomes meaningful
- if (currentPlatformFee > (sentAmount - previousKingPayout)) {
- currentPlatformFee = sentAmount - previousKingPayout;
- }
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining goes to pot
- amountToPot = sentAmount - currentPlatformFee;
+ amountToPot = sentAmount - currentPlatformFee - previousKingPayout;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
// ... rest of function
}

Alternative: Update README to remove the false claim about king payouts if the feature is not intended.

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.