OrderBook

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

Buy orders can be front-run and edited before being confirmed causing users a loss of funds

Buy orders can be front-run and edited before being confirmed causing buyers a loss of funds

Description

Users can use the buyOrder() function to fulfil sell orders, however, sell orders can be edited while the order is still active, this allows the seller to front-run a buy call for their sell order, editing their order so that the buyer ends up overpaying.

Risk

Likelihood:

  • This can happen whenever a malicious seller's order is being fulfilled

Impact:

  • Loss of funds for the buyer

Proof of Concept

Append the following test to TestOrderBook.t.sol and run forge test --mt test_frontRun

function test_frontRun() public {
address attacker = makeAddr("attacker");
address user = makeAddr("user");
uint256 amountToSell = 2e8;
uint256 price = 180_000e6;
wbtc.mint(attacker, amountToSell);
usdc.mint(user, price);
// The attacker creates sell order for wbtc
vm.startPrank(attacker);
wbtc.approve(address(book), 2e8);
uint256 orderId = book.createSellOrder(address(wbtc), amountToSell, price, 2 days);
vm.stopPrank();
// The user submits a transaction trying to fulfil the attacker's sell order
// The attacker sees this pending transaction and front-runs it, reducing the number of tokens inside the order but keeping the price the same
vm.startPrank(attacker);
book.amendSellOrder(orderId, 1, price, 2 days);
vm.stopPrank();
// The user's buy transaction is confirmed, receiving significantly less tokens than what they were expecting
vm.startPrank(user);
usdc.approve(address(book), price);
book.buyOrder(orderId);
vm.stopPrank();
uint256 amountReceivedAfterFees = price - (price * book.FEE() / book.PRECISION());
assertEq(usdc.balanceOf(attacker), amountReceivedAfterFees);
assert(wbtc.balanceOf(user) != amountToSell);
}

Recommended Mitigation

Consider allowing buyers to set a slippage, reverting the transaction if the order is too unfavorable.

function buyOrder(
uint256 _orderId,
+uint256 minReceiveAmount,
+uint256 maxPrice
) 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();
+ if (order.priceInUSDC > maxPrice) revert PriceTooHigh();
+ if (order.amountToSell < minReceiveAmount) revert InsufficientAmount(); .
.
.
}
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.