OrderBook

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

Token can still be traded after the owner disables it – buyOrder ignores allowedSellToken flag

Root + Impact

Description

  • Normal behaviour

    The owner may call setAllowedSellToken(token, false) to block a token that is no longer deemed safe or desirable.
    The intuitive expectation is that no further trades involving that token should be possible.

  • Specific issue

    buyOrder never checks the allowedSellToken mapping.
    If a sell order was created while the token was allowed, that order remains fillable even after the owner later sets the token to false. Consequently, the supposedly-blocked asset can still change hands via the orders already sitting in the orderbook.

// no checks in buyOrder()

Risk

Likelihood: Medium

  • Reason: Requires the owner to disable a token after orders already exist.

Impact: High

  • Impact: Defeats the admin’s intention to halt trading (e.g., in an emergency),
    potentially exposing users to a compromised, risky or depreciated asset.


Proof of Concept

In the proof-of-concept, the owner first enables a previously unsupported ERC-20 by calling setAllowedSellToken(token, true). While the token is permitted, a seller creates a normal sell order for it. Next, the owner decides the asset is unsafe and disables it with setAllowedSellToken(token, false), intending to freeze all trading. Despite this change, any buyer can still execute buyOrder(orderId) and the transaction completes without error, because buyOrder never consults the allowedSellToken mapping. The order is filled, fees are collected, and the supposedly banned token changes hands—proving that disabling a token does not actually stop existing orders from being bought.

function test_buyOrderAfterTokenDisallowed() public {
// alice creates sell order for wbtc
vm.startPrank(alice);
wbtc.approve(address(book), 2e8);
uint256 aliceId = book.createSellOrder(address(wbtc), 2e8, 180_000e6, 2 days);
vm.stopPrank();
//owner sets false
vm.prank(owner);
book.setAllowedSellToken(address(wbtc), false);
// dan buys alice wbtc order
vm.startPrank(dan);
usdc.approve(address(book), 200_000e6);
book.buyOrder(aliceId);
vm.stopPrank();
}

Recommended Mitigation

Add a guard at the start of buyOrder

function buyOrder(uint256 _orderId) public {
+ if (!allowedSellToken[order.tokenToSell]) revert InvalidToken();
Updates

Lead Judging Commences

yeahchibyke Lead Judge
6 days ago
yeahchibyke Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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