Mystery Box

First Flight #25
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

Potential Contract Balance Depletion Due to Expected Payouts Exceeding Income

Summary

The MysteryBox contract is vulnerable to balance depletion because the expected payouts from rewards may exceed the income generated from box sales. Although the expected value of rewards per box (0.05 ether) is less than the box price (0.1 ether), factors like variance in reward distribution, unrestricted owner withdrawals, simultaneous claims of high-value rewards, and insecure randomness can lead to the contract's Ether balance becoming insufficient to cover owed rewards. This situation can result in users being unable to claim their rewards, causing financial loss and eroding trust in the platform.

Box Price: 0.1 ether

Rewards Probabilities and Values:

  • Coal:

    • Probability: 75% (0.75)

    • Value: 0 ether

  • Bronze Coin:

    • Probability: 20% (0.20)

    • Value: 0.1 ether

  • Silver Coin:

    • Probability: 4% (0.04)

    • Value: 0.5 ether

  • Gold Coin:

    • Probability: 1% (0.01)

    • Value: 1 ether

Calculating Expected Value (EV) per Box:

The expected value is calculated as the sum of the products of each reward's probability and its value:

  • Coal: 0.75×0=0 ether0.75 \times 0 = 0\ \text{ether}0.75×0=0 ether

  • Bronze Coin: 0.20×0.1=0.02 ether0.20 \times 0.1 = 0.02\ \text{ether}0.20×0.1=0.02 ether

  • Silver Coin: 0.04×0.5=0.02 ether0.04 \times 0.5 = 0.02\ \text{ether}0.04×0.5=0.02 ether

  • Gold Coin: 0.01×1=0.01 ether0.01 \times 1 = 0.01\ \text{ether}0.01×1=0.01 ether

  • Total EV per Box: 0+0.02+0.02+0.01=0.05 ether0 + 0.02 + 0.02 + 0.01 = 0.05\ \text{ether}0+0.02+0.02+0.01=0.05 ether

Comparison with Box Price:

  • Box Price: 0.1 ether

  • Expected Payout per Box: 0.05 ether

The expected payout per box (0.05 ether) is less than the box price (0.1 ether), suggesting profitability under normal circumstances.

Vulnerability Details

Despite the positive expected value margin, the contract is still vulnerable to balance depletion due to the following factors:

  • Variance in Outcomes: Actual results can deviate from expected probabilities in the short term, leading to more high-value rewards being won than statistically anticipated.

  • Owner Withdrawals: The owner can withdraw funds without restrictions, potentially draining the contract's balance needed to pay out rewards.

  • Accumulation of High-Value Rewards: Users might accumulate and claim high-value rewards simultaneously, exceeding the available balance.

  • Insecure Randomness: If the random number generation is insecure, it could be manipulated to win high-value rewards more frequently.

Impact

  • Failed Reward Claims: Users may be unable to claim their rewards due to insufficient contract balance.

  • Financial Loss: Users could suffer losses if the contract cannot fulfill its payout obligations.

  • Loss of Trust: Confidence in the platform may erode, affecting user retention and reputation.

  • Legal Risks: The owner might face legal consequences for failing to deliver promised rewards.

Tools Used

  • Manual Code Review: Examined the contract's code to identify improper array manipulation practices.

Recommendations

  1. Adjust Reward Economics:

    • Recalculate reward probabilities and values to ensure the expected payout is significantly less than the box price.

    • Example adjustments:

      • Reduce the probability or value of high-value rewards.

      • Increase the box price if necessary.

  2. Implement a Reserve Mechanism:

    • Introduce a reserveBalance to track and maintain sufficient funds for outstanding rewards.

    • Update the reserve when rewards are assigned and claimed.

    • Modify withdrawFunds to prevent the owner from depleting necessary reserves:

      uint256 public reserveBalance;
      function withdrawFunds(uint256 _amount) public {
      require(msg.sender == owner, "Only owner can withdraw");
      require(address(this).balance - _amount >= reserveBalance, "Insufficient reserve for payouts");
      (bool success,) = payable(owner).call{value: _amount}("");
      require(success, "Transfer failed");
      }
      function openBox() public {
      // Existing logic to assign rewards
      // Update reserveBalance when a reward with value is added
      reserveBalance += rewardValue;
      }
      function claimReward(uint256 _index) public {
      // Existing logic to claim reward
      // Decrease reserveBalance when a reward is claimed
      reserveBalance -= rewardValue;
      }
Updates

Appeal created

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Protocol should have a higher initial balance to prevent prize withdrawing problems

Support

FAQs

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

Give us feedback!