OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: low
Likelihood: medium
Invalid

Value Truncation

Truncated Protocol Fee Results in Zero-Fee Collection

Description

The buyOrder function computes a protocol fee using the expression:

uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;

Where:

  • FEE = 3

  • PRECISION = 100

This calculation truncates decimal values due to the way Solidity handles integer division.

Risk

Likelihood: Medium

The truncation occurs whenever priceInUSDC is low enough that (priceInUSDC * FEE) / PRECISION results in a value less than 1. This is very likely to occur in low-value trades.

Impact: Low

Due to value truncation, the protocol fails to collect the intended fee on certain trades. This undermines the protocol’s revenue model, as both buyers and sellers can benefit from fee-free transactions simply by engaging in low-priced orders.

Proof of Concept

Example of sample scenario where value truncation happens:

  1. Bob has 2 WETH, and wish to sell 0.005 ETH for 20 USDC

  2. Dan buys Bob's listing

  3. Due to value truncation, Bob is able to get the entire 20.00 USDC amount in return, bypassing the intended protocol fee

function test_value_truncation() public {
console2.log("Bob WETH balance before selling: ", weth.balanceOf(bob));
console2.log("Dan WETH balance before purchase: ", weth.balanceOf(dan));
console2.log("Total Fees Collected Before: ", book.totalFees());
console2.log("Bob USDC balance before: ", usdc.balanceOf(bob));
// Bob sells
vm.startPrank(bob);
weth.approve(address(book), 5000000000000000);
book.createSellOrder({
_tokenToSell: address(weth),
_amountToSell: 5000000000000000,
_priceInUSDC: 20,
_deadlineDuration: 1 days
});
vm.stopPrank();
// Dan buys
vm.startPrank(dan);
usdc.approve(address(book), 20);
book.buyOrder(1);
vm.stopPrank();
console2.log("\n Bob WETH balance after selling: ", weth.balanceOf(bob));
console2.log("Dan WETH balance after purchase: ", weth.balanceOf(dan));
console2.log("Total Fees Collected After: ", book.totalFees());
console2.log("Bob USDC balance before: ", usdc.balanceOf(bob));
}

the console output as below:

[PASS] test_value_truncation() (gas: 295505)
Logs:
Bob WETH balance before selling: 2000000000000000000
Dan WETH balance before purchase: 0
@> Total Fees Collected Before: 0
@> Bob USDC balance before: 0
Bob WETH balance after selling: 1995000000000000000
Dan WETH balance after purchase: 5000000000000000
@> Total Fees Collected After: 0
@> Bob USDC balance before: 20

Protocol fees are not deducted, despite the trade execution. Fee logic failed for small trades due to integer truncation.

Recommended Mitigation

- uint256 public constant FEE = 3; // 3%
- uint256 public constant PRECISION = 100;
+ uint256 public constant FEE = 3e18; // 3%
+ uint256 public constant PRECISION = 100e18;
Updates

Lead Judging Commences

yeahchibyke Lead Judge
15 days ago
yeahchibyke Lead Judge 11 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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