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 days 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.