# Reward eligibility missing time weight

Reward score keyed to a flag, count or array length instead of actual time-weighted stake

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

## How it works
Reward accounting assumes that eligibility or score reflects how much stake was held for how long. The code instead keys eligibility to a boolean flag, an array length, a loop count, or an uninitialized time slot. When that proxy is wrong or attacker-controlled, a user collects rewards they never earned, or stops earning despite holding stake. The broken assumption: "this flag/count/slot already encodes time-weighted stake." It does not.

## Where it shows up
- Checkpoint or bitmap writes that read the slot being created instead of the last written one (index `n` vs `n-1`) [E2](https://bugbountylibrary.com/r/c4%3A2022-05-velodrome%3AH-03.md).
- Time-indexed accounting (bitmaps, TWAP slots, checkpoints) where an event time equals `block.timestamp` and a branch skips initialization [E1](https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md).
- Loop bounds or battle/round counts taken from user-supplied bytes or array length rather than a fixed constant [E3](https://bugbountylibrary.com/r/c4%3A2025-04-virtuals-protocol%3AM-11.md).
- Staking `invest`/`deposit` accepting user `qty` and `end_date` with no bounds check [E4](https://bugbountylibrary.com/r/dhl%3A20230117-omniestate.md).
- `earned()` or reward math gated on a stored flag rather than accumulated time-weighted balance [E2](https://bugbountylibrary.com/r/c4%3A2022-05-velodrome%3AH-03.md).

## How to review
For each reward path, state the invariant: accrued reward equals sum over time of (eligible stake x rate), and eligibility at time t depends only on stake held at t. Then check:
- Every checkpoint copy reads the previous written entry, not the new empty slot; mappings return defaults silently [E2](https://bugbountylibrary.com/r/c4%3A2022-05-velodrome%3AH-03.md).
- Every time-indexed structure registers the event time even when it equals the current block time; test the `==` boundary [E1](https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md).
- Every count or loop bound that affects score is a fixed constant or validated against the spec [E3](https://bugbountylibrary.com/r/c4%3A2025-04-virtuals-protocol%3AM-11.md).
- Every stake input has lower and upper bounds, and zero/epoch timestamps are rejected [E4](https://bugbountylibrary.com/r/dhl%3A20230117-omniestate.md).

## Variants seen
- Uninitialized time slot makes historical rewards claimable by a new order [E1](https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md).
- Off-by-one checkpoint copy freezes a user's accrual [E2](https://bugbountylibrary.com/r/c4%3A2022-05-velodrome%3AH-03.md).
- Attacker-controlled array length inflates a score [E3](https://bugbountylibrary.com/r/c4%3A2025-04-virtuals-protocol%3AM-11.md).
- Missing input validation lets a minimal stake claim outsized rewards [E4](https://bugbountylibrary.com/r/dhl%3A20230117-omniestate.md).

## Impact and severity
Drains shared reward pools or freezes user rewards; E4 netted ~236 BNB. Severity rises when the pool is shared and the flaw is reachable by any user with a minimal stake or a single call.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2025-11-ekubo%3AM-02.md
- E2: https://bugbountylibrary.com/r/c4%3A2022-05-velodrome%3AH-03.md
- E3: https://bugbountylibrary.com/r/c4%3A2025-04-virtuals-protocol%3AM-11.md
- E4: https://bugbountylibrary.com/r/dhl%3A20230117-omniestate.md
