TwentyOne

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

Potential Bias in Dealer Behavior Due to Stand Threshold Logic in call() Function

Summary
The call() function of the TwentyOne smart contract introduces an issue where the dealer's likelihood of busting is disproportionately low due to the implementation of a deterministic stopping condition (standThreshold) in the range of 17–21. This condition skews the game in favor of the dealer, reducing the player's chances of winning.

Vulnerability Details

Problematic Code

The following segment from the call() function sets a threshold for the dealer's hand and enforces a drawing rule:

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

This threshold (standThreshold) is randomized within a range of 17 to 21. The dealer draws cards until their hand reaches or exceeds this value. However:

  1. The dealer stops drawing as soon as their hand equals or exceeds standThreshold.

  2. If the dealer exceeds 21 during this process, they bust—but the limited range (17–21) minimizes the likelihood of this happening.

Dealer Advantage

  • The threshold range biases the game against the player by ensuring the dealer rarely draws enough cards to bust.

  • This creates an imbalanced game design where the dealer has a statistically lower risk of losing compared to the player, who must actively avoid busting at all stages.

Impact

  • The biased standThreshold implementation diminishes fairness in gameplay by heavily favoring the dealer. This issue could lead to:

    1. Reduced Trust: Players may perceive the game as rigged or unfair.

    2. Financial Loss: Players are more likely to lose their wager due to the imbalance.

    3. Reputational Damage: The protocol risks losing credibility due to unfair game mechanics.

Tools Used

  1. Manual Review of Code

  2. Logical and Statistical Analysis of Gameplay Rules

Recommendations

Fix 1: Introduce Probabilistic Bust Behavior for Dealer

Allow the dealer to draw beyond 21 under certain conditions. For example:

  • Increase randomness in the standThreshold or reduce its lower bound (e.g., 15–21 instead of 17–21).

Fix 2: Dynamically Adjust the Dealer's Risk Profile

Modify the dealer's behavior to more closely mimic real blackjack gameplay:

  • Allow the dealer to draw cards without an explicit threshold but instead use a probabilistic stopping condition based on the player's hand.

  • Example: The dealer should stop drawing if the risk of busting exceeds a certain probability (e.g., 50%).

Fix 3: Balance Gameplay for Fairness

Introduce a compensation mechanism for the player to offset the dealer's reduced risk:

  • Provide players with additional options (e.g., doubling down, splitting cards, or receiving bonuses).

  • Increase the player's reward if the dealer wins with a hand close to 21.

Code Adjustment

Here is a simplified suggestion for increasing dealer fairness:

while (dealersHand(msg.sender) < standThreshold || (dealersHand(msg.sender) <= 21 && randomBustChance())) {
uint256 newCard = drawCard(msg.sender);
addCardForDealer(msg.sender, newCard);
}
// Introduce a random bust chance for the dealer
function randomBustChance() internal view returns (bool) {
uint256 chance = uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % 100;
return chance < 20; // 20% chance for the dealer to continue drawing even if close to busting
}
Updates

Lead Judging Commences

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

Support

FAQs

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