The function TwentOne::endGame
does not handle ETH transfers properly which means in case of a failed ETH transfer the function reverts and the player that wins does not receive a payout as expected.
The function TwentOne::endGame
uses transfer
to transfer ETH from the contract to the player. While this choice can prevent reentrancy attacks due to gas limits, this causes a problem when the function reverts. Since the game state is not stored, an ETH transfer that reverts prevents the winning player from collecting their payout. Calling the TwentOne::call
again, will make the dealer to redraw the cards which leads to a new game result while the previous game state is lost.
Player starts game with 1 ETH
Player calls TwentyOne::call
Player wins the game
Function TwentyOne::call
calls TwentyOne::endGame
to pay out the player
Function TwentyOne::endGame
fails and reverts because of failed ETH transfer
No payout for winner, new game can't be started until TwentyOne::endGame
is successfully called
Player calls TwentyOne::call
but now looses
Player never received payout for win
Code:
Place following code into TwentyOne.t.sol
:
This is medium impact as failed ETH transfers likely stem from smart contracts (eg. that are missing a fallback function or have a fallback function with high gas consumption) playing the game which one can assume for this type of protocol is fairly rare. However, when it does occur it does break critical functionality for the impacted player. Because when a player wins and the ETH transfer of the payout to the player fails, the player may cannot receive their payout unless they are lucky and calling the TwentOne::call
function again results again in a win and the ETH transfer succeeds.
Foundry, manual review, custom test
To ensure smooth functioning of the protocol in any scenario, the best way to handle it is storing the owed payout for each winner which they can withdraw later to a specified address instead of paying out players directly in the TwentOne::endGame
function. This way, if issues occur sending the ETH to a specific address, the funds can be withdrawn by the player to an alternative address at a later point. This also solves the issue that the player can't restart a new game until the previous game is successfully ended (i.e. funds received).
For example the following mapping and function could be added:
This would require the following change in the TwentOne::endGame
function:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.