# Transfer hooks mutating pool reserves

Token transfer/receive hooks that swap, burn or re-stake reserves, skewing pool price or exchange rate

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2026-06-04. Updated 2026-10-25._

## How it works
A token's transfer or receive path does more than move balances: it swaps contract-held tokens into an AMM pair, burns tokens out of the pair, or re-stakes incoming native funds. The broken assumption is that a transfer only changes the sender's and recipient's balances. Here it also changes pool reserves or the staking exchange rate, so the price or ratio a user sees is not the one the next transfer produces. Where the hook is permissionless and repeatable, an attacker can drive the pool or ratio in their favor [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md)[E3](https://bugbountylibrary.com/r/dhl%3A20241117-mft.md); where it fires on protocol-internal transfers, it corrupts accounting without any attacker [E2](https://bugbountylibrary.com/r/c4%3A2025-04-kinetiq%3AH-03.md).

## Where it shows up
- `_transfer`/`transfer` branches that call a swap, burn, or fee-dump when a balance or threshold is crossed [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md)[E3](https://bugbountylibrary.com/r/dhl%3A20241117-mft.md)
- Swaps with `amountOutMin=0` or no slippage bound inside the token itself [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md)
- Burn-on-transfer or fee-on-transfer logic that touches the LP pair's own balance [E3](https://bugbountylibrary.com/r/dhl%3A20241117-mft.md)
- `receive()`/`fallback()` that auto-stakes or auto-invests any incoming native token [E2](https://bugbountylibrary.com/r/c4%3A2025-04-kinetiq%3AH-03.md)
- Bypass amplifier (not a reserve mutation): anti-whale guards (max-holder, cooldown, cost basis) keyed only to per-address state, so fresh-address splitting neutralizes them [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md)

## How to review
Trace every path that moves value and ask whether it can change a shared reserve or ratio. The invariant: a transfer between two non-pool addresses must leave pool reserves and the exchange rate unchanged, and protocol-internal refunds must not be re-deployed [E2](https://bugbountylibrary.com/r/c4%3A2025-04-kinetiq%3AH-03.md). Check that swaps have a real `amountOutMin` [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md), that burns never target the pair [E3](https://bugbountylibrary.com/r/dhl%3A20241117-mft.md), and that `receive()` distinguishes user deposits from validator/refund returns [E2](https://bugbountylibrary.com/r/c4%3A2025-04-kinetiq%3AH-03.md). For anti-whale logic, confirm limits are global, not per fresh address [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md).

## Variants seen
- Sell branch auto-swaps 20% of the contract's own token balance into the pair, no slippage floor [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md)
- Transfer burns tokens from the LP pair on sell [E3](https://bugbountylibrary.com/r/dhl%3A20241117-mft.md)
- `receive()` re-stakes returned validator funds, inflating exchange ratio and blocking withdrawal confirmation [E2](https://bugbountylibrary.com/r/c4%3A2025-04-kinetiq%3AH-03.md)

## Impact and severity
High: E1 drained ~$243k USDT from a PancakeSwap pair; E3 drained pool liquidity via repeated transfers. E2 inflated the exchange ratio and locked users out of withdrawal confirmation. Severity rises when the hook is permissionless and repeatable, and when per-address guards let an attacker split holdings across many fresh addresses to bypass limits [E1](https://bugbountylibrary.com/r/dhl%3A20260604-atm.md).

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20260604-atm.md
- E2: https://bugbountylibrary.com/r/c4%3A2025-04-kinetiq%3AH-03.md
- E3: https://bugbountylibrary.com/r/dhl%3A20241117-mft.md
