OrderBook

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

Lack of slippage protection in buyOrder allows frontrunning via amendSellOrder, causing buyers to overpay

Author Revealed upon completion

Root + Impact

Description

Normally, buyers are expected to purchase orders listed by sellers at a known and agreed-upon price (priceInUSDC) and quantity (amountToSell). Buyers approve the USDC token and call buyOrder(orderId) expecting that the purchase terms remain static during transaction execution.

Explain the specific issue or problem

An attacker can frontrun a buyOrder transaction that is targeted to his own open limit order in the book by calling amendSellOrder right before it executes. This allows the attacker to increase the priceInUSDC or decreaseamountToSell, resulting in the buyer unintentionally overpaying. If the buyer has given a larger token allowance (as is typical), the contract will pull a higher amount of USDC than anticipated.

// Attack tx in same block before buyOrder
function amendSellOrder(
uint256 _orderId,
uint256 _newAmountToSell, // same as current
uint256 _newPriceInUSDC, // maliciously increased
...
)
// Vulnerable sequence
function buyOrder(uint256 _orderId) public {
Order storage order = orders[_orderId];
...
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);
...
}

Risk

Likelihood:Medium

  • Reason 1: This only occurs if a buyer submits buyOrder and has given a larger-than-needed USDC allowance.

  • Reason 2: An attacker can only frontrun buyOrder() transactions to their own open limit orders since protocol only allows amending your own orders

Impact:High

  • Impact 1: Buyer can be forced to overpay, resulting in unexpected fund loss.


Proof of Concept

An attacker can frontrun a buyOrder by calling amendSellOrder and increasing the priceInUSDC or decreasing amountToSell. If the buyer has given a large USDC allowance, they’ll unknowingly overpay when their transaction executes, resulting in unintended fund loss.

//Alice lists an order:
vm.prank(alice);
createSellOrder(WETH, 1 ether, 100 USDC, 1 day);
//Bob sees this order and submits a transaction to:
vm.startPrank(Bob);
iUSDC.approve(orderBook, 1000 USDC); // overly generous approval
orderBook.buyOrder(orderId);
vm.stopPrank();
//Malicious Alice frontruns Bob’s transaction with:
vm.prank(alice);
amendSellOrder(orderId, 1 ether, 500 USDC, 1 day);
//Bob ends up paying 500 USDC instead of 100, resulting in an overpayment of 5x.

Recommended Mitigation

Require buyers to pass the expected priceInUSDC and expectedTokenAmount as parameter to buyOrder. If the on-chain price doesn’t match, revert the transaction. This prevents price manipulation between transaction submission and execution.

- function buyOrder(uint256 _orderId) public {
+ function buyOrder(uint256 _orderId, uint256 expectedPriceInUSDC,uint256 expectTokenAmount) public {
Order storage order = orders[_orderId];
+ if (order.priceInUSDC != expectedPriceInUSDC) revert("Price mismatch");
+ if (order.amountToSell != expectTokenAmount) revert("Token amount mismatch");
Updates

Lead Judging Commences

yeahchibyke Lead Judge
6 days ago
yeahchibyke Lead Judge 2 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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