Last Man Standing

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

Overpayment to claimThrone() Not Refunded, Resulting in Unaccounted ETH Accrual

Root + Impact

Description

  • Players become the King by sending ETH equal to or greater than the current claimFee. However, the contract does not handle excess ETH. Any amount sent above the claimFee is silently absorbed and added to the pot or platform fee. This contradicts reasonable user expectations and opens a vector for malicious frontends to induce overpayment.

require(msg.value >= claimFee, "Game: Insufficient ETH sent");
// No refund of msg.value - claimFee
// Entire msg.value processed as fee and pot contribution

Risk

Likelihood:

  • Occurs whenever a user sends more than claimFee (e.g., by UI mistake, manual tx input, or malicious frontend).

Impact:

  • User funds are silently absorbed without transparency.

  • Malicious UIs could exploit this to inflate pot or platform fees.

  • Reduces trust and introduces unfair game mechanics.

Proof of Concept

This test demonstrates that if a user overpays when calling claimThrone(), the extra ETH is not refunded.

function testExcessEthIsNotRefunded() public {
vm.startPrank(player1);
uint256 balanceBefore = player1.balance;
// Overpay: send 0.5 ETH when only 0.1 ETH is required
game.claimThrone{value: 0.5 ether}();
uint256 balanceAfter = player1.balance;
uint256 expectedSpending = 0.5 ether;
// Confirm full amount was deducted, not just claimFee
assertEq(balanceBefore - balanceAfter, expectedSpending, "Excess ETH was not refunded");
vm.stopPrank();
}

This proves that the user is charged the full 0.5 ETH, rather than just the 0.1 ETH claimFee.

Recommended Mitigation

Enforce exact payment or explicitly refund the difference between msg.value and claimFee:

//Option 1: Require exact ETH
require(msg.value == claimFee, "Game: Must send exact claim fee.");
//Option 2: Accept overpayment but refund
require(msg.value >= claimFee, "Game: Insufficient ETH sent.");
uint256 excess = msg.value - claimFee;
if (excess > 0) {
payable(msg.sender).transfer(excess);
}
Updates

Appeal created

inallhonesty Lead Judge about 2 months 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.