# Transfer-tax reserve desync and skim drain

Tax or burn logic that moves tokens without syncing pair reserves or re-entering fee hooks

_Area: DeFi accounting and math bugs. Seen in 2 disclosed reports; latest 2026-03-27. Updated 2026-10-29._

## How it works
A token's transfer logic (tax, burn, fee) changes the actual token balances held by a pool or pair, but the pool's recorded reserves are not updated to match. The pool's invariant assumes reserves equal real balances; once they diverge, the surplus sitting in the pair is claimable by anyone who calls the pair's skim-style function. The broken assumption is that internal accounting tracks reality after every balance-changing path.

## Where it shows up
- `_transfer()` that charges a fee/tax when sender or recipient is the LP pair, with no `sync()` call afterward [E2](https://bugbountylibrary.com/r/dhl%3A20230110-bra.md)
- Burn or deposit logic that sends tokens directly to a pair or contract, desyncing tracked balances [E1](https://bugbountylibrary.com/r/dhl%3A20260327-est.md)
- Fee hooks (`_update`) that re-enter themselves via internal `_burn`/`_mint`, so the fee applies to the fee [E3](https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md)
- Any path where a contract's stored balance differs from `token.balanceOf(contract)`
- Staking contracts that use a Uniswap pair as their accounting source [E1](https://bugbountylibrary.com/r/dhl%3A20260327-est.md)

## How to review
For every code path that moves tokens into or out of a pair or accounting contract, check the invariant: recorded reserves/balances equal actual token balances immediately after the operation. If a tax, burn, or direct transfer changes real balances, confirm a `sync()` (or equivalent) runs, or that the design never relies on stale reserves. For fee hooks, verify internal burn/mint paths do not route back through the fee-applying hook. Trace `_update` call graphs for recursion.

## Variants seen
- Double tax on LP-pair transfers without `sync()`, leaving reserves skim-able [E2](https://bugbountylibrary.com/r/dhl%3A20230110-bra.md)
- Burn/accounting logic desynced by direct transfers, then repeated `skim()` inflates balances [E1](https://bugbountylibrary.com/r/dhl%3A20260327-est.md)
- Recursive burn fee via `_update` over-burning `totalSupply` and over-charging users [E3](https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md)

## Impact and severity
Direct value loss: 819 BNB [E2](https://bugbountylibrary.com/r/dhl%3A20230110-bra.md) and 150.2 WBNB [E1](https://bugbountylibrary.com/r/dhl%3A20260327-est.md) drained via skims; corrupted `totalSupply` and downstream mint math [E3](https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md). Severity is high because the imbalance is permissionlessly harvestable and repeatable. Worse when the token is paired with a deep pool or the desync persists across many transfers.

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20260327-est.md
- E2: https://bugbountylibrary.com/r/dhl%3A20230110-bra.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md
