# Unbounded enumeration gas exhaustion

Accounting maps that zero balances instead of removing keys, then get iterated in payout loops

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

## How it works
Accounting code assumes the set of participants it iterates stays small, and that an entry stops mattering once its balance hits zero. Both assumptions break. A seller's balance is set to 0 but the key is never removed from an enumerable map, so every historical participant is still visited on each payout loop [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md). The cost of one call grows with data the attacker (or ordinary usage) accumulates over time, until the call no longer fits in a block and the function reverts permanently.

## Where it shows up
- EnumerableMap/EnumerableSet accounting where the exit path zeroes a balance but never calls `.remove()` [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md)
- Payout, reward, or liquidation loops that call `.at(i)` or iterate a full key set [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md)
- Any per-call loop whose length is set by user actions over time, not by a constant
- Buy/add paths that insert keys while sell/exit paths only update values [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md)

## How to review
For each loop, ask what bounds its length and who can add entries. The invariant: every entry that can be added must be removable, and removal must happen on the exit path, not just zeroing [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md). Check that payout loops are bounded by a constant or paginated, not by total historical participants. Trace the sell/withdraw branch and confirm it deletes the key, not merely sets the balance to 0 [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md).

## Variants seen
- Zeroed-but-retained map entries bloating a distribution loop until it exceeds the block gas limit [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md)

## Impact and severity
Rewards distribution froze entirely once the map grew large enough [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md). Severity is high because the failure is a permanent denial of a core function, and it worsens as normal usage accumulates entries, so the bug surfaces only after the contract has been live for a while [E2](https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md).

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-02-gmx%3AH-8.md
- E2: https://bugbountylibrary.com/r/c4%3A2024-08-phi%3AH-03.md
- E3: https://bugbountylibrary.com/r/h1%3A2412583.md
