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.
solidity
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();
}
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);
wsol.mint(attacker, 1000);
wsol.approve(address(book), type(uint256).max);
for (uint256 i = 0; i < 1000; i++) {
book.createSellOrder(
address(wsol),
1,
1,
1 days
);
}
vm.stopPrank();
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);
}
[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
}