OrderBook

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

H-03 `buyOrder` Function Rounding Issue for Fees


Root Cause + Impact

Description

The buyOrder function calculates the fee using integer division (protocolFee = (order.priceInUSDC * FEE) / PRECISION), which may cause fees to be rounded to zero for small orders. This creates an economic loophole where small orders can completely bypass the fee mechanism.

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:

  • Reason 1: The seller incorrectly sets priceInUSDC to a very small value (below 100 wei).

Impact:

  • Impact 1: SellOrders with a USDC price below 100 wei will not incur any fees, impacting the protocol's fee revenue.

Proof of Concept

function buyOrder(uint256 _orderId) public {
...
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
+ console2.log("protocolFee", protocolFee);
uint256 sellerReceives = order.priceInUSDC - protocolFee;
+ console2.log("sellerReceives", sellerReceives);
...
totalFees += protocolFee;
+ console2.log("totalFees", totalFees);
...
}
function test_protocolFee() public {
// NOTE: SellOrder
vm.startPrank(alice);
wbtc.approve(address(book), 20e8);
// INFO: Set USDC = 1 wei
uint256 aliceId = book.createSellOrder(address(wbtc), 20e8, 1, 2 days);
vm.stopPrank();
// NOTE: BuyOrder
vm.startPrank(dan);
usdc.approve(address(book), 200_000e6);
// INFO: protocolFee = 0
book.buyOrder(aliceId); // dan buys from alice
vm.stopPrank();
}

Output

[PASS] test_protocolFee() (gas: 292174)
Logs:
protocolFee 0
sellerReceives 1
BuyOrder totalFees: 0

Recommended Mitigation

Implement a minimum fee requirement for all SellOrders.

Updates

Lead Judging Commences

yeahchibyke Lead Judge
6 months ago
yeahchibyke Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

0x996 Submitter
6 months ago
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!