Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Fee Calculation Precision Issue

Summary

A potential vulnerability exists in the Rock-Paper-Scissors smart contract's fee calculation mechanism where fees are calculated using percentage integers (10%) instead of basis points. This approach lacks precision and can lead to rounding errors, especially with small bet amounts. When users participate in ETH-based games, the protocol fee is calculated as (totalPot * PROTOCOL_FEE_PERCENT) / 100, which can result in inconsistent fee collection, potentially impacting the financial integrity of the system.

Vulnerability Details

The issue involves two contract functions that handle fee calculations:

  • In the finishGame function, fees are calculated using whole percentages:

// Calculate total pot and fee
uint256 totalPot = game.bet * 2;
uint256 fee = (totalPot * PROTOCOL_FEE_PERCENT) / 100;
prize = totalPot - fee;
// Accumulate fees for admin to withdraw later
accumulatedFees += fee;
emit FeeCollected(_gameId, fee);
  • Similarly, in the handleTie function, the same percentage-based approach is used:

// Calculate protocol fee (10% of total pot)
uint256 totalPot = game.bet * 2;
uint256 fee = (totalPot * PROTOCOL_FEE_PERCENT) / 100;
uint256 refundPerPlayer = (totalPot - fee) / 2;
// Accumulate fees for admin
accumulatedFees += fee;
emit FeeCollected(_gameId, fee);
  • The protocol fee percentage is defined as a constant:

// Protocol fee percentage (10%)
uint256 public constant PROTOCOL_FEE_PERCENT = 10;

Impact

  1. Limited precision, as fees can only be set in whole percentage points

  2. Small rounding errors accumulate over time, creating discrepancies between expected and actual fee collection.

  3. Difficulty in making fine-grained fee adjustments (e.g., 10.5%)

  4. Inconsistent fee collection across different transaction sizes

Tools Used

Manual code review

Recommendations

Replace percentage-based calculations with basis points (BPS) where 10000 BPS = 100%:

// Protocol fee in basis points (10% = 1000 BPS)
uint256 public constant PROTOCOL_FEE_BPS = 1000;
// In finishGame function:
uint256 fee = (totalPot * PROTOCOL_FEE_BPS) / 10000;
// In handleTie function:
uint256 fee = (totalPot * PROTOCOL_FEE_BPS) / 10000;
Updates

Appeal created

m3dython Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Rounding Error

The tie-handling logic loses one wei due to integer division

m3dython Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Rounding Error

The tie-handling logic loses one wei due to integer division

Support

FAQs

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