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