OrderBook

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

Order Fill Without Deadline Enforcement Allows Purchase After Expiry

Root + Impact

The fillOrder() function lacks a deadline check, allowing buyers to fill orders even after they’ve expired. This breaks expected order behavior, bypasses seller control, and can lead to unauthorized token transfers after the intended expiration time.

Root: Missing or incorrect deadline check in the fillOrder function.

Impact: Sellers are forced to sell tokens at outdated, unfavorable prices, losing control over their assets and potentially incurring financial losses, while also damaging the platform's credibility.

Description

  • Describe the normal behavior in one or more sentences

  • Ans:

    Under normal behavior, an order should only be fillable before its specified deadline. Once the deadline passes, the order should be considered expired and unfillable by any buyer.

  • Explain the specific issue or problem in one or more sentences

  • ans:

    The fillOrder() function does not enforce the order's deadline, allowing buyers to fill orders even after they have expired. This violates the intended time constraint and undermines seller control over their listings.

// Root cause in the codebase with @> marks to highlight the relevant section
function fillOrder(uint256 orderId) external {
Order storage order = orders[orderId];
// @> Missing deadline check
// Example fix:
// require(block.timestamp <= order.deadline, "Order expired");
require(order.status == Status.Active, "Order is not active");
uint256 price = order.price;
// Transfer USDC from buyer to seller
usdc.safeTransferFrom(msg.sender, order.seller, price);
// Transfer the listed token from contract to buyer
IERC20(order.token).safeTransfer(msg.sender, order.amount);
order.status = Status.Filled;
}

Risk

Likelihood:

  1. This will occur any time a buyer calls fillOrder() on an order that has already passed its deadline.

  2. Since the contract doesn’t prevent expired orders from being filled, any buyer can intentionally exploit this if they know the order ID.

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

    This will occur whenever a buyer calls fillOrder() on an order whose deadline has already passed, because the function does not validate the current block timestamp against the order’s expiration time.

  • Reason 2

    The contract treats all active orders equally, regardless of whether they are past their deadline, allowing expired orders to remain fillable as long as their status is still marked as Active.

Impact:

  • Impact 1

    Attackers or opportunistic buyers can exploit stale listings to acquire tokens unfairly, leading to reduced trust in the protocol and financial loss.

  • Impact 2

    Sellers may have their tokens purchased after the intended expiration, violating their control and expectations.

Proof of Concept

  1. The buyer calls fillOrder(orderId) after the deadline, and the order is still processed successfully.

  2. A seller creates an order with a short deadline (e.g., 8,9,10 minutes from now)

  3. The deadline passes without any buyer filling it.

// Seller creates an order
orderBook.createOrder(wETH, amount, price, block.timestamp + 600); // expires in 10 min
// Wait 10+ minutes...
// Buyer fills it after expiration
orderBook.fillOrder(orderId); // ✅ still succeeds — vulnerable

Recommended Mitigation

Add a check in the fillOrder() function to ensure that the order cannot be filled after its deadline.

- remove this code
+ add this code
function fillOrder(uint256 orderId) external {
Order storage order = orders[orderId];
+ require(block.timestamp <= order.deadline, "Order expired");
require(order.status == Status.Active, "Order is not active");
uint256 price = order.price;
usdc.safeTransferFrom(msg.sender, order.seller, price);
IERC20(order.token).safeTransfer(msg.sender, order.amount);
order.status = Status.Filled;
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
4 months ago
yeahchibyke Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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