# Transfer path double-counts tokens

Reconcile every mint destination and transfer/skim/burn path against the contract's actual token balance

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2025-02-18. Updated 2026-10-29._

## How it works
A contract assumes a token balance it does not actually hold, or counts the same tokens more than once as they move through transfer, skim, or burn paths. The broken assumption is "the tokens I account for are the tokens I custody." When minting, vesting, or fee logic routes tokens somewhere other than the assumed holder, or when an internal burn/mint re-enters the same accounting hook, balances and totalSupply drift from reality. Later transfers then either revert (insufficient balance) or release more value than intended.

## Where it shows up
- Migration/launch code that transfers `totalSupply - numTokensToSell` while vesting or airdrop mints tokens to the token contract itself [E1](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.1.2.md)
- Token `transfer` logic that interacts with LP pool `skim`/`sync`, where a direct transfer to the pool can be counted twice [E2](https://bugbountylibrary.com/r/dhl%3A20250121-ast.md)
- `_update` hooks that apply fees, combined with internal `_burn`/`_mint` that route back through `_update` [E3](https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md)
- Any place a contract computes an amount from `totalSupply` or a stored balance instead of `balanceOf(address(this))`

## How to review
Reconcile every mint destination against every later balance assumption. The invariant: for each transfer/skim/migrate/burn path, the amount moved must be backed by tokens actually held by the sender, and each token must be counted exactly once per operation. Check that internal `_burn`/`_mint` do not re-enter fee logic. Check that skim/sync accounting cannot be inflated by a plain transfer. Confirm migration transfers use real balances, not derived totals.

## Variants seen
- Vesting mints to token contract, migration assumes custody it lacks, reverts and strands funds [E1](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.1.2.md)
- Transfer logic double-counts LP tokens during skim, pool releases excess [E2](https://bugbountylibrary.com/r/dhl%3A20250121-ast.md)
- Burn fee recurses through `_update`, over-burning totalSupply and over-charging users [E3](https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md)

## Impact and severity
Stranded funds and impossible migration [E1](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.1.2.md); ~65K USD drained via flash loan and price manipulation [E2](https://bugbountylibrary.com/r/dhl%3A20250121-ast.md); silently corrupted totalSupply and downstream mint math [E3](https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md). Severity rises when the drift is silent (no revert) and when a public skim/swap path lets an attacker trigger it.

## Evidence
- E1: https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.1.2.md
- E2: https://bugbountylibrary.com/r/dhl%3A20250121-ast.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2025-01-peapods-finance%3AM-11.md
