Root + Impact
Description
## Summary
The OrderBook contract fails to account for ERC20 tokens that charge transfer fees,
leading to order amount mismatches and potential transaction failures. When users create
or amend orders, the contract stores the requested amount but may receive less tokens due
to transfer fees, causing buyers to receive fewer tokens than expected or transactions to fail entirely.
## Vulnerability Details
The contract assumes all ERC20 tokens transfer the exact amount specified, but many legitimate tokens
implement fee-on-transfer mechanisms. In `createSellOrder()` and `amendSellOrder()`, the contract:
1. Calls `safeTransferFrom()` with the user-specified amount
2. Stores the full requested amount in the order
3. Never validates the actual amount received
This creates a mismatch between the stored order amount and the actual tokens available for buyers.
## Impact
- Failed transactions: Buyers attempting to purchase orders may have transactions revert
- Gas loss: Users lose gas on failed transactions
- Value loss: Buyers may receive fewer tokens than the order specified
- Order manipulation: Malicious actors can exploit fee-charging tokens to manipulate effective prices
- Locked funds: Orders may become unfillable, requiring cancellation to recover tokens
## Affected Functions
- `createSellOrder()` - Lines 110-135
- `amendSellOrder()` - Lines 137-175
- `buyOrder()` - Lines 177-195 (indirectly affected)
Risk
Likelihood:
Impact:
Proof of Concept
# Proof of Concept
1. **Deploy a token that charges 5% transfer fees**
2. **User creates order to sell 1000 tokens**
- User transfers 1000 tokens
- Contract only receives 950 tokens (5% fee taken)
- Order shows 1000 tokens available
3. **Buyer tries to purchase 1000 tokens**
- Contract tries to send 1000 tokens
- Contract only has 950 tokens
- **Transaction fails**
## Key Issue
The contract stores the requested amount (1000) but receives less due to fees (950),
creating a mismatch that causes buyer transactions to fail.
## Code Location
IERC20(_tokenToSell).safeTransferFrom(msg.sender, address(this), _amountToSell);
Recommended Mitigation
# Mitigation
## How to Fix
Check how many tokens you actually received and store that amount instead:
uint256 balanceBefore = IERC20(_tokenToSell).balanceOf(address(this));
IERC20(_tokenToSell).safeTransferFrom(msg.sender, address(this), _amountToSell);
uint256 balanceAfter = IERC20(_tokenToSell).balanceOf(address(this));
uint256 actualAmountReceived = balanceAfter - balanceBefore;
orders[orderId] = Order({
amountToSell: actualAmountReceived,
});
--The Fix: Now if a token charges 5% fees:
- User transfers 1000 tokens
- Contract receives 950 tokens
- Order shows 950 tokens available
- Buyer can successfully purchase 950 tokens
- **Transaction succeeds**