OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

H01-No slippage protection in buyOrder

Root + Impact

Description

  • In normal operation, a buyer reads an order's amountToSell and priceInUSDC off-chain, and expects to buy that exact amount of tokens at that price.

  • However, the seller can call amendSellOrder() before buyOrder() is executed, modifying amountToSell or priceInUSDC without buyer consent. This creates a slippage risk, where the buyer may overpay or receive fewer tokens than expected.

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

Risk

Likelihood:

  • Sellers can amend their orders at any time before a buyer fills it.

  • Buyers relying on off-chain reads cannot guarantee the state hasn’t changed by the time their transaction is mined.

Impact:

  • Buyer receives fewer tokens than expected, losing value.

  • Buyer pays more USDC than intended, enabling a griefing or front-running scenario.

Proof of Concept

1) User see the order in the website app ui. For example
"500 wETH against 1000 USDC"

2) User submits the transaction on-chain through l'UI

3) While the transaction is on the mempool, the seller changes the amount, for ex. 1 wETH against 1000 USDC"

4) When the first transaction is taken by a validor, the amount in wETH is now 1. As a result, the user will pay 1000 USDC for only 1 wETH.


Recommended Mitigation

Add two supplementary paramters in the function buyOrder: uint256 expectedAmountToSell, uint256 expectedPriceInUSDC.

In the function, check if their value match the value inside the order, otherwise revert.

+ error SlippageOnAmount();
+ error SlippageOnPrice();
- function buyOrder(uint256 _orderId) public {
+ function buyOrder(uint256 _orderId,
+uint256 expectedAmountToSell, uint256 expectedPriceInUSDC) public {
if (!order.isActive) revert OrderNotActive();
+ if (order.amountToSell != expectedAmountToSell) revert SlippageOnAmount();
+ if (order.priceInUSDC != expectedPriceInUSDC) revert SlippageOnPrice();
// rest of the code
Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

Buy orders can be front-run and amended maliciously

A malicious seller can front-run a buy order for their order, and decrease the amount of assets to be sold. If the price is unchanged, the buy transaction fulfills, but the buyer gets lesser amount than expected.

Support

FAQs

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