TwentyOne

First Flight #29
Beginner FriendlyGameFiFoundrySolidity
100 EXP
View results
Submission Details
Severity: high
Invalid

TwentyOne::call function has a logic error

Summary

TwentyOne::call function has a logic error and the player wins even with playersHand > 21.

TwentyOne::call function has a logic error and doesn't check for if the playersHand > 21 and players wins even if (playersHand > 21 when playersHand > dealersHand)

Vulnerability Details

In TwentyOne::call function

if (dealerHand > 21) {
emit PlayerWonTheGame(
"Dealer went bust, players winning hand: ",
playerHand
);
endGame(msg.sender, true);
-->> } else if (playerHand > dealerHand) { // -->> no check for if the playerHand is > 21
emit PlayerWonTheGame(
"Dealer's hand is lower, players winning hand: ",
playerHand
);
endGame(msg.sender, true);
} else {
emit PlayerLostTheGame(
"Dealer's hand is higher, dealers winning hand: ",
dealerHand
);
endGame(msg.sender, false);
}

Impact

The player will win even if his score is greater than 21, which should not happen and will break tha game.

Proof of Code

Run this test by making TwentyOne::addCardForPlayer , TwentyOne::addCardForDealer , TwentyOne::initializeDeck functions public and commenting out the standThreshold of dealer in the TwentyOne::call function

function testCallPlayerHandGreaterThan21AndDealer() public {
vm.startPrank(player1);
vm.deal(address(twentyOne), 10 ether);
twentyOne.initializeDeck(player1);
twentyOne.addCardForPlayer(player1, 10); // Player's card (King, value = 10)
twentyOne.addCardForPlayer(player1, 10); // Player's card (Ace, value = 10)
twentyOne.addCardForPlayer(player1, 10); // Player's card (Queen, value = 10)
twentyOne.addCardForDealer(player1, 12); // Dealer's card (2)
twentyOne.addCardForDealer(player1, 9); // Dealer's card (3)
// Verify player's hand > 21
uint256 playerHand = twentyOne.playersHand(player1);
uint256 dealerHand = twentyOne.dealersHand(player1);
assertGt(playerHand, 21, "Player's hand is not greater than 21");
assertGt(playerHand, dealerHand, "Player's hand is not greater than dealer's hand");
// Call the function
vm.expectEmit(true , true , true , true);
emit TwentyOne.PlayerWonTheGame("Dealer's hand is lower, players winning hand: ",
playerHand);
twentyOne.call();
vm.stopPrank();
}

Tools Used

Manual Review

Recommendations

Add the following in your TwentyOne::call function

function call() public {
require(
playersDeck[msg.sender].playersCards.length > 0,
"Game not started"
);
uint256 playerHand = playersHand(msg.sender);
Calculate the dealer's threshold for stopping (between 17 and 21)
uint256 standThreshold = (uint256(
keccak256(
abi.encodePacked(block.timestamp, msg.sender, block.prevrandao)
)
) % 5) + 17;
Dealer draws cards until their hand reaches or exceeds the threshold
while (dealersHand(msg.sender) < standThreshold) {
uint256 newCard = drawCard(msg.sender);
addCardForDealer(msg.sender, newCard);
}
uint256 dealerHand = dealersHand(msg.sender);
// Determine the winner
if (dealerHand > 21) {
emit PlayerWonTheGame(
"Dealer went bust, players winning hand: ",
playerHand
);
endGame(msg.sender, true);
+ } else if (playerHand > dealerHand && playerHand <= 21>) {
emit PlayerWonTheGame(
"Dealer's hand is lower, players winning hand: ",
playerHand
);
endGame(msg.sender, true);
} else {
emit PlayerLostTheGame(
"Dealer's hand is higher, dealers winning hand: ",
dealerHand
);
endGame(msg.sender, false);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
7 months ago
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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