OrderBook

First Flight #43
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Seller can buy their own order (No self-purchase check)

Root + Impact

Description

  • The buyOrder function never checks that msg.sender (buyer) is different from order.seller. As a result, a seller can call buyOrder on their own order. In that case, they effectively pay the protocol fee to the contract and receive their tokens back:

order.isActive = false;
uint256 protocolFee = (order.priceInUSDC * FEE) / PRECISION;
...
iUSDC.safeTransferFrom(msg.sender, address(this), protocolFee);
iUSDC.safeTransferFrom(msg.sender, order.seller, sellerReceives);
IERC20(order.tokenToSell).safeTransfer(msg.sender, order.amountToSell);

Since order.seller == msg.sender, the contract sends the seller’s USDC back to themselves and returns the locked tokens. The seller only loses the fee. While not immediately catastrophic, this is unintended behavior and could waste fees or be used for manipulation (e.g. inflating volume).

Risk

Likelihood: Low

  • The seller would need to hold sufficient USDC to pay the fee. Usually sellers lock their tokens and have no remaining USDC, so this scenario is uncommon.

Impact: Low

  • The seller only pays a 3% fee (which goes to the protocol owner) and reclaims their tokens. No tokens are stolen, but fees are unnecessarily paid. It is still an inconsistent logic hole.

Proof of Concept

// Assume Alice sells 2 WBTC for 180,000 USDC
uint256 aliceId = book.createSellOrder(address(wbtc), 2e8, 180_000e6, 2 days);
// Alice acquires 180,000 USDC somehow (e.g., minted or via other means)
usdc.mint(alice, 180_000e6);
usdc.approve(address(book), 180_000e6);
// Alice calls buyOrder on her own order
book.buyOrder(aliceId);
// Result: Alice pays 3% fee (5400 USDC) and gets her 2 WBTC back

Here, the contract does not block Alice from buying her own order, and she ends up paying fees for no effective trade.

Recommended Mitigation

- order.isActive = false;
+ require(msg.sender != order.seller, "Cannot buy own order");
+ order.isActive = false;

Simply adding a check at the start of buyOrder prevents this case. This ensures only third-party buyers can fill an order.

Updates

Lead Judging Commences

yeahchibyke Lead Judge
about 1 month ago
yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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