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;
delete dealersDeck[player].dealersCards;
delete availableCards[player];
if (playerWon) {
@> payable(player).transfer(2 ether);
emit FeeWithdrawn(player, 2 ether);
}
}
Impact:
The can break the game pay out logic, player can win the game but the pay out will fail.
Proof of Concept:
create a fresh game contract.
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");
+ 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;
delete dealersDeck[player].dealersCards;
delete availableCards[player];
+ lockedPayoutEther -= PAYOUT;
if (playerWon) {
payable(player).transfer(PAYOUT);
emit FeeWithdrawn(player, PAYOUT);
}
}