Last Man Standing

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

No Mechanism to Recover Stuck Funds .

Root + Impact

Description

Smart contract users can mistakenly send ETH directly to a contract address instead of invoking a specific function like claimThrone(). Since the contract does not implement a receive() or fallback() function, and no recovery function exists, these funds become permanently stuck inside the contract.

This behavior results in:

  • Unclaimable ETH held indefinitely in the contract.

  • User confusion or frustration due to loss of funds.

  • A misleading on-chain balance that does not reflect actual game logic or available pot size.

// No receive() or fallback() function
// No logic to redirect or use funds from raw ETH transfers
// Example of a user sending ETH directly (gets stuck):
@> contractInstanceAddress.transfer(1 ether);

Risk

Likelihood: Medium

  • Many users (especially non-technical or mobile wallet users) may interact directly with the contract address by mistake.

  • It is not unusual for users to send ETH directly without calling a specific function.

Impact: Low

  • ETH becomes inaccessible, but not vulnerable to theft.

  • Affects transparency and user experience more than core protocol functionality.


Proof of Concept

// There is no receive() or fallback() defined in the contract.
// This means ETH sent directly is accepted but not processed.
(address(contract)).transfer(1 ether); // ETH enters the contract but is unusable
// Contract balance increases
// No event is emitted
// Funds are not reflected in pot or available to players

PoC Explanation:

  1. A user mistakenly sends ETH to the contract address directly.

  2. The contract accepts the ETH (because receive/fallback is not defined to revert), but doesn’t track it.

  3. No function handles it; ETH stays locked forever unless explicitly recovered.


Recommended Mitigation

- // No receive() or fallback() defined
+ receive() external payable {
+ revert("Direct ETH transfers not allowed");
+ }
+ function recoverStuckFunds() external onlyOwner {
+ payable(owner).transfer(address(this).balance - currentPot);
+ }
  • Option 1: Reject unintended ETH transfers with receive() that reverts.

  • Option 2: Implement an onlyOwner recovery function that safely transfers excess funds not used by the protocol logic.

  • Option 3: Auto-forward funds to game logic if appropriate (e.g., count them toward the pot).


References


Updates

Appeal created

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

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