Section 15 of 16
Router: Fee-on-Transfer Support
What You Are Building
Some ERC-20 tokens deduct a fee on every transfer. When you send 100 tokens, the recipient might only receive 98. The original USDT, deflationary tokens, and many "rebase" tokens behave this way.
The standard swap functions from the previous section assume the full amount arrives at the pair. They pre-calculate the exact output using getAmountOut. But if 2% disappears during the transfer, the math breaks. The pair would try to send more output than the constant product formula allows, and the swap would revert.
Uniswap V2 Router02 solves this with a separate set of swap functions that do not pre-calculate amounts. Instead, they measure the actual balance after each transfer to determine the real input.
_swapSupportingFeeOnTransferTokens
This is the alternative to _swap(). Instead of using a pre-calculated amounts array, it reads the pair's actual token balance at each hop:
- For each hop, get the pair's reserves.
- Read the pair's current balance of the input token.
- Calculate
amountInput = balance - reserveInput. This is the actual amount that arrived, after any transfer fee. - Use
getAmountOut(amountInput, reserveIn, reserveOut)to calculate the output for this hop. - Determine the swap direction and call
pair.swap().
The key difference: the standard _swap() trusts the pre-calculated amounts array. The FOT version trusts the actual on-chain balance. This makes it work with any token, regardless of transfer behavior.
FOT Swap Variants
Three external functions use the FOT-safe internal swap:
swapExactTokensForTokensSupportingFeeOnTransferTokens: Transfers input tokens to the first pair, calls the FOT swap, then checks that the recipient's final balance increased by at least amountOutMin. It cannot use getAmountsOut to predict the output, so it measures the recipient's balance before and after.
swapExactETHForTokensSupportingFeeOnTransferTokens: Wraps ETH, deposits WETH to the first pair, runs the FOT swap, and validates the recipient's output balance.
swapExactTokensForETHSupportingFeeOnTransferTokens: Runs the FOT swap with the Router as recipient, measures the WETH received, unwraps it, and sends ETH to the user.
Notice there are no "exact output" FOT variants. You cannot guarantee an exact output when fees are unpredictable.
FOT-Safe Liquidity Removal
removeLiquidityETHSupportingFeeOnTransferTokens: Similar to removeLiquidityETH, but instead of trusting the returned amountToken, it measures the Router's actual token balance after the burn. This handles tokens that take a fee on the transfer from the pair to the Router.
removeLiquidityETHWithPermitSupportingFeeOnTransferTokens: Adds permit support to the FOT-safe liquidity removal.
Library Wrapper View Functions
The Router also exposes the Library's calculation functions as public view functions so external contracts and frontends can call them without importing the library directly: quote, getAmountOut, getAmountIn, getAmountsOut, and getAmountsIn. Each simply delegates to UniswapV2Library.
Your Task
Implement the FOT-safe swap internal function, all five FOT external functions, and the five library wrapper view functions. The starter code provides the function signatures and the infrastructure from previous sections.