Last Man Standing

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

Faulty Reward Mechanism: No ETH Returned to Previous King in Throne Claiming

Root + Impact

Missing Logic to calculate and assign the previousKingPayout in the Game::claimThrone function leaving no ETH for the previous King.

Description

  • When a new player claims the throne by paying the required claimFee, a portion of their payment should be allocated to the previous king as a payout, the platform takes a small percentage as a fee, and the rest goes into the prize pot. This incentivizes participation and ensures fair distribution of funds between players and the platform.

  • The previousKingPayout is initialized to 0 and never updated with the actual payout amount owed to the previous king. As a result, when a new player claims the throne, the previous king receives no compensation.

// Root cause in the codebase.
//The contract fails to calculate or assign a value to previousKingPayout, which is referenced in critical fee logic.
//Since previousKingPayout remains 0, it breaks the intended logic around fee deductions and reward distribution.

Risk

Likelihood:

  • This issue occurs whenever a user claims the throne, as the fee logic always references previousKingPayout, which is never set or updated in the contract.

  • Since the contract is live and allows multiple users to claim the throne, the faulty logic is consistently triggered in every execution of the claimThrone function, leading to loss of funds for each previous king.

Impact:

  • The previous king never receives any payout , resulting in a complete loss of funds for that user after being dethroned.

  • The entire claim amount is either allocated to the platform fee or the pot inaccurately, breaking the intended reward flow of the game and discouraging users from participating further.

Proof of Concept

To uncover this issue, a minor modification was made to the claimThrone() function:

//Original condition
require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");

//Modified for testing purposes:
require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");

This change allowed multiple distinct users to claim the throne consecutively, which revealed that the previousKingPayout is always zero, meaning the dethroned king receives no payout at all.

This Proof of Concept can be tested in Game.t.sol

function test_missingpreviousKingPayout() public {
vm.deal(player1, 10 ether);
vm.prank(player1);
game.claimThrone{value: 10 ether}();
console2.log(
"Platform Balance after player1:",
game.platformFeesBalance()
);
vm.deal(player2, 10 ether);
vm.prank(player2);
game.claimThrone{value: 6 ether}();
console2.log(
"Platform Balance after player2:",
game.platformFeesBalance()
);
console2.log("Pot balance:", game.pot());
console2.log(
"Pending winnings for player1:",
game.pendingWinnings(player1)
);
}

Recommended Mitigation

To ensure dethroned kings are fairly compensated, the contract should:

  • Calculate the previousKingPayout before assigning the new king, This could be a fixed percentage of the incoming claimFee or based on a Formula.

  • Assign the payout to the correct address by updating pendingWinnings[previousKing] before changing currentKing

// Store previous king
+ address previousKing = currentKing;
// Define a percentage or formula for the dethroned king’s payout
+ uint256 previousKingPayout = (msg.value * previousKingPayoutPercentage) / 100;
//Update their pending balance
+ pendingWinnings[previousKing] += 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.