Last Man Standing

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

Missing Previous King Payout on Throne Claim

Root + Impact

Description

  • When a new player claims the throne, the dethroned (previous) king should receive a payout from the new claim, as described in the README (“Receives a small payout from the next player's claimFee”).

  • The current implementation never pays or credits any amount to the previous king. The relevant code in claimThrone() only initializes previousKingPayout to 0 and never updates or pays it. As a result, dethroned kings receive no reward for holding the throne, breaking the intended game economics.

@> uint256 previousKingPayout = 0; // @> Should be calculated and credited to previous king
...
// @> There is no logic to credit the previous king with their payout

Risk

Likelihood:

  • This will always occur whenever a player is dethroned. The previous king receives nothing because their payout is never calculated or credited.

  • Anyone reading the README or expecting the documented behavior will notice the missing payout in both tests and live usage.

Impact:

  • The dethroned king is never rewarded, breaking fairness and removing a key incentive from the game.

  • Players may lose trust or interest, leading to reduced participation and negative reputation for the protocol.

  • The contract does not match its documentation or intended game rules.

Proof of Concept

  1. Alice claims the throne.

  2. Bob claims the throne after Alice.

  3. Expected: Alice receives a payout from Bob’s claim.

  4. Actual: Alice receives nothing. The entire amount (minus platform fee) goes only to the pot.

pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "../src/Game.sol";
contract GamePreviousKingPayoutTest is Test {
Game public game;
address public alice = address(0xA11CE);
address public bob = address(0xB0B);
function setUp() public {
// Deploy the Game contract as "owner" (address(this))
// Let's say: initialClaimFee = 1 ether, gracePeriod = 1 day, feeIncrease = 10, platformFee = 5
game = new Game(1 ether, 1 days, 10, 5);
}
function testPreviousKingGetsNothing() public {
// Alice claims the throne, sending exactly claimFee (1 ether)
vm.deal(alice, 2 ether); // Give Alice some ETH
vm.startPrank(alice);
game.claimThrone{value: 1 ether}();
vm.stopPrank();
// Bob claims the throne, sending next claimFee (should be >1 ether, but let's send 1.1 for demo)
uint256 nextFee = game.claimFee();
vm.deal(bob, 2 ether);
vm.startPrank(bob);
game.claimThrone{value: nextFee}();
vm.stopPrank();
// At this point, Alice was dethroned. Let's check her pending winnings
uint256 aliceWinnings = game.pendingWinnings(alice);
// Fails: Alice gets nothing as previous king
assertEq(aliceWinnings, 0, "Alice should have received previous king payout, but got nothing.");
}
}

Recommended Mitigation


  1. Add a parameter for previousKingPayoutPercentage (e.g., settable by the owner, capped at 100).

  2. On every new claim, if there was a previous king, credit them their share to pendingWinnings.

  3. Ensure the sum of previous king payout, platform fee, and amount to pot does not exceed sentAmount.

  4. Players can then withdraw their pending winnings via the existing withdrawWinnings() function.

function claimThrone() external payable gameNotEnded nonReentrant {
- uint256 previousKingPayout = 0;
+ uint256 previousKingPayout = 0;
+ address previousKing = currentKing;
+
+ // Only pay previous king if there is one (i.e., not address(0))
+ if (previousKing != address(0)) {
+ previousKingPayout = (sentAmount * previousKingPayoutPercentage) / 100;
+ pendingWinnings[previousKing] += previousKingPayout;
+ }
uint256 currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
- if (currentPlatformFee > (sentAmount - previousKingPayout)) {
- currentPlatformFee = sentAmount - previousKingPayout;
- }
platformFeesBalance = platformFeesBalance + currentPlatformFee;
- amountToPot = sentAmount - currentPlatformFee;
- pot = pot + amountToPot;
+ amountToPot = sentAmount - currentPlatformFee - previousKingPayout;
+ pot = pot + amountToPot;
Updates

Appeal created

inallhonesty Lead Judge 4 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!