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 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!