# Stale checkpoint skips same-block accrual

Checkpoint reads from prior block or truncated window; same-block writes and loop branches skip accrual

_Area: DeFi accounting and math bugs. Seen in 3 disclosed reports; latest 2024-08-06. Updated 2026-10-24._

## How it works
Accounting that reads a checkpoint assumes the checkpoint reflects all writes up to now. That assumption breaks when a write and a read land in the same block, or when the read window is shorter than the position's lifetime. The delta is then computed from a prior-block snapshot or an extrapolated zero, so the running total (governance score, reward weight, time-weighted liquidity) drifts from the real contribution. Time-elapsed guards often skip the accrual path entirely when no block has passed, so the new write is never folded in [E1](https://bugbountylibrary.com/r/c4%3A2021-05-fairside%3AH-07.md).

## Where it shows up
- A delta computed via `getPrior...(user, block.number - 1)` while the current checkpoint may already hold a same-block write [E1](https://bugbountylibrary.com/r/c4%3A2021-05-fairside%3AH-07.md).
- Accrual loops with a `time == lastTime` or elapsed-guard branch that returns early, bypassing the update [E1](https://bugbountylibrary.com/r/c4%3A2021-05-fairside%3AH-07.md).
- Lookbehind windows (`MAX_NUM_WEEKS`) shorter than the max lock/vesting duration, so old positions extrapolate to zero [E2](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AM-02.md).
- Loop iterators where one branch recomputes the step and another leaves the initial `dt` [E3](https://bugbountylibrary.com/r/c4%3A2023-10-canto%3AM-02.md).
- Totals (TOTAL_GOVERNANCE_SCORE, relative weight) updated from a value that can differ from the per-user stored value.

## How to review
For each checkpoint read, ask: can a write to this value occur earlier in the same block? If yes, the read must use the current checkpoint, not `block.number - 1`. For each accrual loop, verify every branch sets the step from the actual accrued interval (`end - start`), not the loop's initial value [E3](https://bugbountylibrary.com/r/c4%3A2023-10-canto%3AM-02.md). For each lookbehind, confirm the window covers the maximum lock or vesting duration, or that the last known value is carried forward instead of extrapolated to zero [E2](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AM-02.md). Invariant: the sum of per-account deltas equals the stored total after any ordering of same-block operations.

## Variants seen
- Same-block score update plus balance drop reads stale prior-block score, inflating the total [E1](https://bugbountylibrary.com/r/c4%3A2021-05-fairside%3AH-07.md).
- Truncated lookbehind zeroes long-inactive nominees' weight and rewards [E2](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AM-02.md).
- Missing `dt` update in one exit branch skips accrual iterations [E3](https://bugbountylibrary.com/r/c4%3A2023-10-canto%3AM-02.md).

## Impact and severity
Inflated governance totals can distort quorum [E1](https://bugbountylibrary.com/r/c4%3A2021-05-fairside%3AH-07.md); silent zeroing strips long-term voters of power and rewards [E2](https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AM-02.md); under-counted time-weighted liquidity mispays rewards [E3](https://bugbountylibrary.com/r/c4%3A2023-10-canto%3AM-02.md). Severity rises when the drift feeds quorum or reward distribution, since it persists until the next checkpoint.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2021-05-fairside%3AH-07.md
- E2: https://bugbountylibrary.com/r/c4%3A2024-05-olas%3AM-02.md
- E3: https://bugbountylibrary.com/r/c4%3A2023-10-canto%3AM-02.md
