TwentyOne

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

Player Funds Mismanagement

Summary

The contract currently faces an issue with player funds mismanagement, which could occur when the contract balance is insufficient to cover prize payouts at the end of the game. Specifically, if a player wins and the contract does not have enough funds to transfer the prize, the contract may revert, leaving the player without the expected reward. This vulnerability arises because there is no check or alternative flow to handle insufficient funds for payouts in the endGame() function.

Vulnerability Details

Insufficient Funds Check Missing: The contract does not check if it has enough balance to pay out the player’s prize (2 ether) when the game ends.

  • Potential for Unmet Expectations: When a player wins, they expect to receive the prize, but if the contract balance is insufficient, the contract will revert and no funds will be transferred.

  • No Fallback Mechanism: There is no mechanism to handle the situation where the contract balance is insufficient for payouts, such as storing pending payouts or notifying players of the insufficient balance.

Impact

Player Experience: Players may end a game expecting a payout, but due to insufficient funds in the contract, they will not receive the prize, and the transaction will fail.

  • Trust Issues: Players may lose trust in the contract, as it could appear that the game system is unreliable or faulty due to the unexpected lack of payouts.

  • Reverts and Inconsistent State: When the contract balance is insufficient, the transaction reverts entirely, including state changes like card drawing, making the contract behavior unpredictable and potentially confusing.

  • Financial Risk: The contract owner may face financial and reputational risks if players are not able to claim their prizes, leading to negative feedback or dispute

Tools Used

Manual Review

Recommendations

To address this issue, the following changes should be made to the startGame() function:

Proposed Solution

Define Constants for Payout and Fees: Use constants to avoid magic numbers and ensure consistency across the contract. The prize payout for each player should be defined as a constant. The fees (e.g., gas fees) should also be calculated in advance to avoid underestimating the necessary funds.

uint256 constant PAYOUT_PRIZE = 2 ether; // Prize payout per player
uint256 constant FEE_PER_PLAYER = 0.01 ether; // Estimated fee per player

Balance Check: Before allowing a player to start a game, check if the contract has sufficient funds to cover the prize payouts for all active players as well as the fees. If the balance is insufficient, revert the transaction with an appropriate error message.

function startGame() public payable returns (uint256) {
address player = msg.sender;
uint256 currentPlayerCount = getActivePlayerCount(); // Function to retrieve current active players
uint256 totalRequiredBalance = (currentPlayerCount * PAYOUT_PRIZE) + (currentPlayerCount * FEE_PER_PLAYER);
// Check if contract balance is sufficient for payouts and fees
require(address(this).balance >= totalRequiredBalance, "Not enough funds for the amount of players or transaction fees");
// Continue with game initialization if balance is sufficient
initializeDeck(player);
uint256 card1 = drawCard(player);
uint256 card2 = drawCard(player);
addCardForPlayer(player, card1);
addCardForPlayer(player, card2);
return playersHand(player);
}

Either this, or you implement a 1 player only system.

With a global variable being true or false becoming true upong startGame() function being called, and false upon endGame being called.

Updates

Lead Judging Commences

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

Insufficient balance for payouts / Lack of Contract Balance Check Before Starting Game

Support

FAQs

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