OrderBook

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

Expired Orders Are Never Cleaned Up, Causing Permanent Storage Bloat

Unbounded Order growth causes bloat

Description

The OrderBook contract allows users to create time-limited sell orders, but once the order passes its deadlineTimestamp, it is never automatically marked inactive or removed from the orders mapping unless the seller explicitly calls cancelOrder(). There is no mechanism to clean up expired but unfilled orders.

orders[orderId] = Order({
id: orderId,
seller: msg.sender,
tokenToSell: _tokenToSell,
amountToSell: _amountToSell,
priceInUSDC: _priceInUSDC,
deadlineTimestamp: deadlineTimestamp,
isActive: true //<@ This value is never set to false if order expires and seller doesn't cancel
});

Missing cleanup logic for expired orders.

The OrderBook contract allows users to create time-limited sell orders, but once the order passes its deadlineTimestamp, it is never automatically marked inactive or removed from the orders mapping unless the seller explicitly calls cancelOrder(). There is no mechanism to clean up expired but unfilled orders.

Risk

Likelihood:

  • There is no automatic cleanup for expired orders

  • Sellers have no incentive to cancel their expired listings

  • Users can forget or abandon their orders

  • Frontend interfaces may not warn about expired-but-active orders

  • Over time, any live deployment will accumulate stale data unless actively purged

Impact:

  • Orders are tracked using a monotonically increasing _nextOrderId counter.

  • All created orders (expired, cancelled, filled) remain in the orders mapping permanently.

  • Expired but unfilled orders retain the isActive = true status, even when they’re no longer fulfillable.

  • There is no off-chain incentive or on-chain mechanism to clean them up.

  • In the long term, orderId inflation and stale storage negatively impact:Proof of Concept

PoC:

Copy this and paste in the test file it shows that expired orders just accumulate in the contract

function test_ExpiredOrdersAccumulateWithoutCleanup() public {
// Seller creates 3 orders with 1-second deadlines
for (uint256 i = 0; i < 3; ++i) {
vm.startPrank(seller);
token.approve(address(book), 1e18);
book.createSellOrder(address(token), 1e18, 100e6, 1); // expires in 1 second
vm.stopPrank();
}
// Fast forward time to expire all orders
vm.warp(block.timestamp + 2);
// All orders are expired, but still marked as active
for (uint256 i = 1; i <= 3; ++i) {
(,,,,, bool isActive) = book.orders(i);
assertTrue(isActive); //<@ Orders are expired but still active
}
}

Recommended Mitigation

check for expired orders and set them to false

+ function purgeExpiredOrder(uint256 _orderId) external {
+ Order storage order = orders[_orderId];
+ if (order.isActive && block.timestamp >= order.deadlineTimestamp) {
+ order.isActive = false;
+ emit OrderPurged(_orderId);
+ }
+}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 4 months 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.