OrderBook

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

Able to Extend Order Deadline Beyond MAX_DEADLINE_DURATION via Amendments

Root + Impact

Description

  • The maximum deadline a seller should be able to set for their token listing is 3 days from the original order creation time, as per MAX_DEADLINE_DURATION.

  • However, the contract currently only checks that each new deadline duration is within MAX_DEADLINE_DURATION, relative to block.timestamp, not the order's original creation time. This allows sellers to keep an order alive forever by continuously calling amendSellOrder every few days.

// Root cause in the codebase with @> marks to highlight the relevant section
// OrderBook.sol
function amendSellOrder(...) external {
...
if (_newDeadlineDuration == 0 || _newDeadlineDuration > MAX_DEADLINE_DURATION) {
@> revert InvalidDeadline();
}
order.deadlineTimestamp = block.timestamp + _newDeadlineDuration;
}

Risk

Likelihood:

  • This can happen any time a seller decides to prolong their listing.

  • Attackers can keep looping amendments and never let the order expire

Impact:

  • Stale or overpriced orders can persist indefinitely, cluttering the order book.

  • May result in unrealistic order availability for buyers.
    If frontends display active listings based on isActive, they’ll remain visible much longer than intended.

Proof of Concept

This test shows that a seller can bypass the 3-day maximum deadline rule by repeatedly calling amendSellOrder() with a new 3-day deadline. Since each amendment resets the deadline relative to block.timestamp, the order can stay active indefinitely, defeating the intended time limit.

function test_setNewDeadline() external {
vm.startPrank(alice); // Create a sell order
wbtc.approve(address(book), 2e8);
uint256 aliceId = book.createSellOrder(address(wbtc), 2e8, 180_000e6, 2 days);
vm.stopPrank();
vm.warp(2 days); // Wait until order is about to expire
vm.prank(alice);
book.amendSellOrder(aliceId, 1.75e8, 175_000e6, 3 days); // Extend
vm.warp(4 days);
vm.prank(alice);
book.amendSellOrder(aliceId, 1.75e8, 175_000e6, 3 days); // Extend again
vm.warp(6 days);
vm.prank(alice);
book.amendSellOrder(aliceId, 1.75e8, 175_000e6, 3 days); // Extend again
string memory details = book.getOrderDetailsString(aliceId);
console2.log(details);
// Deadline ends up at ~9 days from original creation, bypassing the 3-day rule
// just like this sellers can extend the deadline as long as they want
}

Recommended Mitigation

This mitigation fixes the bug where sellers could extend their order deadline indefinitely by repeatedly calling amendSellOrder(). It adds a createdAt timestamp to each order and ensures that any new deadline never exceeds createdAt + MAX_DEADLINE_DURATION. This enforces a true upper limit on how long an order can stay active, as originally intended.

- remove this code
+ add this code
struct Order {
uint256 id;
address seller;
address tokenToSell; // Address of wETH, wBTC, or wSOL
uint256 amountToSell; // Amount of tokenToSell
uint256 priceInUSDC; // Total USDC price for the entire amountToSell
uint256 deadlineTimestamp; // Block timestamp after which the order expires
+ uint256 createdAt; // Timestamp when the order was created
bool isActive; // Flag indicating if the order is available to be bought
};
...
// Inside createSellOrder:
orders[orderId] = Order({
id: orderId,
seller: msg.sender,
tokenToSell: _tokenToSell,
amountToSell: _amountToSell,
priceInUSDC: _priceInUSDC,
deadlineTimestamp: deadlineTimestamp,
+ createdAt: block.timestamp,
isActive: true
});
...
// Inside amendSellOrder:
- if (_newDeadlineDuration == 0 || _newDeadlineDuration > MAX_DEADLINE_DURATION) revert InvalidDeadline();
+ if (
+ _newDeadlineDuration == 0 ||
+ _newDeadlineDuration + block.timestamp > order.createdAt + MAX_DEADLINE_DURATION
+ ) revert InvalidDeadline();
...
// Inside getOrderDetailsString:
details = string(
abi.encodePacked(
"Order ID: ",
order.id.toString(),
"\n",
"Seller: ",
Strings.toHexString(uint160(order.seller), 20),
"\n",
"Selling: ",
order.amountToSell.toString(),
" ",
tokenSymbol,
"\n",
"Asking Price: ",
order.priceInUSDC.toString(),
" USDC\n",
++ "Order CreatedAt: ",
++ order.createdAt.toString()
++ "\n",
"Deadline Timestamp: ",
order.deadlineTimestamp.toString(),
"\n",
"Status: ",
status
)
Updates

Lead Judging Commences

yeahchibyke Lead Judge
4 months ago
yeahchibyke Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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