TwentyOne

First Flight #29
Beginner FriendlyGameFiFoundrySolidity
100 EXP
View results
Submission Details
Severity: medium
Valid

The game contract may not have enough ether to pay out the ether if players win the game.

Description: If the game contract does not have enough ether, player can still start the game, if the player win the game, the pay out will fail.

function endGame(address player, bool playerWon) internal {
delete playersDeck[player].playersCards; // Clear the player's cards
delete dealersDeck[player].dealersCards; // Clear the dealer's cards
delete availableCards[player]; // Reset the deck
if (playerWon) {
@> payable(player).transfer(2 ether); // Transfer the prize to the player
emit FeeWithdrawn(player, 2 ether); // Emit the prize withdrawal event
}
}

Impact:
The can break the game pay out logic, player can win the game but the pay out will fail.

Proof of Concept:

  1. create a fresh game contract.

  2. PLAYER deposits 1 ether and win the game.

Add the following code into TwentyOne.t.sol:

Proof of Code ```solidity function test_PlayerWins_but_game_has_insufficient_eth() public { vm.startPrank(player1);
twentyOne.startGame{value: 1 ether}();
vm.expectRevert();
twentyOne.call();
vm.stopPrank();
}
```
run `forge test --mt test_PlayerWins_but_game_has_insufficient_eth` it will pass. run `forge test --mt test_PlayerWins_but_game_has_insufficient_eth -vvvv` you will see the error in console `EvmError: OutOfFunds`.

Recommended Mitigation:
Add a lockedPayoutEther to keep track of the ether that is locked for payout, and check if the contract has enough balance to pay out the ether.

function startGame() public payable returns (uint256) {
address player = msg.sender;
require(msg.value == BET, "only 1 ether allowed");
// fix for payout
+ require(address(this).balance - lockedPayoutEther >= PAYOUT, "not enough balance to pay out");
+ lockedPayoutEther += PAYOUT;
...
}
...
function endGame(address player, bool playerWon) internal {
delete playersDeck[player].playersCards; // Clear the player's cards
delete dealersDeck[player].dealersCards; // Clear the dealer's cards
delete availableCards[player]; // Reset the deck
+ lockedPayoutEther -= PAYOUT;
if (playerWon) {
payable(player).transfer(PAYOUT); // Transfer the prize to the player
emit FeeWithdrawn(player, PAYOUT); // Emit the prize withdrawal event
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Insufficient balance for payouts / Lack of Contract Balance Check Before Starting Game

Support

FAQs

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