Last Man Standing

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

No Previous King Payout on Throne Claim

Root + Impact

Description

  • Normal behavior:
    When a new player claims the throne, the previous king should receive a portion of the claim fee as a reward for holding the throne.

    Specific issue:
    The contract does not pay the previous king when a new player claims the throne. Although the variable previousKingPayout is declared, it is never used to credit the dethroned king. As a result, the previous king’s pendingWinnings remains zero, and they receive no reward.

// src/Game.sol
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; // @> Bug: payout is not calculated or assigned
// ...rest of code...
// No code to credit previous king!
currentKing = msg.sender;
// ...rest of code...
}

Risk

Likelihood:

  • This will occur every time a new player claims the throne, as the payout logic is missing.

Impact:

  • The dethroned king receives no reward, breaking the game’s economic incentives and fairness.

  • Violates the protocol’s documentation and may lead to loss of trust or legal risk.

Proof of Concept

The following test script demonstrates the bug. Player1 claims the throne, then player2 claims the throne. Player1’s pendingWinnings remains zero, proving that no payout is made.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test, console2} from "forge-std/Test.sol";
import {Game} from "../src/Game.sol";
contract GameTest is Test {
Game public game;
address public deployer;
address public player1;
address public player2;
uint256 public constant INITIAL_CLAIM_FEE = 0.1 ether;
uint256 public constant GRACE_PERIOD = 1 days;
uint256 public constant FEE_INCREASE_PERCENTAGE = 10;
uint256 public constant PLATFORM_FEE_PERCENTAGE = 5;
function setUp() public {
deployer = makeAddr("deployer");
player1 = makeAddr("player1");
player2 = makeAddr("player2");
vm.deal(deployer, 10 ether);
vm.deal(player1, 10 ether);
vm.deal(player2, 10 ether);
vm.startPrank(deployer);
game = new Game(
INITIAL_CLAIM_FEE,
GRACE_PERIOD,
FEE_INCREASE_PERCENTAGE,
PLATFORM_FEE_PERCENTAGE
);
vm.stopPrank();
}
function testPreviousKingGetsPayout() public {
// Player1 claims throne
vm.startPrank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
// Calculate next claim fee (with 10% increase)
uint256 nextClaimFee = INITIAL_CLAIM_FEE + (INITIAL_CLAIM_FEE * FEE_INCREASE_PERCENTAGE) / 100;
// Player2 claims throne
vm.startPrank(player2);
game.claimThrone{value: nextClaimFee}();
vm.stopPrank();
// Expected payout to player1 (example: 10% of player2's claim, adjust as per your logic)
uint256 expectedPayout = (nextClaimFee * 10) / 100; // If 10% is the previous king's share
// Check pendingWinnings for player1
assertEq(game.pendingWinnings(player1), expectedPayout, "Previous king did not receive correct payout");
}
}

Recommended Mitigation

Pay the previous king before updating currentKing in claimThrone():

+ uint256 previousKingPayout = (sentAmount * 10) / 100; // 10% for previous king
+ if (currentKing != address(0) && previousKingPayout > 0) {
+ pendingWinnings[currentKing] += 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.