Last Man Standing

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

Missing Previous King Compensation Logic Prevents Expected Payouts Upon Dethroning

Missing Previous King Compensation Logic Prevents Expected Payouts Upon Dethroning

Description

  • The documentation specifies that the previous king should receive a small portion of the next player's claimFee as a payout when dethroned. This is a core part of the incentive mechanism described in both the inline comments and the project README.

  • However, the Game::claimThrone() function never actually computes or transfers any payout to the previous king. The variable previousKingPayout is defined and used in a defensive fee cap, but it is always set to 0 and never updated. As a result, the previous king will receives nothing, and the logic intended to ensure fee correctness becomes dead code.

@> uint256 previousKingPayout = 0;
...
@> if (currentPlatformFee > (sentAmount - previousKingPayout)) {
@> currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;

The defensive cap sentAmount - previousKingPayout is meaningless because previousKingPayout is always 0.

Risk

Likelihood:

  • This happens on every throne claim after the first one.

  • The payout logic is completely missing, regardless of game round or state.

Impact:

  • Previous kings never receive the compensation promised in the documentation.

  • Players are financially disincentivized from participating multiple times.

  • Funds that should go to dethroned kings are instead redirected to the platform fees or the pot.

Proof of Concept

Add this proof of code to the Game.t.sol test file. Also make sure to correct the breaking logic that prevent anyone to call claimThrone to check this missing payout.

function testPreviousKingReceivesNoPayout() public {
// Capture balance of player1 before doing anything
uint256 balanceBefore = player1.balance;
// player1 becomes king
vm.prank(player1);
game.claimThrone{value: 0.2 ether}();
// player2 dethrones player1
vm.prank(player2);
game.claimThrone{value: 0.3 ether}();
// Player 1 balance after being dethroned
uint256 balanceAfter = player1.balance;
// Assert that player 1 received no payout
assertEq(balanceAfter, balanceBefore - 0.2 ether);
}

Recommended Mitigation

  • Add explicit logic to calculate and send a payout to the dethroned king before updating the game state:

address dethronedKing = currentKing;
...
uint256 previousKingPayout = (sentAmount * kingPayoutPercentage) / 100;
(bool sent, ) = payable(dethronedKing).call{value: previousKingPayout}("");
require(sent, "Failed to pay previous king.");
  • Then update the defensive fee cap logic accordingly:

if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
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.