# Invariant rounds to zero for tiny reserves

Fixed-point division in stable-curve or share math truncating to zero at small reserves, disabling checks

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

## How it works
Stable-curve and share math scales values by a fixed factor (e.g. 1e18) and divides. When the numerator is smaller than the divisor, integer division truncates to zero. The invariant or exchange rate then becomes zero, and any check that compares against it passes trivially. The safety property the code assumes — that the invariant is a meaningful positive number — silently stops holding for tiny reserves.

## Where it shows up
- Stable-curve `_k()` or invariant functions with `(_x*_y)/1e18`-style fixed-point division.
- Swap or mint paths that compare against the invariant without a minimum-reserve or minimum-k guard.
- First-LP / initial-mint paths with no minimum liquidity requirement.
- 1:1 wrap/unwrap or linear-pool math where a 1-wei trade deducts less than the asset is worth [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-balancer-rounding-error.md).
- Exchange rate computed as balance/supply where balance can drop without supply changing [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-balancer-rounding-error.md).

## How to review
For every fixed-point division, ask: can the numerator be smaller than the scale factor? If yes, the result is zero. Check that the invariant is required to be strictly positive (or above a floor) before any swap, mint, or rate calculation relies on it. For first-LP paths, confirm a minimum liquidity or minimum-k guard exists. For wrap/unwrap pools, verify that a 1-wei trade moves value in the correct direction and that rate cannot fall while supply is unchanged [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-balancer-rounding-error.md). The invariant to hold: for all reachable reserve states, the computed invariant is positive and monotonic under trades.

## Variants seen
- Linear/ERC4626 pool: 1-wei wrap/unwrap rounds in the trader's favor, draining 1 wei per flash swap [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-balancer-rounding-error.md).
- Stable pair: `_k()` truncates to zero for tiny reserves, swap check vacuous, first LP drains and inflates totalSupply to block future mints [E2](https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AM-1.md).
- Same stable-pair truncation, exploited via mint/drain loop to DOS the pool [E3](https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-06.md).

## Impact and severity
Drain of boosted pools with no capital via repeated flash swaps [E1](https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-balancer-rounding-error.md); pool drain plus permanent DOS by inflating totalSupply [E2](https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AM-1.md)[E3](https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-06.md). Severity is high: the flaw is reachable by the first LP or a flash-swap attacker, needs no capital, and the zero invariant disables the check meant to prevent it. Worse when fees are zero and supply can be inflated cheaply.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-balancer-rounding-error.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2024-06-velocimeter%3AM-1.md
- E3: https://bugbountylibrary.com/r/c4%3A2025-05-blackhole%3AM-06.md
