TwentyOne

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

The `TwentyOne` contract has no eth reserved for winners' payout, this would cause the transfer in `TwentyOne::endGame` to revert, robbing winning players of their prize

Summary

The protocol has no extra ether to pay out to winners if players win more than they lose. As a consequence, the protocol will not be able to pay winners their rightful prize. This would mean that a winner is not guaranteed their payout, thus severely breaking the protocol's functionality.

Vulnerability Details

Proof of Concept:

  1. Player calls TwentyOne::startGame and pays the entrance fee of 1 ether.

  2. Player calls TwentyOne::call and wins the game.

  3. The call function calls endGame with the player as the winner.

  4. The transfer in endGame fails with an EVM::Out of funds error.

  5. The transaction reverts and player does not get their payout.

Proof of Code:

Place the below code into TwentyOneTest in TwentyOne.t.sol

event PlayerWonTheGame(string message, uint256 cardsTotal);
.
.
.
function testAWinnerDoesNotGetPaid() public {
//Call startGame as player1
vm.startPrank(player1);
uint256 playerHand = twentyOne.startGame{value: 1 ether}();
uint256 initialPlayerBalance = player1.balance;
//Player calls to compare hands and wins
vm.expectRevert();
vm.expectEmit(true, true, false, true);
emit PlayerWonTheGame("Dealer went bust, players winning hand: ", playerHand);
twentyOne.call();
//Player's balance does not increase, prize transfer fails
uint256 finalPlayerBalance = player1.balance;
assertEq(finalPlayerBalance, initialPlayerBalance);
vm.stopPrank();
}

Impact

Winning players may not get their payout.

Tools Used

Foundry suite

Recommendations

A check that the contract has enough funds for the payout should be made before calculating the winner in the TwentyOne::call function. If the contract does not have enough funds, that should be handled properly as well.

.
.
.
function call() public {
require(playersDeck[msg.sender].playersCards.length > 0, "Game not started");
+ require(address(this).balance >= 2 ether, "Eth balance not sufficient for payout");
.
.
.
}
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.