OrderBook

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

Missing Slippage Protection May Result in Unfavorable Trades for Users (No Minimum Expected Output → Potential Overpayment or Value Loss)

Author Revealed upon completion

Missing Slippage Protection May Result in Unfavorable Trades for Users (No Minimum Expected Output → Potential Overpayment or Value Loss)

Description

  • The buyOrder(uint256 _orderId) function lacks a mechanism for the buyer to specify acceptable trade terms, such as a minimum amount of tokens expected or a maximum price they're willing to pay.

  • This makes the function vulnerable to price mismatches or delays between order visibility (e.g., on a frontend or block explorer) and transaction execution. Since orders are matched and fulfilled purely based on order ID without user-side slippage bounds, buyers may unintentionally complete trades under worse-than-expected conditions.

  • Additionally, this lack of slippage protection exposes users to front-running MEV bots, who can snipe valuable orders from the mempool before legitimate users.


Risk

Likelihood:

  • This design pattern (no slippage/min expected amount check) is very common in simple DeFi contracts.

  • Many developers assume price/order terms are static or fixed, but:

    • Mempool latency

    • Delayed UI state

    • Miner reordering or MEV bots
      all create scenarios where execution conditions change between user intent and transaction mining.

  • Front-running risk is already high on public chains (see Flashbots ecosystem).

Impact:

  • Honest users may overpay or receive fewer tokens than expected due to price shifts or stale UI data.

  • Users are vulnerable to MEV-style frontrunning attacks.

  • There's no way for buyers to protect themselves by reverting unfavorable trades.

Proof of Concept

function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
if (order.seller == address(0)) revert OrderNotFound();
if (!order.isActive) revert OrderNotActive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired();
order.isActive = false;
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
uint256 sellerReceives = order.priceInUSDC - protocolFee;
iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee);
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
IERC20(order.tokenToSell).safeTransfer(msg.sender, order.amountToSell);
}

Notice the absence of any checks like:

require(order.amountToSell >= minExpectedAmount, "Slippage too high");

or

require(order.priceInUSDC <= maxAcceptablePrice, "Price exceeds slippage bounds");

Recommended Mitigation

  • Add buyer-defined protection parameters to the buyOrder() function:

  • function buyOrder(uint256 _orderId, uint256 maxPriceInUSDC) external { ... }

Or:

  • function buyOrder(uint256 _orderId, uint256 minTokensOut) external { ... }

  • Include require() statements to ensure the order still meets the buyer’s terms:

  • require(order.priceInUSDC <= maxPriceInUSDC, "Slippage limit exceeded");

  • Encourage off-chain quoting (e.g., signed metadata) or batch execution for safer pricing.

Updates

Lead Judging Commences

yeahchibyke Lead Judge
7 days ago
yeahchibyke Lead Judge 4 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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