Last Man Standing

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

Reentrancy Attack Possible in Winner Withdrawal Function

#Scope = src/Game.sol

Players Can Lose ETH When Sending More Than Required Claim Fee

Description

  • Players are expected to send exactly the claimFee amount to claim the throne, with any excess being added to their claim value.

  • When players send more ETH than the required claimFee, the entire amount is used for fee calculations (platform fee and previous king payout), potentially causing players to pay much more than intended without receiving proportional benefits.

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; // Uses entire msg.value, not just claimFee
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// If there's a previous king, calculate their payout (small percentage of the claim fee)
if (currentKing != address(0)) {
@> previousKingPayout = (sentAmount * 5) / 100; // 5% of entire amount, not just claimFee
}
// Calculate platform fee
@> currentPlatformFee = (sentAmount * platformFeePercentage) / 100; // Platform fee on entire amount

Risk

Likelihood: HIGH

  • Players commonly send round numbers or slightly more than required to ensure transactions succeed

  • Frontend interfaces may have rounding errors or users may manually enter amounts

  • No mechanism exists to refund excess ETH sent beyond the claim fee

Impact: MEDIUM

  • Players lose ETH through unintended higher fees on excess amounts

  • Previous kings receive unintended larger payouts from accidental overpayments

  • Creates unfair advantage for players who accidentally send more ETH

Proof of Concept

This test demonstrates how overpayments result in excessive fees charged on the entire amount instead of just the required claim fee.

function testOverpaymentExcessiveFees() public {
// Player 1 claims throne with exact amount
vm.prank(player1);
game.claimThrone{value: 0.1 ether}(); // Exact claimFee
// Player 2 accidentally sends 1 ETH instead of required 0.11 ETH
uint256 player1BalanceBefore = game.pendingWinnings(player1);
uint256 platformFeesBefore = game.platformFeesBalance();
vm.prank(player2);
game.claimThrone{value: 1 ether}(); // Massive overpayment
// Verify excessive fees charged on full amount instead of just claimFee:
// Previous king gets 5% of 1 ETH = 0.05 ETH instead of 0.0055 ETH (9x more)
assertEq(game.pendingWinnings(player1) - player1BalanceBefore, 0.05 ether);
// Platform gets 5% of 1 ETH = 0.05 ETH instead of 0.0055 ETH (9x more)
assertEq(game.platformFeesBalance() - platformFeesBefore, 0.05 ether);
}
```## Recommended Mitigation
Calculate fees only on the required `claimFee` amount and refund excess ETH to prevent overpayment losses.
```diff
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 sentAmount = claimFee; // Only use required claim fee for calculations
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// If there's a previous king, calculate their payout (small percentage of the claim fee)
if (currentKing != address(0)) {
previousKingPayout = (sentAmount * 5) / 100; // 5% to previous king
}
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// ... rest of existing logic ...
+ // Refund excess ETH to sender
+ uint256 excess = msg.value - claimFee;
+ if (excess > 0) {
+ (bool success, ) = payable(msg.sender).call{value: excess}("");
+ require(success, "Game: Failed to refund excess ETH");
+ }
}
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.