OrderBook

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

Fee Calculation Precision Loss

Root + Impact - The fee calculation uses integer division which results in zero fees for orders with priceInUSDC less than 34, causing protocol revenue loss and enabling fee avoidance.

Description

  • The protocol is designed to charge a 3% fee on each transaction for revenue generation.

  • The fee calculation (order.priceInUSDC * FEE) / PRECISION uses integer division where FEE = 3 and PRECISION = 100, causing any order with priceInUSDC < 34 to result in zero fees.

// Root cause in the codebase
function buyOrder(uint256 _orderId) public {
// ... validation checks ...
order.isActive = false;
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION; // @> Integer division: (33 * 3) / 100 = 99 / 100 = 0
uint256 sellerReceives = order.priceInUSDC - protocolFee;
iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee); // @> Transfers 0 fees to protocol
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
// ... rest of function
}

Risk

Likelihood:

  • Users naturally experiment with small orders to test the system

  • Small-value trades (under $34) are common in DeFi for testing

Impact:

  • Protocol loses expected fee revenue on small orders

  • Creates economic incentive to fragment large orders into smaller ones

Proof of Concept - The following scenario demonstrates how the integer division vulnerability leads to complete fee loss:

// Test scenario demonstrating fee loss:
// 1. Create order with priceInUSDC = 33
// 2. protocolFee = (33 * 3) / 100 = 99 / 100 = 0 (integer division)
// 3. sellerReceives = 33 - 0 = 33
// 4. Protocol receives 0 USDC instead of expected 0.99 USDC
// 5. Buyer pays 33 USDC, seller receives 33 USDC, protocol gets nothing
contract TestFeeLoss {
function demonstratePrecisionLoss() public pure returns (uint256) {
uint256 priceInUSDC = 33;
uint256 FEE = 3;
uint256 PRECISION = 100;
uint256 protocolFee = (priceInUSDC * FEE) / PRECISION;
return protocolFee; // Returns 0 instead of expected 0.99
}
}

Recommended Mitigation - The most effective solution is to implement a minimum order value that ensures non-zero fees:

+ uint256 public constant MIN_ORDER_VALUE = 34; // Minimum to ensure non-zero fees
+ error OrderValueTooSmall();
function createSellOrder(
address _tokenToSell,
uint256 _amountToSell,
uint256 _priceInUSDC,
uint256 _deadlineDuration
) public returns (uint256) {
if (!allowedSellToken[_tokenToSell]) revert InvalidToken();
if (_amountToSell == 0) revert InvalidAmount();
if (_priceInUSDC == 0) revert InvalidPrice();
+ if (_priceInUSDC < MIN_ORDER_VALUE) revert OrderValueTooSmall();
if (_deadlineDuration == 0 || _deadlineDuration > MAX_DEADLINE_DURATION) revert InvalidDeadline();
// ... rest of function
}
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!