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 10 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.

Give us feedback!