OrderBook

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

Loss of protocol fee due to integer truncation when priceInUSDC is low

Root + Impact

Description

  • When a user buys an order, the protocol is expected to take a 3% fee based on priceInUSDC. This fee is deducted from the buyer's payment and added to totalFees.

  • The fee is calculated using integer division:

    uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;

    This formula rounds down any value below 1 to 0 . Thus, if priceInUSDC < 34, protocolFee will be zero, and no fee is collected.

    This allows users to bypass fees entirely by setting very low-priced orders, leading to lost protocol revenue and inconsistent behavior.

uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;

Risk

Likelihood: Medium

  • Common in low-value trades or intentionally used by attackers but not much likely becuse of the limited potential of gain

Impact: Low

  • Fee is entirely skipped however Impact is low because only very small orders can executed without fees.

  • However, an attacker can dissect a large order into multiple micro-orders to avoid protocol fees. The feasibility and the final impact would depend on gas fees.

  • Protocol earns zero revenue from such trades.

Proof of Concept

This PoC demonstrates that when an order is created with a priceInUSDC below 33, the protocol fee calculation results in zero due to integer truncation. As a result, no fee is collected when the order is filled, allowing users to bypass fees entirely on low-value trades. The test verifies this by asserting that the totalFees remain unchanged before and after the purchase.

function test_buy_Without_fees() public {
vm.startPrank(alice);
wbtc.approve(address(book), 2e8);
uint256 aliceId = book.createSellOrder(address(wbtc), 2e8, 33, 2 days);
vm.stopPrank();
uint _tempTotalFees=book.totalFees();
vm.startPrank(dan);
usdc.approve(address(book), 200_000e6);
book.buyOrder(aliceId);
vm.stopPrank();
assertEq(_tempTotalFees, book.totalFees());
}

Recommended Mitigation

To prevent fee bypass via small priceInUSDC values, the contract can check if fee is nonzero inside createSellOrders and amendOrders. This ensures that the 3% fee calculation always results in a non-zero value.Since FEE is constant we can also define a constant variable 34.

// createSellOrder()
+ if (_priceInUSDC < 34) revert InvalidPrice(); // enforce minimum price
// amendOrders
+ if (_newpPrceInUSDC < 34) revert InvalidPrice(); // enforce minimum price
Updates

Lead Judging Commences

yeahchibyke Lead Judge
5 months ago
yeahchibyke Lead Judge 5 months 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.

Give us feedback!