Last Man Standing

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

Missing Previous King Payout Implementation Breaks Game Economics

Missing Previous King Payout Implementation Breaks Game Economics

Description

  • The game documentation and comments indicate that previous kings should receive a portion of new claim fees as compensation for being dethroned

  • The claimThrone() function calculates previousKingPayout = 0 but never actually implements the payout logic, breaking the intended economic model

function claimThrone() external payable gameNotEnded nonReentrant {
// ... validation logic ...
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0; // @> Always set to 0, never calculated properly
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// @> Missing: No actual payout to previous king
// @> Missing: previousKingPayout is never calculated or sent
// Remaining amount goes to the pot
amountToPot = sentAmount - currentPlatformFee; // @> Should subtract previousKingPayout too
pot = pot + amountToPot;
}

Risk

Likelihood: High

  • This affects every throne claim transaction

  • Previous kings never receive their intended compensation

  • Economic incentives are completely broken

Impact: High

  • Previous kings lose expected compensation payments

  • Game pot receives more funds than intended, skewing prize amounts

  • Economic model fails to incentivize participation

  • Players have no compensation for being dethroned

Proof of Concept

The test demonstrates that previous kings should receive compensation when dethroned:

uint256 public constant PREVIOUS_KING_PAYOUT_PERCENTAGE = 10; // 10%
function testClaimThrone_ShouldPayPreviousKing() public {
// Player1 becomes king
uint256 player1BalanceBefore = player1.balance;
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
// Player2 claims throne - should pay player1 a portion
uint256 claimFee = game.claimFee();
vm.prank(player2);
game.claimThrone{value: claimFee}();
// Player1 should have received some compensation for being dethroned
// Currently this fails because no payout mechanism exists
uint256 expectedPayout = (claimFee * PREVIOUS_KING_PAYOUT_PERCENTAGE) / 100; // PREVIOUS_KING_PAYOUT_PERCENTAGE = 10
assertEq(player1.balance, player1BalanceBefore - INITIAL_CLAIM_FEE + expectedPayout);
}

Explanation: The test shows that when a new player claims the throne, the previous king should receive compensation, but currently receives nothing.

Recommended Mitigation

// Add new state variable for previous king payout percentage
contract Game is Ownable {
// ... existing variables ...
+ uint256 public previousKingPayoutPercentage; // Percentage of claim fee for previous king
constructor(
uint256 _initialClaimFee,
uint256 _gracePeriod,
uint256 _feeIncreasePercentage,
- uint256 _platformFeePercentage
+ uint256 _platformFeePercentage,
+ uint256 _previousKingPayoutPercentage
) Ownable(msg.sender) {
// ... existing validation ...
+ require(_previousKingPayoutPercentage <= 50, "Game: Previous king payout percentage must be 0-50.");
// ... existing assignments ...
+ previousKingPayoutPercentage = _previousKingPayoutPercentage;
}
+ event PreviousKingPayout(
+ address indexed previousKing,
+ uint256 payoutAmount,
+ address indexed newKing
+ );
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;
+ // Pay previous king if one exists
+ if (currentKing != address(0)) {
+ previousKingPayout = (sentAmount * previousKingPayoutPercentage) / 100;
+ (bool success, ) = payable(currentKing).call{value: previousKingPayout}("");
+ require(success, "Game: Failed to pay previous king.");
+
+ emit PreviousKingPayout(currentKing, previousKingPayout, msg.sender);
+ }
uint256 currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
- amountToPot = sentAmount - currentPlatformFee;
+ amountToPot = sentAmount - currentPlatformFee - previousKingPayout;
pot = pot + amountToPot;
// ... rest of function
}
+
+ event PreviousKingPayoutPercentageUpdated(uint256 newPreviousKingPayoutPercentage);
+
+ function updatePreviousKingPayoutPercentage(uint256 _newPreviousKingPayoutPercentage)
+ external
+ onlyOwner
+ gameEndedOnly
+ {
+ require(_newPreviousKingPayoutPercentage <= 50, "Game: Previous king payout percentage must be 0-50.");
+ previousKingPayoutPercentage = _newPreviousKingPayoutPercentage;
+ emit PreviousKingPayoutPercentageUpdated(_newPreviousKingPayoutPercentage);
+ }

Explanation: Add configurable previous king payout mechanism (0-50%) with proper transfer logic, transparency events, update function with access control, and correct pot accounting to restore the intended game economics.

Updates

Appeal created

inallhonesty Lead Judge 4 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!