# Fee-on-transfer AMM reserve desync

Tokens whose fees or hooks make pair balanceOf diverge from cached reserves, then skim/sync abuse

_Area: DeFi oracles, liquidations and flash loans. Seen in 6 disclosed reports; latest 2026-08-03. Updated 2026-10-23._

## How it works
Uniswap V2-style pairs cache reserves and update them only through swap/mint/burn/sync. They assume the token balance held equals the cached reserve. Two mechanisms break that assumption: (a) fee-on-transfer, where the pair receives less than the amount recorded, so actual balance falls below the reserve; and (b) pair-side tax, where the token charges its fee from the pair's own balance, so the pair pays extra and the balance drops without the caller spending [E5](https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md). Either way, balance and reserve drift apart. `skim()` pays out any surplus above reserves and `sync()` rewrites reserves to the current balance; both are permissionless by design. A caller who can create a gap can move the pair's price away from the token's real backing and trade against the distorted ratio.

## Where it shows up
- Tokens with buy/sell tax, burn, reflection, or a transfer hook, paired in V2-style pools.
- Tax charged from the pair's own balance rather than the sender [E5](https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md).
- Helper contracts that can mint/burn against a live pair, letting a reserve be pushed to dust [E3](https://bugbountylibrary.com/r/dhl%3A20260606-boss.md).
- Routers that transfer tokens twice for one logical swap, or use fee-on-transfer paths [E2](https://bugbountylibrary.com/r/dhl%3A20260616-dip.md).
- Deflationary mechanics where repeated small transfers plus skim shift the ratio [E4](https://bugbountylibrary.com/r/dhl%3A20250518-krctoken-pair.md).
- Direct transfers to the pair followed by repeated skim calls [E6](https://bugbountylibrary.com/r/dhl%3A20231008-pseudoeth.md).

## How to review
State the invariant: after every token movement, cached reserves must equal actual balances, or any divergence must be unexploitable. Check:
- Does the token deduct a fee on transfer? Does the pair account for the received amount or the sent amount?
- On each swap direction, which address pays the tax — sender or the pair itself [E5](https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md)?
- Compare `token.balanceOf(pair)` against `pair.getReserves()` before and after a transfer; any gap is the attack surface.
- Can an unprivileged caller create a gap and then call `skim()`/`sync()` to profit [E1](https://bugbountylibrary.com/r/dhl%3A20260803-aic.md)[E4](https://bugbountylibrary.com/r/dhl%3A20250518-krctoken-pair.md)[E6](https://bugbountylibrary.com/r/dhl%3A20231008-pseudoeth.md)?
- Can a helper mint/burn the pair's reserve to dust [E3](https://bugbountylibrary.com/r/dhl%3A20260606-boss.md)?
- Does the router path transfer tokens more than once per swap [E2](https://bugbountylibrary.com/r/dhl%3A20260616-dip.md)?
- Is `sync()` reachable mid-swap, before the pair's own accounting settles?

## Variants seen
- Flash-swap collapses one reserve, then cheap buyout of the other via skim/sync [E1](https://bugbountylibrary.com/r/dhl%3A20260803-aic.md).
- Sell fee plus double router-transfer distorts reserves via skim/sync [E2](https://bugbountylibrary.com/r/dhl%3A20260616-dip.md).
- Helper mint/burn pushes pair reserve to dust, then taxed swaps drain the quote token [E3](https://bugbountylibrary.com/r/dhl%3A20260606-boss.md).
- Repeated transfer+skim cycles desync a deflationary token pair [E4](https://bugbountylibrary.com/r/dhl%3A20250518-krctoken-pair.md).
- Tax charged from pair balance skews price on buys [E5](https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md).
- Direct transfers plus repeated skim desync a pair from the token's backing [E6](https://bugbountylibrary.com/r/dhl%3A20231008-pseudoeth.md).

## Impact and severity
Drains ranged from ~$7k [E4](https://bugbountylibrary.com/r/dhl%3A20250518-krctoken-pair.md) and ~1.4 ETH [E6](https://bugbountylibrary.com/r/dhl%3A20231008-pseudoeth.md) to 10,207 USDT [E3](https://bugbountylibrary.com/r/dhl%3A20260606-boss.md), 111k USDC [E2](https://bugbountylibrary.com/r/dhl%3A20260616-dip.md), and 32.36 BNB [E1](https://bugbountylibrary.com/r/dhl%3A20260803-aic.md). Flash-swap funded the collapse in [E1](https://bugbountylibrary.com/r/dhl%3A20260803-aic.md); a flash loan funded the pair-side-tax manipulation in [E5](https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md). Severity is high: where the token is freely transferable, any holder can trigger the gap [E1](https://bugbountylibrary.com/r/dhl%3A20260803-aic.md)[E4](https://bugbountylibrary.com/r/dhl%3A20250518-krctoken-pair.md)[E5](https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md)[E6](https://bugbountylibrary.com/r/dhl%3A20231008-pseudoeth.md); [E3](https://bugbountylibrary.com/r/dhl%3A20260606-boss.md) additionally needs a helper mint/burn privilege. Pool liquidity is the loss ceiling, worse when the token is widely paired and skim/sync are reachable.

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20260803-aic.md
- E2: https://bugbountylibrary.com/r/dhl%3A20260616-dip.md
- E3: https://bugbountylibrary.com/r/dhl%3A20260606-boss.md
- E4: https://bugbountylibrary.com/r/dhl%3A20250518-krctoken-pair.md
- E5: https://bugbountylibrary.com/r/dhl%3A20250224-invistech.md
- E6: https://bugbountylibrary.com/r/dhl%3A20231008-pseudoeth.md
