OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
Submission Details
Impact: high
Likelihood: medium
Invalid

H-03 `buyOrder` Function Rounding Issue for Fees

Author Revealed upon completion

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
5 days ago
yeahchibyke Lead Judge 1 day ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

Hawk 1 Submitter
about 11 hours ago

Support

FAQs

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