Last Man Standing

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

Missing Logic For Previous King Payout Leads to The Previous King Never Being Paid Out

Missing Logic For Previous King Payout Leads to The Previous King Never Being Paid Out

Description

  • Under the intended rules, when a player claims the throne, the previous king should receive a small portion of the claim fee as a payout. This incentivizes players to compete and rewards the dethroned king for holding the position.

  • However, while the variable previousKingPayout is declared in claimThrone(), it is never assigned a value or credited to the actual previous king. As a result, dethroned kings receive no payout at all, which violates game rules, misleads participants, and breaks part of the economic model.

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 platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// Defensive check to ensure platformFee doesn't exceed available amount after previousKingPayout
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining amount goes to the pot
amountToPot = sentAmount - currentPlatformFee;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
// Increase the claim fee for the next player
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}

Risk

Likelihood:

  • This will occur every time a new player successfully calls claimThrone() after a previous king is in place.

  • It will continue indefinitely across all game rounds, since no logic ever pays the dethroned king.

Impact:

  • Players are financially disincentivized from participating, since there is no reward for holding the throne.

  • The core game loop is economically broken, reducing player engagement and eroding trust in the contract’s fairness.

Proof of Concept

Add this test to Game.t.sol and run with forge test --mt testPreviousKingDoesNotRecievePayout

function testPreviousKingDoesNotRecievePayout() public {
// Player 1 claims throne and becomes King
vm.startPrank(player1);
game.claimThrone{value: game.claimFee()}();
vm.stopPrank();
// Player 2 claims throne and becomes King
vm.startPrank(player2);
game.claimThrone{value: game.claimFee()}();
vm.stopPrank();
// Player 1 does not recieve any payment for being overthrown
assertEq(game.pendingWinnings(player1), 0);
}

Recommended Mitigation

Create logic to calculate what the previousKingPayout will be set to every time someone claims the throne, and add a check that updates the pending winnings of the previous king:

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 previousKingPayout = /* logic here */;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
+ if (currentKing != address(0)) {
+ pendingWinnings[currentKing] += currentKingPayout;
+ }
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// Defensive check to ensure platformFee doesn't exceed available amount after previousKingPayout
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining amount goes to the pot
amountToPot = sentAmount - currentPlatformFee;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
// Increase the claim fee for the next player
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}
Updates

Appeal created

inallhonesty Lead Judge about 1 month 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.