Last Man Standing

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

Missing payout to previous king during throne claim

Broken incentive causes unexpected behavior

Description

  • Normally, when a new player calls Game::claimThrone, the ETH they send should be distributed between three parties:

    1. A portion to the previous king

    2. A portion to the platform owner as fees

    3. The remainder to the prize pot

  • However, this behavior is not implemented in the contract. The documentation clearly states that "a small portion of the new claim fee is sent to [the previous King]," but in reality, no such payout occurs. The entire claim fee is split only between the pot and the platform.

@> /**
* @dev Allows a player to claim the throne by sending the required claim fee.
* If there's a previous king, a small portion of the `new claim fee` is sent to them.
* A portion also goes to the platform owner, and the rest adds to the pot.
*/
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, // e king throne
sentAmount, // e ether sent 1e17
claimFee, // e 1e17
pot, // e 5e16
block.timestamp // e time claimed
);
}

Risk

Likelihood:

  • This will happen every time a new player claims the throne and there is an existing king.

  • Since the previous king receives nothing, the expected game dynamics are broken.

Impact:

  • The contract behaves in a way that contradicts its own documentation.

Proof of Concept

This test demonstrates how the first king is dethroned by another player and recieves no portion of the claimFee

function test_previous_king_does_not_receive_portion_of_claim_fee() public {
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
uint256 balanceBefore = address(player1).balance;
vm.prank(player2);
game.claimThrone{value: 1.1e17}();
uint256 balanceAfter = address(player1).balance;
// No increase in balance
assertEq(balanceBefore, balanceAfter);
}

Recommended Mitigation

Modify the claimThrone function to pay a configurable portion of the new claim fee to the previous king.

Updates

Appeal created

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