Last Man Standing

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

Accidentally Sent ETH is Permanently Locked in Contract

Root + Impact

The contract includes a receive() function, allowing it to accept direct Ether transfers without an associated function call. However, there is no mechanism to withdraw these funds. Any user who accidentally sends ETH to the contract address will have their funds permanently locked, as the transfer does not credit any user-specific balance or the game pot.

Description

  • The receive() function enables the contract to accept raw ETH transfers.

  • Funds sent this way increase the contract's total balance but are not allocated to the pot, platformFeesBalance, or any pendingWinnings.

  • There is no function for users to reclaim these accidentally transferred funds, leading to a permanent loss.

// ... contract code ...
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
@> receive() external payable {} //@audit no withdraw method
}

Risk

Likelihood:

  • Users may accidentally send ETH to the contract address directly from their wallet, mistaking it for a payment address.

  • This is a common user error, especially for less experienced users interacting with smart contracts.

Impact:

  • Causes direct, irreversible financial loss for users who make this mistake.

  • Can damage the reputation and trustworthiness of the game, as users will see their funds locked without recourse.

Proof of Concept

function test_DirectSendLocksEth() public {
uint256 initialPot = game.pot();
uint256 initialPlatformFees = game.platformFeesBalance();
uint256 initialContractBalance = address(game).balance;
uint256 sendAmount = 1 ether;
// Player1 accidentally sends ETH directly to the contract
(bool success,) = address(game).call{value: sendAmount}("");
require(success, "Failed to send ETH");
// Check that the contract balance increased
uint256 newContractBalance = address(game).balance;
assertEq(newContractBalance, initialContractBalance + sendAmount, "Contract balance should increase");
// Check that the pot and platform fees did not increase
assertEq(game.pot(), initialPot, "Pot should not increase");
assertEq(game.platformFeesBalance(), initialPlatformFees, "Platform fees should not increase");
// Verify the player has no winnings to withdraw
assertEq(game.pendingWinnings(player1), 0, "Player should have no pending winnings");
// Attempting to withdraw should fail
vm.startPrank(player1);
vm.expectRevert("Game: No winnings to withdraw.");
game.withdrawWinnings();
vm.stopPrank();
}

Recommended Mitigation

Instead of removing the receive() function, an alternative is to add a new function that allows the contract owner to withdraw any funds that have been accidentally sent to the contract.

// ... contract code ...
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
receive() external payable {}
+ /**
+ * @dev Allows the contract owner to withdraw funds from the contract.
+ * This function is intended to rescue funds that are accidentally sent to the contract.
+ * NOTE: This gives the owner the ability to withdraw any funds from the contract, including the pot.
+ * This assumes the owner is trusted.
+ * @param to The address to send the funds to.
+ * @param amount The amount of ETH to send.
+ */
+ function emergencyWithdraw(address payable to, uint256 amount) external onlyOwner {
+ require(to != address(0), "Game: Cannot send to zero address");
+ require(address(this).balance >= amount, "Game: Insufficient balance for withdrawal");
+ (bool success, ) = to.call{value: amount}("");
+ require(success, "Game: Emergency withdrawal failed.");
+ }
}
Updates

Appeal created

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

Give us feedback!