The createSellOrder()
function assumes that safeTransferFrom()
will transfer the full _amountToSell
without deductions. However, many ERC20 tokens implement fee-on-transfer mechanics, where a portion of the tokens is automatically deducted during transfers. This leads the contract to overestimate the amount received, recording incorrect order data.
As a result, the contract may promise buyers more tokens than it holds, causing fund loss, trading failures, or griefing attacks. This also makes the system incompatible with fee-on-transfer tokens, which are common on mainnet.
The createSellOrder()
function originally assumed that safeTransferFrom()
would always transfer the full _amountToSell
of tokens from the seller to the contract. However, this assumption is not valid for fee-on-transfer or deflationary tokens (e.g., RFI, SafeMoon, etc.).
Such tokens deduct a fee during transfers, meaning the contract receives less than _amountToSell
. If not accounted for, this leads to the contract overestimating its balance, storing incorrect order data, and ultimately misleading buyers about the amount they are purchasing.
Likelihood:
This will occur when a fee-on-transfer or deflationary token is added to the allowed token list.
Many tokens in production (e.g., SafeMoon, EverRise, RFI) have non-standard transfer behavior, making this a realistic threat on mainnet.
Impact:
The order book will misrepresent how many tokens the contract actually holds, causing buyers to receive less than expected.
This may break downstream logic or protocols relying on accurate token amounts and result in financial loss for users.
This test simulates a common fee-on-transfer scenario where a token deducts a 10% fee during transfers. Although the seller intends to transfer 100 tokens, only 90 arrive at the contract. Since the original logic stores the full _amountToSell
, the order is overstated. If a buyer later purchases the order expecting 100 tokens, they receive only 90 — leading to fund loss and inconsistent contract state. This highlights how dangerous the incorrect assumption is in real-world use.
This fix replaces the assumption-based logic with a pre- and post-balance check, ensuring the contract records only the tokens it actually receives. This approach is robust against fee-on-transfer, burn-on-transfer, or deflationary tokens. The optional revert
on actualReceived == 0
protects the system from dust or unexpected zero-value transfers. By storing actualReceived
instead of _amountToSell
, the system guarantees accurate accounting and prevents buyer-side loss or orderbook inconsistencies.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.