OrderBook

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

Timestamp-Based Expiry Logic

Timestamp-Based Expiry Logic

Description

  • The contract uses block.timestamp to determine whether an order has expired. While this is a common pattern, the value of block.timestamp can be manipulated slightly by miners (usually by ±15 seconds). This small window can impact precision-critical expiry logic—such as order fills or amendments close to the deadline.

  • Affected Functions:

buyOrder(uint256)
amendSellOrder(...)
getOrderDetailsString(...)

Risk

Likelihood:

  • Miner manipulation of block.timestamp is real but constrained:

    • Miners can only set it within a small window (~±15 seconds).

    • Requires block production control — unlikely for most users, but possible for MEV-aware miners or bots.

  • If expiry windows are very tight, this issue becomes more likely to affect behavior.

Impact:

  • A malicious or incentivized miner could manipulate timestamp to:

  • Force early order expiration.

  • Delay expiry to allow unauthorized or delayed purchases.

  • Causes fragility around expiration boundaries.

  • Unreliable in environments where seconds matter (e.g., auctions, arbitrage, high-frequency DEXs).


Proof of Concept

function test_orderExpiryBoundaryManipulation() public {
// Step 1: Create a sell order with short deadline
vm.startPrank(alice);
wbtc.approve(address(book), 1e8);
uint256 orderId = book.createSellOrder(address(wbtc), 1e8, 100_000e6, 1 hours);
vm.stopPrank();
// Step 2: Warp to 1 second before expiry and buy
vm.warp(book.getOrder(orderId).deadlineTimestamp - 1);
vm.startPrank(dan);
usdc.approve(address(book), 100_000e6);
book.buyOrder(orderId); // Passes
vm.stopPrank();
// Step 3: Create another order and warp to expiry
vm.startPrank(alice);
wbtc.approve(address(book), 1e8);
uint256 orderId2 = book.createSellOrder(address(wbtc), 1e8, 100_000e6, 1 hours);
vm.stopPrank();
vm.warp(book.getOrder(orderId2).deadlineTimestamp);
vm.startPrank(dan);
usdc.approve(address(book), 100_000e6);
vm.expectRevert(OrderBook.OrderExpired.selector);
book.buyOrder(orderId2); // Reverts
vm.stopPrank();
}

Recommended Mitigation

Add a grace buffer (e.g., allow deadline + 15s margin).

Optionally use block.number with estimated time for greater consistency.

If strict expiry is acceptable, document this edge behavior clearly for users.

Updates

Lead Judging Commences

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

Give us feedback!