Fee-on-transfer tokens can credit a recipient with fewer units than the sender spends; rebasing tokens can change a wallet's balance without a trade. A bot must separate the quote, the actual swap receipt and later balance adjustments, then check the exact router's support. Size the next action from reconciled holdings, not a quoted amount or transfer log alone.
Identify which amount changed
A swap involves several quantities: the amount debited from the sender, the amount reaching the pool, the pool's output and the amount credited to the final recipient. An integration that stores all four as “trade amount” loses the evidence needed to explain a shortfall.
With an inclusive fee-on-transfer token, part of a transfer is burned or diverted, leaving the recipient with less than the sender transferred. An exclusive fee adds a separate trailing transfer. These are distinct integration problems in the Uniswap v2 troubleshooting guide; a method handling the first does not establish compatibility with the second.
A rebase changes token balances independently of a transfer. For a documented implementation, Lido's stETH can update a holder's balance without emitting a holder-level ERC-20 transfer event. Its integration guide explains the share accounting behind that behavior. An event-only inventory can therefore become stale even when no swap was missed.
Pool liquidity also affects output. The concentrated-liquidity execution guide follows that cause through a swap path. Here, the additional question is whether the token's own mechanics change the amounts crossing each boundary.
Check the exact router and function
“The DEX supports this token” is too broad for a bot configuration. Record the chain, token contract, router address, contract version, called function and full route. An aggregator's name does not reveal which underlying contracts execute its quote.
In canonical Uniswap v2 Router02, the exact-input function swapExactTokensForTokensSupportingFeeOnTransferTokens calculates each hop's input from the pair's current balance minus its recorded reserve. It checks the final recipient's output balance increase against amountOutMin. The function does not return the ordinary swap method's amounts array. These details come from the Router02 source, rather than a promise about every router bearing a similar name.
| Documented integration | What the evidence establishes | What still needs checking |
|---|---|---|
| Uniswap v2 Router02 supporting exact-input token swap | Input is derived from pair balances; final token receipt has a minimum check | Token behavior, deployed code, route and recipient |
| Uniswap v3 router contracts | The v3 documentation says fee-on-transfer tokens do not function with those routers | A different integration needs its own evidence |
| Custom router or wrapper | Its own implementation defines handling | Debit, receipt, conversion rate and every relevant failure path |
The Uniswap v3 token integration page also makes a narrower distinction for rebasing tokens: pool creation and swaps can succeed, while liquidity providers bear negative-rebase losses when positions become active. A successful swap does not establish suitable accounting for holding the asset or providing liquidity. These are v3 statements; they do not describe every Uniswap version.
Increasing slippage tolerance cannot add a missing token-handling implementation. It changes the acceptable execution boundary. Investigate an unsupported transfer path before changing that boundary.
A hypothetical taxed swap, followed by a rebase
This original example uses fictitious tokens X and Y. Both have six decimal places. The pool starts with 100,000 X and 50,000 Y, with balances equal to recorded reserves. There are no competing transactions, donations or rebases during the swap. The recipient starts with zero Y.
Assume the sender spends 1,000 X. X deducts a 2% inclusive fee on the transfer into the pool. Y deducts a 3% inclusive fee on the pool's transfer to the recipient. These invented token fees are separate from the pool fee.
For the pool calculation, use the canonical Uniswap v2 library's output formula:
output = floor(input × 997 × reserveY / (reserveX × 1000 + input × 997))
All values in that expression are integer base units. The factor 997/1000 represents this formula's 0.30% pool fee. For the invented transfer fees, floor the fee in base units and credit the remainder. This states the rounding rule explicitly instead of rounding each displayed amount arbitrarily.
A reserve-based estimate that treats the full 1,000 X as pool input gives 493.579017 Y before any output transfer fee. The actual input reaching the pool is 1,000 − 20 = 980 X, producing a smaller gross output.
| Quantity or confirmed step | X amount | Y amount | Accounting meaning |
|---|---|---|---|
| Estimate ignoring token transfer fees | 1,000.000000 input | 493.579017 estimated output | A comparison quote, not inventory |
| Sender's actual debit | 1,000.000000 | — | Total X spent by the wallet |
| Inclusive X fee | 20.000000 | — | X diverted or burned |
| Actual pool input | 980.000000 | — | Input used in the pool formula |
| Gross pool output | — | 483.802954 | Y leaving the pool before its fee |
| Inclusive Y fee | — | 14.514088 | Y diverted or burned |
| Recipient's swap receipt | — | 469.288866 | Actual acquired wallet units |
| Later 4% negative rebase | — | −18.771555 | Balance adjustment under the hypothetical rule |
| Holding after that rebase | — | 450.517311 | Current Y inventory |
The output fee is floor(483.802954 × 3%) at six decimals, or 14.514088 Y. Subtracting it leaves 469.288866 Y. Relative to the quote ignoring transfer fees, the receipt is 24.290151 Y lower, about 4.9212%. That difference combines the reduced pool input and the output transfer fee; it is not all a change in market price.
For this fixture, a recipient minimum of 470 Y would fail the Router02 balance-delta check. A minimum of 465 Y would pass that check, subject to the example's other assumptions. Those are comparisons for explaining the check, not suggested slippage settings.
After the swap, assume Y undergoes a separate 4% negative rebase. The hypothetical token computes the new wallet balance by flooring 469.288866 × 0.96 to six decimals, yielding 450.517311 Y. No further trades or transfers occur. This is an invented stress case, not a statement about stETH's rebase size, frequency or history.
Keep the 18.771555 Y reduction as a balance adjustment. Rewriting the original fill as 450.517311 Y would erase the distinction between execution and subsequent ownership changes. The example stops at inventory: valuing that holding requires a separate price observation, and gas is excluded from these token amounts.
Reconcile holdings without inventing trades
Use a token-unit identity for a defined wallet and period:
closing balance = opening balance + net receipts − actual debits + rebase adjustments + other explained changes
Each term needs a timestamp or block reference. For the example's swap-only period, zero opening Y and no other activity make the wallet increase equal to the receipt. In a shared wallet, a deposit or another bot's transfer between observations would break that shortcut. Block-end balances also combine all relevant activity in the block; they do not automatically isolate one transaction.
Retain the transaction hash, recipient, token address, transfer records and balance observations. Use transaction-level execution evidence where necessary to isolate the swap. If the evidence cannot separate concurrent changes, leave an unresolved difference and suspend dependent sizing instead of assigning it to the nearest trade.
Preserve event granularity when preparing reports. The aggregate versus raw trades guide explains why aggregation changes what a record represents. On-chain records add another boundary: a pool swap event, a token transfer and a rebase adjustment answer different questions. None should silently become an extra fill in the journal.
For stETH specifically, Lido recommends storing and operating shares, then converting to token amounts for interaction. Its guide also documents small integer-rounding residues in token/share conversion. Rebasing tokens need an adapter for their actual accounting model; shares are not a universal standard for all of them.
A wrapper changes the unit being held. In Lido's wstETH implementation, the holder's wrapper balance stays constant through a rebase while its stETH conversion rate changes. Keep the wrapper contract, quantity and rate observation together, using the same Lido integration guidance. Treating one wrapper unit as one underlying token would introduce a fresh accounting error.
Validate the boundary before enabling automation
Use this worksheet for each token and route. Save the evidence alongside the configuration so a later change can be compared with the original assumptions.
| Check | Evidence to retain | Decision it supports |
|---|---|---|
| Asset identity | Chain, token contract, decimals and wrapper relationship | Which unit the bot owns |
| Transfer semantics | Sender debit and recipient credit for each relevant boundary | Whether fees or extra transfers alter amounts |
| Router handling | Deployed address, version, source and exact function | Whether that execution path handles those semantics |
| Quote assumptions | Block reference, route, input and included fees | Whether estimated output means final receipt |
| Balance model | Rebase event/index or share conversion, with observation time | How holdings can change between trades |
| Reconciliation scope | Wallet, transaction identities and concurrent activity | Whether a balance delta can be attributed |
| Failure policy | Revert, unknown transaction status and unresolved balance actions | When the next instruction must wait |
In a controlled simulation, exercise the actual contract path in both directions: wallet to pool and pool to wallet. Check the chosen minimum against final recipient receipt, then run a rebase between an acquisition and its follow-up action. Include existing inventory, rounding residue and another wallet movement. A simulation at one state supports only those tested conditions.
Rebases can also affect pool balances. Uniswap's v2 troubleshooting documentation describes reserve drift: negative rebases can shift a deficit to a later transacting account, while positive rebases can leave a surplus outside recorded reserves. Re-reading the wallet alone cannot certify the next pool quote.
An exclusive trailing fee or a changed transfer rule requires a compatibility investigation. A provider timeout needs a different response: reconcile the original transaction's status before another spending attempt. The API retry and backoff guide helps define transient-error handling; repeated submissions cannot resolve an unsupported token mechanism.
For centralized exchange balances, retain the venue's separate available-versus-total reconciliation. An on-chain wallet receipt does not define an exchange's credited or spendable balance. These on-chain examples are educational and do not establish KLINEO support for these token types or router integrations.
Before the next action, require three recorded values: what the quote expected, what the swap actually delivered, and what the wallet holds at the new decision time. If those values differ, explain the difference before the bot spends the balance.
