# Reentrancy in buy function for ERC777 tokens allows buying funds with considerable discount

Caviar Pair.buy transfers fractional tokens before pulling base tokens; an ERC777 base token hook re-enters buy repeatedly at stale reserves, then sells for profit.

- Payout: Contest reward
- Severity: high
- Program: Caviar
- Credited: carlitox477
- Disclosed: 2023-01-26
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-12-caviar-findings/issues/343
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-caviar%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:oracle-manipulation vuln:reentrancy

**Target:** Pair.sol AMM contract (buy/sell/add/remove) with ERC777 base token
**Root cause:** buy/sell update fractional token balance and transfer fractional tokens before the base token transferFrom/safeTransfer interaction, and buyQuote reads live reserves; an ERC777 tokensReceived hook re-enters before base token balance changes.
**Steps:**
1. Pair holds 1000 base (ERC777) and 1000 fractional tokens.
2. Attacker contract calls `buy(50, 80)`; buyQuote returns ~52 base tokens.
3. Pair `_transferFrom(address(this), msg.sender, 50)` sends fractional tokens, triggering ERC777 hook on attacker contract before base tokens are pulled.
4. Hook re-enters `buy(50, 80)` up to 6 more times; each quote uses unchanged baseTokenReserves (1000) but shrinking fractionalTokenReserves (950, 900, ...), so each buy is cheap.
5. Attacker accumulates 350 fractional tokens while base reserve still reads 1000.
6. Final hook step calls `sell(350, 442)`; sellQuote returns ~536 base tokens.
7. Attacker repays the ~442 base tokens owed across the buys, netting ~94 base tokens profit.
**How found:** Code reading of buy/sell ordering: fractional token transfer (effect) precedes base token transferFrom (interaction), and quotes read live reserves, so an ERC777 callback between them sees stale base reserves.
**Exploitation:** Deploy malicious contract implementing ERC777 tokensReceived hook with a counter; call buy, re-enter buy 6 times during fractional-token transfers, then sell accumulated fractional tokens to extract more base tokens than owed, repaying debt and keeping the surplus.
**Escalation:** Repeated re-entry during each hook multiplies discount; scaling iterations drains larger share of pair reserves.
**Insight:** When auditing AMMs, check whether quote functions read live reserves while effects (share/fractional balance updates) happen before the asset pull; any token with transfer callbacks (ERC777, ERC1363, hooks) turns that ordering into a re-entrant discount loop.
