Last Man Standing

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

Untracked ETH Sent to receive() Function

Root + Impact

Description

  • All ETH sent to the contract should either be part of the game mechanics (pot/fees) or be rejected.

    Specific issue:
    The contract has a receive() function that accepts ETH but doesn't update any state variables. This ETH becomes orphaned - it exists in the contract balance but is not tracked in pot, platform fees, or winnings.

receive() external payable {
// @> Accepts ETH but doesn't update any state variables
// This ETH becomes orphaned and untrackable
}

Risk

Likelihood:

  • Users accidentally send ETH directly to contract address.

  • UI bugs cause incorrect transaction formatting.

  • Users confuse direct transfers with game participation.

Impact:

  • ETH becomes permanently orphaned and unrecoverable.

  • Contract balance exceeds tracked amounts (pot + platform fees + winnings).

  • Value is lost with no way to recover through game mechanics.

Proof of Concept

function testUntrackedETHSentToReceive() public {
uint256 potBefore = game.pot();
uint256 platformFeesBefore = game.platformFeesBalance();
uint256 contractBalanceBefore = game.getContractBalance();
// Send ETH directly to contract
uint256 orphanedETH = 1 ether;
vm.startPrank(player1);
(bool success,) = address(game).call{value: orphanedETH}("");
assertTrue(success, "Direct ETH transfer should succeed");
vm.stopPrank();
// ETH was accepted but not tracked anywhere
assertEq(game.pot(), potBefore, "Pot should not increase");
assertEq(game.platformFeesBalance(), platformFeesBefore, "Platform fees should not increase");
assertEq(game.getContractBalance(), contractBalanceBefore + orphanedETH, "Contract balance increased");
// ETH is now orphaned
uint256 untrackedETH = game.getContractBalance() - game.pot() - game.platformFeesBalance();
assertEq(untrackedETH, orphanedETH, "ETH should be untracked/orphaned");
}

Recommended Mitigation

Reject direct transfers

receive() external payable {
- // Empty function that accepts ETH
+ revert("Game: Use claimThrone() to participate");
}
Updates

Appeal created

inallhonesty Lead Judge 9 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.