OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
View results
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)

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
10 months ago
yeahchibyke Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!