OrderBook

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

Expired Order Handling

Expired orders persist in storage and continue to incur gas costs because the contract lacks an automatic cleanup mechanism


Description

  • The contract is designed to prevent execution of expired orders by checking block.timestamp >= order.deadlineTimestamp during buy operations, ensuring expired orders cannot be filled.

  • Expired orders remain permanently stored in the contract's state, accumulating over time and increasing gas costs for operations that iterate through or query order data, while providing no functional value to the protocol.

function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
// @> Contract correctly prevents buying expired orders
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired();
// ... rest of function
}
// @> No mechanism exists to remove expired orders from storage
mapping(uint256 => Order) public orders; // @> Expired orders accumulate here indefinitely
// @> Users can manually cancel expired orders, but this requires gas and initiative
function cancelSellOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
if (order.seller == address(0)) revert OrderNotFound();
if (order.seller != msg.sender) revert NotOrderSeller();
if (!order.isActive) revert OrderAlreadyInactive();
order.isActive = false;
// @> Manual cleanup only - no automatic expiration handling
IERC20(order.tokenToSell).safeTransfer(order.seller, order.amountToSell);
}

Risk

Likelihood:

  • As users create sell orders with varying deadline durations, many naturally expire due to shifting market dynamics or user inaction.

  • As the protocol scales and handles thousands of orders daily, a substantial fraction remains unfilled and uncanceled.

  • Over time, this leads to linear growth in storage costs, driven by the accumulation of expired, inactive order data in contract state.

Impact:

  • As the number of expired orders accumulates, gas costs for view functions and order queries grow proportionally, impacting contract efficiency.

  • The resulting storage bloat degrades the user experience by making order browsing and interaction slower and less responsive.

  • Additionally, sellers’ tokens remain locked in these expired orders until manually canceled, leading to diminished capital efficiency across the protocol.


Proof of Concept

// Scenario 1: Storage bloat over time
// 1. Day 1: 100 orders created, 20 expire
// 2. Day 30: 3,000 orders created, 600 expired orders accumulated
// 3. Day 365: 109,500 orders created, 21,900 expired orders in storage
// 4. getOrderDetailsString() and similar functions become expensive to call
// Scenario 2: Seller token lockup
// 1. Bob creates sell order for 5 ETH with 1-day deadline
// 2. Order expires without being filled
// 3. Bob's 5 ETH remains locked in contract until he manually cancels
// 4. Bob may forget or be unaware, keeping capital unnecessarily locked

Recommended Mitigation

Implement an automatic cleanup mechanism or incentivize users to clean up expired orders.

Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Expired orders can cause backlog

By design only `seller` can call `cancelSellOrder()` on their `order`. But when an `order` expires, and the `seller` doesn't have access to the protocol, the expired `order `should be be able to be cancelled by an `admin`.

Support

FAQs

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