OrderBook

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

No Minimum Token Amount on Creation May Lead to Denial-of-Service

No Minimum Token Amount on Creation May Lead to Denial-of-Service

Description

  • While a user cannot create a sell order with a zero amount, the contract does not enforce a minimum amount.

  • This allows a malicious user to create a very large number of sell orders with an insignificant amount (e.g., 1 wei), which bloats the contract's storage and can lead to a denial-of-service condition.

function createSellOrder(
address _tokenToSell,
uint256 _amountToSell,
uint256 _priceInUSDC,
uint256 _deadlineDuration
) public returns (uint256) {
if (!allowedSellToken[_tokenToSell]) revert InvalidToken();
@> if (_amountToSell == 0) revert InvalidAmount(); // only limits non-zero values
if (_priceInUSDC == 0) revert InvalidPrice();
if (_deadlineDuration == 0 || _deadlineDuration > MAX_DEADLINE_DURATION) revert InvalidDeadline();
uint256 deadlineTimestamp = block.timestamp + _deadlineDuration;
uint256 orderId = _nextOrderId++;
IERC20(_tokenToSell).safeTransferFrom(msg.sender, address(this), _amountToSell);
// Store the order
orders[orderId] = Order({
id: orderId,
seller: msg.sender,
tokenToSell: _tokenToSell,
amountToSell: _amountToSell,
priceInUSDC: _priceInUSDC,
deadlineTimestamp: deadlineTimestamp,
isActive: true
});
emit OrderCreated(orderId, msg.sender, _tokenToSell, _amountToSell, _priceInUSDC, deadlineTimestamp);
return orderId;
}

Risk

Likelihood: High

  • A malicious actor can easily automate the creation of thousands of near-worthless orders in a script.

  • The cost of this attack is very low, as each transaction only requires a minimal token amount and gas.

Impact: Medium

  • The contract's storage becomes bloated with economically insignificant orders, increasing the state size on the blockchain.

  • This can degrade the user experience by making it more difficult to parse through legitimate orders and may cause transactions that create many orders to run out of gas, as shown in the PoC.

Proof of Concept

The following test demonstrates how a user can create a large number of orders with a minimal amount (1 wei), eventually causing the transaction to fail due to running out of gas. This highlights the potential for storage bloat and DoS.

function test_createLotsOrders() public {
vm.startPrank(bob);
weth.approve(address(book), type(uint256).max);
for (uint256 i = 0; i < 10000; i++) {
book.createSellOrder(address(weth), 1, 100_000, 1 days);
}
vm.stopPrank();
}

The following trace shows the transaction failing with an OutOfGas error, proving that this vector can be used to disrupt the normal operation of the contract.

├─ [74048] OrderBook::createSellOrder(MockWETH: [0xF62849F9A0B5Bf2913b396098F7c7019b51A820a], 1, 100000 [1e5], 86400 [8.64e4])
│ ├─ [4466] MockWETH::transferFrom(will_sell_weth_order: [0x5836Fb2f9DE86916F726F675AA83Ced224C0E7B3], OrderBook: [0xd0398c02CB8A3Ea728E6e32EBC6030E8B3cCcA59], 1)
│ │ ├─ emit Transfer(from: will_sell_weth_order: [0x5836Fb2f9DE86916F726F675AA83Ced224C0E7B3], to: OrderBook: [0xd0398c02CB8A3Ea728E6e32EBC6030E8B3cCcA59], value: 1)
│ │ └─ ← [Return] true
│ └─ ← [OutOfGas] EvmError: OutOfGas
└─ ← [Revert] EvmError: Revert

Recommended Mitigation

Introduce a minimum sell amount to prevent users from creating economically insignificant orders. This should be enforced in both the createSellOrder and amendSellOrder functions.

// In OrderBook.sol
// --- Constants ---
+ uint256 public constant MIN_SELL_AMOUNT = 1e4; // Example value, adjust based on token decimals and desired minimum 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 (_amountToSell < MIN_SELL_AMOUNT) revert InvalidAmount();
if (_priceInUSDC < 34) revert InvalidPrice();
if (_deadlineDuration == 0 || _deadlineDuration > MAX_DEADLINE_DURATION) revert InvalidDeadline();
// ...
}
function amendSellOrder(
uint256 _orderId,
uint256 _newAmountToSell,
uint256 _newPriceInUSDC,
uint256 _newDeadlineDuration
) public {
// ...
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired(); // Cannot amend expired order
- if (_newAmountToSell == 0) revert InvalidAmount();
+ if (_newAmountToSell < MIN_SELL_AMOUNT) revert InvalidAmount();
if (_newPriceInUSDC < 34) revert InvalidPrice();
if (_newDeadlineDuration == 0 || _newDeadlineDuration > MAX_DEADLINE_DURATION) revert InvalidDeadline();
// ...
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
13 days ago
yeahchibyke Lead Judge 9 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.