OrderBook

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

Failure to Invalidate Expired Orders Could Lead to Resource Drain and Abuse

Root + Impact

Orders that pass their deadlineTimestamp still show isActive = true, even though they can’t be bought. This leads to misleading UX and unnecessary state bloat.

Impact:

  • Users may see expired orders as active

  • Increases contract storage usage

  • Could be abused to spam expired listings

Description

  • Normal Behavior:
    When an order reaches its deadlineTimestamp, it should no longer be considered active or available for purchase. Ideally, expired orders should be automatically marked as inactive or cleaned up to reflect their status accurately and prevent user confusion or contract bloat.

  • Specific Issue:
    Expired orders remain in an active state even after their deadlineTimestamp has passed. Although the buyOrder function correctly prevents purchasing these orders, the order's isActive flag remains true, leading to inconsistent state representation and requiring manual cancellation to reflect the correct status.

function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
// Validation checks
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired(); // <-- This blocks expired orders
order.isActive = false;
...
}

Risk

Likelihood:

  • This issue will occur whenever a seller creates a sell order and it reaches its deadlineTimestamp without being filled. In this case, the order remains marked as isActive = true, even though it can no longer be executed.

  • The contract does not include any automatic expiration logic, and functions like getOrder or getOrderDetailsString do not adjust the status unless cancellation is performed manually. This makes the stale order appear active to frontends, indexers, or external integrations.

Impact:

  • Stale orders remain active, misleading buyers and UIs into thinking they are valid and fillable, potentially causing failed transactions and poor user experience.

  • Frontends and third-party integrators may rely on the isActive flag alone, leading to inaccurate display of order statuses, broken logic in dApps, or wasted gas fees.

Proof of Concept

Patrick creates a sell order with a short deadline (e.g., 1 minute).

Wait until the deadline passes

Call getOrder(orderId) and observe:


  1. order.isActive == true

    order.deadlineTimestamp < block.timestamp

    Attempt to call buyOrder(orderId) — it reverts with OrderExpired.

    This shows the order is still marked active on-chain, despite being unfillable.

No special setup or exploits are needed this behavior occurs naturally after deadline expiry.

function testExpiredOrderStillActive() public {
// Create a sell order with a short deadline
uint256 amount = 1e18;
uint256 price = 100e6;
uint256 shortDeadline = 1; // 1 second
// Mint and approve token for seller
mockToken.mint(address(this), amount);
mockToken.approve(address(orderBook), amount);
uint256 orderId = orderBook.createSellOrder(
address(mockToken), amount, price, shortDeadline
);
// Fast forward time beyond deadline
skip(2); // skips 2 seconds
// Fetch the order and check status
OrderBook.Order memory order = orderBook.getOrder(orderId);
assertTrue(order.isActive, "Order should still be marked as active");
assertLt(order.deadlineTimestamp, block.timestamp);
// Try to buy (should revert)
usdc.mint(address(this), price);
usdc.approve(address(orderBook), price);
vm.expectRevert(OrderBook.OrderExpired.selector);
orderBook.buyOrder(orderId);
}

Recommended Mitigation

To ensure expired orders cannot be considered "active", update the getOrderDetailsString function so it correctly reflects expired orders.

- remove this code
string memory status = order.isActive
? (block.timestamp < order.deadlineTimestamp ? "Active" : "Expired (Active but past deadline)")
: "Inactive (Filled/Cancelled)";
if (order.isActive && block.timestamp >= order.deadlineTimestamp) {
status = "Expired (Awaiting Cancellation)";
} else if (!order.isActive) {
status = "Inactive (Filled/Cancelled)";
} else {
status = "Active";
}
+ add this code
string memory status;
if (!order.isActive) {
status = "Inactive (Filled/Cancelled)";
} else if (block.timestamp >= order.deadlineTimestamp) {
status = "Expired (Awaiting Cancellation)";
} else {
status = "Active";
}
Updates

Lead Judging Commences

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

Expired orders still show as active

The `buyOrder()` function checks if an order is expired but fails to update the `isActive` flag when reverting, causing expired orders to remain marked as active in storage.

Support

FAQs

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