Last Man Standing

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

The `claimThrone` function fails to pay the previous king upon throne transition, violating the game's intended reward mechanism.

Description

The previousKingPayout variable is declared but never assigned a value in the claimThrone function. As a result, no reward is ever transferred to the previous king when a new player claims the throne. This breaks a core incentive mechanism of the game.

Risk

Impact:
Players who previously held the throne do not receive the expected financial reward when dethroned. This undermines the game’s economic model and significantly reduces player motivation to participate, as there is no tangible benefit in holding the throne if no payout is guaranteed upon losing it. Over time, this could lead to low engagement and a non-functional game economy.

Proof of Concept

  1. Admin deploys the contract.

  2. Player player1 pays the claim fee and successfully calls claimThrone.

  3. Player player2 pays the (increased) claim fee and calls claimThrone, dethroning player1.

  4. Player player1 checks their balance or attempts to call withdrawWinnings() — they receive zero payout despite being the previous king.

This confirms that no reward was allocated or credited to the previous king.

Recommended Mitigation

Since the original specification does not define the exact amount or percentage to be paid to the previous king, we recommend introducing a configurable previousKingFeePercentage parameter set at deployment. This allows flexibility and transparency while ensuring the reward mechanism is enforceable.

The following changes are proposed:

1. Add State Variable

// Game Parameters (Configurable by Owner)
uint256 public initialClaimFee;
uint256 public feeIncreasePercentage;
uint256 public platformFeePercentage;
+ // Percentage of the claim fee allocated to the previous king
+ uint256 public previousKingFeePercentage;
uint256 public initialGracePeriod;

2. Update Constructor

constructor(
uint256 _initialClaimFee,
uint256 _gracePeriod,
uint256 _feeIncreasePercentage,
uint256 _platformFeePercentage,
+ uint256 _previousKingFeePercentage
) Ownable(msg.sender) {
require(_initialClaimFee > 0, "Game: Initial claim fee must be greater than zero.");
require(_gracePeriod > 0, "Game: Grace period must be greater than zero.");
require(_feeIncreasePercentage <= 100, "Game: Fee increase percentage must be 0-100.");
require(_platformFeePercentage <= 100, "Game: Platform fee percentage must be 0-100.");
+ require(_previousKingFeePercentage <= 100, "Game: Previous king fee percentage must be 0-100.");
+ require(_platformFeePercentage + _previousKingFeePercentage < 100,
+ "Game: Sum of platform and previous king fees must be less than 100%");
initialClaimFee = _initialClaimFee;
initialGracePeriod = _gracePeriod;
feeIncreasePercentage = _feeIncreasePercentage;
platformFeePercentage = _platformFeePercentage;
+ previousKingFeePercentage = _previousKingFeePercentage;
claimFee = initialClaimFee;
gracePeriod = initialGracePeriod;
lastClaimTime = block.timestamp;
gameRound = 1;
gameEnded = false;
}

3. Fix claimThrone Logic

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;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
+ // Calculate payout to the previous king
+ previousKingPayout = (sentAmount * previousKingFeePercentage) / 100;
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
+ // Pay the previous king (before updating current king!)
+ if (previousKingPayout > 0 && currentKing != address(0)) {
+ pendingWinnings[currentKing] += previousKingPayout;
+ }
// Prevent platform fee from exceeding available funds after previous king payout
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance += currentPlatformFee;
// Remaining ETH goes to the pot
amountToPot = sentAmount - currentPlatformFee;
pot += amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender]++;
totalClaims++;
// Increase fee for next claim
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(msg.sender, sentAmount, claimFee, pot, block.timestamp);
}

Benefits of the Fix:

  • Introduces a transparent and configurable reward for the previous king.

  • Maintains fund safety with proper arithmetic and overflow checks.

  • Ensures fair distribution: previous king, platform, and pot.

  • Increases player engagement by restoring the intended economic incentive.

Updates

Appeal created

inallhonesty Lead Judge 26 days 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.