TwentyOne

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

Improper Handling of Ties in `call` Function

Summary

The call function does not explicitly handle ties between the player and the dealer. In the current implementation, ties are treated as losses for the player, which may not align with typical Blackjack rules where a tie usually results in the player's bet being returned.

Vulnerability Details

In the call function, the else if statement determines the outcome of the game by comparing the player's hand to the dealer's hand.

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) {
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);
}
}

The last else block handles both cases where the dealer's hand is greater than or equal to the player's hand, resulting in the player losing even in a tie situation.

Impact

Ties being treated as losses for the player can lead to unfair outcomes and player dissatisfaction.

Tools Used

Manual Review

Recommended Mitigation

Modify both the call and endGame functions to handle bet returns in the event of a tie. A new parameter can be introduced to indicate whether the game ended in a tie. If it is a tie, the endgame function should return the player's original bet amount without any additional winnings.

The else if statement in the call function should be modified to pass the appropriate parameters to endGame function.

if (dealerHand > 21) {
emit PlayerWonTheGame(
"Dealer went bust, players winning hand: ",
playerHand
);
endGame(msg.sender, true, false);
} else if (playerHand > dealerHand) {
emit PlayerWonTheGame(
"Dealer's hand is lower, players winning hand: ",
playerHand
);
endGame(msg.sender, true, false);
}else if (playerHand == dealerHand) {
emit PlayerLostTheGame(
"It's a tie, player's bet is returned",
playerHand
);
endGame(msg.sender, false, true);
} else {
emit PlayerLostTheGame(
"Dealer's hand is higher, dealers winning hand: ",
dealerHand
);
endGame(msg.sender, false, false);
}

With the introduction of a new parameter to indicate whether the game ended in a tie, the endgame function should return the player's original bet amount.

function endGame(address player, bool playerWon, bool isTie) internal {
delete playersDeck[player].playersCards; // Clear the player's cards
delete dealersDeck[player].dealersCards; // Clear the dealer's cards
delete availableCards[player]; // Reset the deck
if (isTie) {
// Return the player's original bet of 1 ether
payable(player).transfer(1 ether);
emit FeeWithdrawn(player, 1 ether); // Emit the bet return event
} else if (playerWon) {
// Transfer the prize to the player
payable(player).transfer(2 ether);
emit FeeWithdrawn(player, 2 ether); // Emit the prize withdrawal event
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Tie case

Support

FAQs

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