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);
uint256 standThreshold = (uint256(
keccak256(
abi.encodePacked(block.timestamp, msg.sender, block.prevrandao)
)
) % 5) + 17;
while (dealersHand(msg.sender) < standThreshold) {
uint256 newCard = drawCard(msg.sender);
addCardForDealer(msg.sender, newCard);
}
uint256 dealerHand = dealersHand(msg.sender);
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;
delete dealersDeck[player].dealersCards;
delete availableCards[player];
if (isTie) {
payable(player).transfer(1 ether);
emit FeeWithdrawn(player, 1 ether);
} else if (playerWon) {
payable(player).transfer(2 ether);
emit FeeWithdrawn(player, 2 ether);
}
}