Last Man Standing

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

Incorrect Claim Fee Increase Based on Minimum Instead of Actual Sent Amount

Root + Impact

Description

In a normal flow, when a player claims the throne, they must pay at least claimFee. Upon successful claim, the contract increases claimFee by a percentage defined in feeIncreasePercentage.

However, the current implementation increases the claim fee based only on the minimum threshold, rather than the actual amount sent, which may be higher. This allows a player to overpay significantly to create the illusion of a high-value throne (to hold the throne for longer and make it harder for others to claim it), yet the next user can still claim the throne with a minimal increase over the baseclaimFee.

This breaks the intended fairness of increasing claim cost over time and allows strategic manipulation of perceived throne value.

function claimThrone() external payable gameActive nonReentrant {
require(
msg.value >= claimFee,
"Game: Insufficient ETH sent to claim the throne."
);
uint256 sentAmount = msg.value;
uint256 currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
platformFeesBalance += currentPlatformFee;
uint256 contributionToPot = sentAmount - currentPlatformFee;
pot += contributionToPot;
address previousKing = currentKing;
currentKing = msg.sender;
lastClaimTime = block.timestamp;
// The next claim fee is calculated based on the previous claimFee, not on the actual amount deposited
@> claimFee = claimFee + ((claimFee * feeIncreasePercentage) / 100);
totalClaims++;
emit ThroneClaimed(msg.sender, sentAmount, claimFee, pot, block.timestamp);
}

Risk

Likelihood:

The likelihood is High because:

  • This is very likely to occur in competitive scenarios, where players deliberately overpay to secure the throne and psychologically deter challengers.

  • Users may falsely assume the next claim will require an amount proportionate to the previous deposit, but the contract logic doesn't enforce that.

Impact:

The impact is high because it:

  • Breaks fairness: allows manipulation of the claim cost mechanism.

  • Can create a deceptive sense of high-value gameplay while allowing low-cost takeovers, possibly affecting user trust or leading to front-running.

Proof of Concept

The POC demonstrates how an user who overpaid so that he can stay at the throne for longer and make it harder for others to claim his throne is overthrown by a lower deposit that his.

Add the test below to the Game.t.sol and use the following script:

NOTE: for the poc to work replace the following require statement inside the claimThrone() (which is another issue):

- require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
+ require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");
forge test --match-path test/Game.t.sol --match-test test_userWithLargeDepositIsOverthrownByAnotherUserWithSmallerDeposit
function test_userWithLargeDepositIsOverthrownByAnotherUserWithSmallerDeposit()
public
{
// player sends 1 eth (the minimum is 0.1 eth)
vm.prank(player1);
game.claimThrone{value: 1 ether}();
// player2 sends only 0.11 eth to overthrow the current king
vm.prank(player2);
game.claimThrone{value: 0.11 ether}();
assertEq(player2, game.currentKing());
}

Result:

Ran 1 test for test/Game.t.sol:GameTest
[PASS] test_userWithLargeDepositIsOverthrownByAnotherUserWithSmallerDeposit() (gas: 191453)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.14ms (152.92µs CPU time)
Ran 1 test suite in 7.19ms (1.14ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

Instead of increasing claimFee based on the old claimFee, it should increase based on the actual contribution to the pot (sentAmount - fee), which better reflects the real stake of the last king:

- claimFee = claimFee + ((claimFee * feeIncreasePercentage) / 100);
+ claimFee = amountToPot + ((amountToPot * feeIncreasePercentage) / 100);
Updates

Appeal created

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Overpayment not refunded, included in pot, but not in claim fee

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.