OrderBook

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

Lack of Support for Fee-on-Transfer Tokens Can Lead to Locked Orders

Description
The contract assumes that the amount of tokens transferred is exactly the amount specified. When a seller creates an order, safeTransferFrom moves _amountToSell tokens into the contract. When a buyer fills the order, the contract attempts to safeTransfer that same _amountToSell to the buyer. If the owner were to allow a fee-on-transfer token, the initial transfer to the contract would result in the contract holding less than _amountToSell. The subsequent transfer to the buyer would fail because the contract's balance is insufficient, reverting the buyOrder transaction. The order would then be un-fillable, and the seller's funds would be stuck until manually cancelled.

Impact
If a fee-on-transfer token is whitelisted, any orders created for it will be impossible to fill, leading to locked assets and a poor user experience.

Proof of Concept (PoC):

  1. The contract owner whitelists a fee-on-transfer token (e.g., FEE-TOKEN) using setAllowedSellToken.

  2. A seller creates a sell order for 100 FEE-TOKEN. The token charges a 2% fee on transfer, so only 98 tokens are actually received by the contract.

  3. The order is stored with amountToSell = 100.

  4. A buyer attempts to fill the order. The contract tries to transfer 100 FEE-TOKEN to the buyer using safeTransfer, but the contract only holds 98 tokens.

  5. The transfer fails and the transaction reverts, making the order unfillable.

  6. The seller must manually cancel the order to recover the remaining tokens, but the order can never be filled.


Recommended Mitigation
The contract should either explicitly forbid fee-on-transfer tokens in its documentation and off-chain processes, or it should be modified to handle them. To handle them, the contract should measure its token balance before and after the initial safeTransferFrom to accurately record the amount received, and use this actual amount for the subsequent transfer to the buyer.

// In createSellOrder(...)
// RECOMMENDED LOGIC
+IERC20 token = IERC20(_tokenToSell);
+uint256 balanceBefore = token.balanceOf(address(this));
+token.safeTransferFrom(msg.sender, address(this), _amountToSell);
+uint256 balanceAfter = token.balanceOf(address(this));
+uint256 amountReceived = balanceAfter - balanceBefore;
// Store amountReceived in the Order struct instead of _amountToSell
+orders[orderId] = Order({
// ...
+ amountToSell: amountReceived,
// ...
+});
Updates

Lead Judging Commences

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

Appeal created

0xanis Submitter
4 days ago
yeahchibyke Lead Judge about 8 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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