OrderBook

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

Front-running Attack on OrderBook Allows Price Manipulation

Root + Impact

Description

  • Normally, when a seller lists an order, the price and token amount are stored and visible to all.

  • A buyer can submit a transaction to buy at that listed price, assuming it will remain unchanged.

  • However, the contract allows the seller to call amendSellOrder() without restriction, updating the price or amount even after a buyer has already submitted their buy tx (still pending in the mempool).

  • This allows the seller to front-run the buyer’s transaction and change the order price, leading to unexpected reverts, DoS, or even user losses if the buyer is using an auto-bot or private RPC.

function amendSellOrder(
uint256 orderId,
uint256 newAmount,
uint256 newPrice,
uint256 newDuration
) external {
Order storage order = orders[orderId];
require(order.orderSeller == msg.sender, "Only seller can amend order");
require(order.status == OrderStatus.Active, "Only active orders can be amended");
@> order.amount = newAmount; <@
@> order.price = newPrice; <@
@> order.deadline = block.timestamp + newDuration; <@
// ...
}

This shows that there is no lock or protection preventing a seller from updating the price at any time, including right before a buyer’s tx executes.

Risk

Likelihood:

  • This occurs when a seller monitors the mempool and detects a pending buyOrder() transaction.

  • Since amendSellOrder() has no restriction or lock mechanism, the seller can immediately increase the price or change the order before the buyer’s transaction confirms.

  • This becomes highly likely in production environments where bots or frontend wallets use public RPCs.

Impact:

  • Buyers experience unexpected transaction reverts, leading to gas loss and denial-of-service.

  • Opportunistic sellers can intentionally manipulate the order price after seeing large incoming buys, extracting more USDC or trapping front-running bots.

  • Creates a toxic market experience and opens the door for griefing attacks or malicious auction strategies.

Proof of Concept

Explanation:

This test simulates a frontrunning attack where a buyer (Dan) prepares a buyOrder() transaction based on a known order price of 180,000 USDC. While Dan’s transaction is pending in the mempool, the seller (Alice) amends the order and increases the price to 200,000 USDC. Since Dan only approved 190,000 USDC (believing that was more than enough), the transaction reverts unexpectedly.

This illustrates that sellers can manipulate listed orders after buyers have already submitted transactions, causing:

  • unexpected reverts

  • DoS for bot traders

  • potential fund mismanagement for unaware users

The test demonstrates how this vulnerability can be reproduced and the conditions under which it becomes exploitable.

function test_frontRunAmendBuyOrder() public {
// Step 1: Alice creates an order to sell WBTC at 180_000 USDC
vm.startPrank(alice);
wbtc.approve(address(book), 2e8); // 2 WBTC
uint256 aliceId = book.createSellOrder(address(wbtc), 2e8, 180_000e6, 2 days);
vm.stopPrank();
// Step 2: Dan (the buyer) approves just enough USDC to buy at the listed price
vm.startPrank(dan);
usdc.approve(address(book), 190_000e6); // Dan only has 190k
vm.stopPrank();
// Step 3: Alice front-runs the mempool and increases the price to 200_000 USDC
vm.prank(alice);
book.amendSellOrder(aliceId, 2e8, 200_000e6, 2 days);
// Step 4: Dan's pending buy tx now reverts due to insufficient balance for the new price
vm.startPrank(dan);
vm.expectRevert(); // Expect revert due to price being changed mid-flight
book.buyOrder(aliceId);
vm.stopPrank();
}

Recommended Mitigation

Explanation:

The mitigation introduces a grace period after order creation, during which amendments are disallowed (e.g., 15 minutes). This delay reduces the attack window for frontrunning and helps ensure that once a buyer sees an order and acts on it, the terms remain stable.

Additionally, the following hardening strategies could be considered:

  • Nonce or signature binding: Require buyers to sign the order price+ID before execution to detect tampering.

  • Slippage protection on buyer side: Let buyers define acceptable max price changes during buyOrder() to auto-reject if price was amended.

This ensures predictable trading UX, mitigates mempool-based attacks, and aligns with best practices from modern DEXs and NFT marketplaces.

- function amendSellOrder(...) public {
+ function amendSellOrder(...) public {
+ require(orders[orderId].timestamp + 15 minutes < block.timestamp, "Cannot amend order too soon after listing.");
+ require(orders[orderId].timestamp > 0, "Order does not exist.");
+ require(orders[orderId].orderSeller == msg.sender, "Only seller can amend.");
+ require(!orders[orderId].fulfilled, "Cannot amend fulfilled order.");
+ require(!orders[orderId].cancelled, "Cannot amend cancelled order.");
+ // Add a grace period or lock window post-creation to prevent mempool manipulation
+ // Alternatively, restrict amendments if the order is about to be fulfilled (pending buyer tx)
...
+ // Consider adding nonces or order hashes that must be signed by the buyer
+ // Or append slippage tolerance checks on buyer’s end to reject changed prices
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
10 days ago
yeahchibyke Lead Judge 9 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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