OrderBook

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

Precision Loss / Rounding Issues in Fee Calculation

Description


The contract calculates protocol fees using integer division ((order.priceInUSDC * FEE) / PRECISION). This simple integer division truncates any remainder, always rounding down. For certain priceInUSDC values, especially small ones, this can lead to the protocol earning slightly less fees than intended (potentially rounded down to 0 for very small amounts) or minor discrepancies.


Risk


Root Cause: Integer division in Solidity inherently truncates decimal values, always rounding down.

Solidity

function buyOrder(uint256 _orderId) public {
// ...
// @> uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
// This calculation rounds down, potentially losing fractional fees.
// ...
}

Likelihood: High. This occurs whenever the product of order.priceInUSDC * FEE is not perfectly divisible by PRECISION.

Impact: Low. It results in minor revenue loss for the protocol and slight inaccuracies. This is an economic or precision concern, not a security exploit leading to fund loss or contract manipulation.


Proof of Concept


Assume iUSDC has 6 decimal places (e.g., 1 USDC = 1,000,000 wei). FEE = 3 (for 3%), PRECISION = 100.

Consider a priceInUSDC = 10 USDC-wei (which is 0.00001 USDC).

  1. Expected fee: 10 * 0.03 = 0.3 USDC-wei.

  2. Contract calculation: protocolFee = (10 * 3) / 100 = 30 / 100 = 0 (due to integer truncation).

  3. Result: The protocol collects 0 fees for this transaction, losing 0.3 USDC-wei. While minuscule for a single transaction, this demonstrates the rounding down behavior and can accumulate over many small trades.


Recommended Mitigation


Use a higher precision factor for calculations, such as basis points (BPS), where 10_000 represents 100%. This allows for more granular fee computations, minimizing rounding errors.

Diff

// --- Constants ---
uint256 public constant MAX_DEADLINE_DURATION = 3 days;
- uint256 public constant FEE = 3; // 3%
- uint256 public constant PRECISION = 100;
+ uint256 public constant FEE_BPS = 300; // 3% represented in basis points (300 out of 10,000)
+ uint256 public constant BPS_DENOMINATOR = 10_000;
// ...
function buyOrder(uint256 _orderId) public {
// ...
- uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
+ uint256 protocolFee = (order.priceInUSDC * FEE_BPS) / BPS_DENOMINATOR;
uint256 sellerReceives = order.priceInUSDC - protocolFee;
// ...
}

Reference Files:

  • src/OrderBook.sol

  • test/TestOrderBook.t.sol

Updates

Lead Judging Commences

yeahchibyke Lead Judge
about 1 month ago
yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Fee can be bypassed

Protocol Suffers Potential Revenue Leakage due to Precision Loss in Fee Calculation

Support

FAQs

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