# Stale reserves reused after swap

Pricing, APR or share math reading reserves cached before a swap instead of re-deriving from updated state

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

## How it works
A protocol computes a price, rate or share value from pool reserves (or virtual reserves). A swap, mint or burn then changes those reserves. If a later computation reuses the pre-change value — a cached variable, an old virtual reserve, or a reserve that the path never debited — the output no longer matches the pool's real state. The broken assumption is that "the reserve I read is the reserve that now exists." Any path that consumes inventory without updating the reserve it prices against leaves a gap between quoted value and actual backing.

## Where it shows up
- One pricing helper (e.g. `calcSwapOutput`) called from both swap and issuance/mint paths, where only swap debits the reserve [E1](https://bugbountylibrary.com/r/c4%3A2021-07-spartan%3AH-11.md).
- Post-swap math doing linear arithmetic on an old virtual reserve (`oldVirtual ± amount`) instead of recomputing from new real reserves [E3](https://bugbountylibrary.com/r/cantina%3Aterm_max_december2024%3A3.1.6.md).
- Externally callable `swapAndLiquify`-style functions that move reserves and can be triggered to distort pair accounting [E2](https://bugbountylibrary.com/r/dhl%3A20221129-mbc-zzsh.md).
- Custom burn/swap hooks that read pair reserves or `totalSupply` and can be iterated against the token's own pair [E4](https://bugbountylibrary.com/r/dhl%3A20230712-wgpt.md).
- Any APR, fee, reward or share calculation fed reserves captured before the state-changing call.

## How to review
The invariant: every value used for pricing, APR or shares must be derived from reserves as they exist after the last state change on that path. For each function, trace which reserves it reads and confirm the preceding swap/mint/burn actually wrote them. Check that issuance paths debit the same reserve that swap paths do [E1](https://bugbountylibrary.com/r/c4%3A2021-07-spartan%3AH-11.md), and that virtual reserves are recomputed from updated real reserves rather than adjusted arithmetically [E3](https://bugbountylibrary.com/r/cantina%3Aterm_max_december2024%3A3.1.6.md). For token hooks, confirm reserve reads cannot be driven by an external caller in a loop [E2](https://bugbountylibrary.com/r/dhl%3A20221129-mbc-zzsh.md)[E4](https://bugbountylibrary.com/r/dhl%3A20230712-wgpt.md).

## Variants seen
- Issuance priced off an AMM curve without debiting the reserve, making mint cheaper than swap [E1](https://bugbountylibrary.com/r/c4%3A2021-07-spartan%3AH-11.md).
- Post-swap APR from stale virtual reserves, corrupting the rate on every swap [E3](https://bugbountylibrary.com/r/cantina%3Aterm_max_december2024%3A3.1.6.md).
- Liquidity/burn hooks manipulating pair reserves to extract value [E2](https://bugbountylibrary.com/r/dhl%3A20221129-mbc-zzsh.md)[E4](https://bugbountylibrary.com/r/dhl%3A20230712-wgpt.md).

## Impact and severity
Arbitrage against mispriced issuance [E1](https://bugbountylibrary.com/r/c4%3A2021-07-spartan%3AH-11.md), corrupted market APR on every swap [E3](https://bugbountylibrary.com/r/cantina%3Aterm_max_december2024%3A3.1.6.md), and pool drainage via flash loans [E2](https://bugbountylibrary.com/r/dhl%3A20221129-mbc-zzsh.md)[E4](https://bugbountylibrary.com/r/dhl%3A20230712-wgpt.md) (one case ~$80k). Severity rises when the stale value is exploitable repeatedly or atomically within one transaction.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2021-07-spartan%3AH-11.md
- E2: https://bugbountylibrary.com/r/dhl%3A20221129-mbc-zzsh.md
- E3: https://bugbountylibrary.com/r/cantina%3Aterm_max_december2024%3A3.1.6.md
- E4: https://bugbountylibrary.com/r/dhl%3A20230712-wgpt.md
