# Reward math on stale totals

Reward and referral math reading lifetime, cancelled, or uninitialized totals instead of active state

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2026-01-12. Updated 2026-11-01._

## How it works
Reward logic assumes the number it reads represents *currently active* participation. When that number is actually cumulative, historical, or never initialized, the assumption breaks: a user can inflate or inherit rewards without holding the underlying stake. The invariant that should hold — rewards accrue only against live, correctly-initialized state — is violated because the code reads a proxy that never decrements or never gets set.

## Where it shows up
- Reward eligibility checks that test a threshold (votes, deposits) but don't filter terminal states (cancelled, vetoed, withdrawn).
- Counters named `total*` (totalInvested, lifetimeWithdrawn) feeding upline/team/referral logic.
- Time-indexed accounting (bitmaps, checkpoints, TWAP slots) where an event at `block.timestamp == startTime` falls into an `else` branch and skips initialization.
- `rewardRatesBeforeSlot[x]` left at 0, so `currentRate - 0` returns all history.
- Early-cancel or deposit/withdraw paths that let a user cycle state cheaply.

## How to review
For each reward input, ask: does this variable decrease when stake is removed or a proposal is cancelled? Trace every write. Check boundary conditions (`<` vs `<=`, `==` cases) on time-indexed slots to confirm initialization always runs. Confirm terminal states are excluded from eligible sets. The property: *reward weight must equal active, initialized participation at accrual time* — no path lets a user collect against state they no longer hold or never registered.

## Variants seen
- Cancelled proposals still counted eligible, enabling create-vote-cancel spam to steal rewards [E1](https://bugbountylibrary.com/r/sherlock%3A2024-03-nouns-dao-2%3AH-2.md).
- Instant order at `startTime` skips bitmap registration, draining historical rewards [E2](https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md).
- Referral counts use cumulative `totalInvested` including withdrawals, farmed via deposit/withdraw chains [E3](https://bugbountylibrary.com/r/dhl%3A20250813-grizzifi.md).

## Impact and severity
Direct theft of shared reward pools: 61k USD drained via referral farming [E3](https://bugbountylibrary.com/r/dhl%3A20250813-grizzifi.md), full historical reward capture [E2](https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md), reward theft from honest participants [E1](https://bugbountylibrary.com/r/sherlock%3A2024-03-nouns-dao-2%3AH-2.md). Severity high where pools are shared and cycles are cheap; worse when a single actor controls the threshold or can chain contracts.

## Why fixes fail
Filtering one terminal state (e.g. cancelled) without auditing other non-final states, or fixing the threshold check without ensuring the counter decrements on withdrawal, leaves the same stale-total read exploitable.

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2024-03-nouns-dao-2%3AH-2.md
- E2: https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md
- E3: https://bugbountylibrary.com/r/dhl%3A20250813-grizzifi.md
