OrderBook

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

No slippage protection for buyers

Root + Impact

Sellers can arbitrarily change the amount of asset they’re selling. This allows them to frontrun a buyer’s transaction and reduce the amount for sale, effectively scamming the buyer.

Description

  • When calling the buyOrder function, buyers only specify the orderId and expect to receive the full amount of the asset listed in the order.

  • The issue is that the seller can call amendSellOrder at any time and reduce the amount for sale (_newAmountToSell). If this happens while a buy transaction is already sitting in the mempool, the seller can frontrun it and reduce the amount—causing the buyer to receive less than what was originally listed.

function amendSellOrder(
uint256 _orderId,
uint256 _newAmountToSell,
uint256 _newPriceInUSDC,
uint256 _newDeadlineDuration
) public {
Order storage order = orders[_orderId];
// Validation checks
if (order.seller == address(0)) revert OrderNotFound(); // Check if order exists
if (order.seller != msg.sender) revert NotOrderSeller();
if (!order.isActive) revert OrderAlreadyInactive();
if (block.timestamp >= order.deadlineTimestamp) revert OrderExpired(); // Cannot amend expired order
if (_newAmountToSell == 0) revert InvalidAmount();
if (_newPriceInUSDC == 0) revert InvalidPrice();
if (_newDeadlineDuration == 0 || _newDeadlineDuration > MAX_DEADLINE_DURATION) revert InvalidDeadline();
uint256 newDeadlineTimestamp = block.timestamp + _newDeadlineDuration;
IERC20 token = IERC20(order.tokenToSell);
// Handle token amount changes
if (_newAmountToSell > order.amountToSell) {
// Increasing amount: Transfer additional tokens from seller
uint256 diff = _newAmountToSell - order.amountToSell;
token.safeTransferFrom(msg.sender, address(this), diff);
} else if (_newAmountToSell < order.amountToSell) {
// Decreasing amount: Transfer excess tokens back to seller
uint256 diff = order.amountToSell - _newAmountToSell;
token.safeTransfer(order.seller, diff);
}
// Update order details
@> order.amountToSell = _newAmountToSell;
order.priceInUSDC = _newPriceInUSDC;
order.deadlineTimestamp = newDeadlineTimestamp;
emit OrderAmended(_orderId, _newAmountToSell, _newPriceInUSDC, newDeadlineTimestamp);
}

Risk

Likelihood:

Can happen on each buyOrder call.

Impact:

The seller could change the amount to 1 (closest to nothing as possible) so get all of the price fot the initial amount of tokens giving almost nothing in return.

POC

Alice lists an sell offer 1e18 weth for 3000e6 usdc.
Bob wants to buy it so he calls the buyOrder function.
Alice sees the transaction in the mempool and immediately calls the amendSellOrder frontruning bob and changing the amountToSell to 1 (dust amount) and leaves the price to 3000e6 usdc.
The buyOrder call passes and Bob pays the full price for some dust amount.

Recommended Mitigation

Add some coolDown period after the change of the price when no offers are accepted or some slippage protection -> let the buyers specify the amount they are willing to receive and if it doesn't match with the offer, cancel the transaction.

function buyOrder(
uint256 _orderId,
+ uint256 minimumAmountToReceive,
) 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.amountToSell < minimumAmountToReceive) rever AmountNotMatched();
order.isActive = false;
Updates

Lead Judging Commences

yeahchibyke Lead Judge 14 days 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.