OrderBook

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

Precision Loss in Fee Calculation Leads to Zero-Fee Trades

Precision Loss in Fee Calculation Leads to Zero-Fee Trades

Description

  • The buyOrder function is intended to collect a 3% fee on the USDC value of every filled order.

  • Due to precision loss from integer division, if an order's priceInUSDC is less than 34 (the smallest integer that yields a non-zero fee), the calculated protocolFee rounds down to zero.

function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
// Validation checks
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired();
order.isActive = false;
@> uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
uint256 sellerReceives = order.priceInUSDC - protocolFee;
iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee);
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
IERC20(order.tokenToSell).safeTransfer(msg.sender, order.amountToSell);
totalFees += protocolFee;
emit OrderFilled(_orderId, msg.sender, order.seller);
}

Risk

Likelihood:

A seller creates an order with a priceInUSDC value of 33 or less, and a buyer subsequently fills it.

Impact:

The protocol earns no fee from these small-value trades, resulting in a minor loss of revenue.

Proof of Concept

Below scenario shows that the zero-fee trade is executed with 33 USDC.

function test_audit_feePrecisionLoss() public {
// clara sells 2e11 sol for 33 usdc
uint256 sellingWSOLAmount = 2e11;
uint256 priceUSDC = 33;
vm.startPrank(clara);
wsol.approve(address(book), sellingWSOLAmount);
uint256 claraId = book.createSellOrder(address(wsol), sellingWSOLAmount, priceUSDC, 2 days);
vm.stopPrank();
vm.startPrank(dan);
usdc.approve(address(book), priceUSDC);
book.buyOrder(claraId); // dan buys wsol
vm.stopPrank();
assertEq(book.totalFees(), 0);
}

Recommended Mitigation

Enforce a minimum priceInUSDC in the createSellOrder and amendSellOrder functions to ensure the calculated fee is always non-zero.

+ uint256 public constant MIN_PRICE_IN_USDC = 34;
- if (_priceInUSDC == 0) revert InvalidPrice();
+ if (_priceInUSDC < MIN_PRICE_IN_USDC) revert InvalidPrice();
Updates

Lead Judging Commences

yeahchibyke Lead Judge 6 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!