Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: low
Invalid

M02. Direct ETH Transfer Results in Locked Funds

Root + Impact

Description

In normal gameplay, ETH is meant to be sent through the claimThrone() function, where it is distributed between the pot, the platform fees, and potential payouts. The game logic ensures ETH is properly attributed and accounted for.

However, the contract includes a receive() function that accepts ETH transfers with no data, but does not assign the received ETH to the pot, pendingWinnings, or any other internal state. As a result, any ETH sent directly to the contract outside of intended functions is accepted but becomes permanently inaccessible — it cannot be claimed by the winner, withdrawn by the owner, or recovered by the sender.

@> receive() external payable {}

Risk

Likelihood: Low

  • This will occur any time a user mistakenly or intentionally sends ETH to the contract directly (e.g., from a wallet or exchange).

  • There is no UI or function-level enforcement that prevents such transfers.

Impact: MEDIUM to HIGH

  • Users may lose funds that are never incorporated into game logic.

  • Locked ETH bloats the contract balance and causes accounting inconsistencies between getContractBalance() and the sum of tracked balances (pot + pendingWinnings + platformFeesBalance).


Proof of Concept

// Anyone can send ETH directly to the contract
(bool sent, ) = address(game).call{value: 1 ether}("");
require(sent);
// But this ETH is now locked and unaccounted for
uint256 untracked = address(game).balance
- game.pot()
- game.platformFeesBalance()
- game.pendingWinnings(address1)
- game.pendingWinnings(address2);
// untracked > 0


Recommended Mitigation

Choose one of the following solutions based on desired behavior:

Option A: Add to the pot (if donations are acceptable)

+ receive() external payable {
+ pot += msg.value;
+ emit DonationReceived(msg.sender, msg.value);
+ }

This ensures all ETH is useful and contributes to the game.


Option B: Reject direct ETH transfers

- receive() external payable {}
+ receive() external payable {
+ revert("Game: Direct ETH transfers not allowed");
+ }

This prevents accidental or malicious ETH from becoming trapped.

Updates

Appeal created

inallhonesty Lead Judge 11 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Direct ETH transfers - User mistake

There is no reason for a user to directly send ETH or anything to this contract. Basic user mistake, info, invalid according to CH Docs.

accessdenied Submitter
11 days ago
inallhonesty Lead Judge
10 days ago
inallhonesty Lead Judge 7 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Direct ETH transfers - User mistake

There is no reason for a user to directly send ETH or anything to this contract. Basic user mistake, info, invalid according to CH Docs.

Support

FAQs

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