# One-directional checkpoint accounting drift

Checkpoint or sync logic that adjusts a total only upward, or caches staleness, leaving phantom capacity

_Area: DeFi accounting and math bugs. Seen in 2 disclosed reports; latest 2026-05-18. Updated 2026-10-25._

## How it works
A checkpoint or sync routine reconciles a stored total (effective balance, bond capacity, redemption weight, eligibility cache) with reality. The bug is that reconciliation handles only one direction of the delta: it adds when the real value exceeds the prediction, but never subtracts when the prediction overshot. The stored total then diverges from reality and stays there. The broken assumption is that the correction is symmetric or that the "must always hold" inequality is guaranteed. When that assumption fails, phantom capacity persists until something else consumes it.

## Where it shows up
- Pre-credit of a predicted maximum followed by a settlement branch guarded by `actual > predicted` with no `else` to subtract over-credit [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)
- Developer comments asserting an inequality "has to always be true" [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)
- Sentinel/infinite values written into accumulator state and never cleared [E2](https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.7.md)
- Cached "already up to date" flags that skip recomputation after another function mutates the underlying data [E3](https://bugbountylibrary.com/r/c4%3A2024-07-loopfi%3AM-30.md)
- Reordered spec steps where a stateful function with hysteresis/thresholds runs twice instead of once [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)

## How to review
Identify every stored total that is reconciled against a computed value. The invariant: after any checkpoint, the stored total must equal the freshly computed value in both directions. Check that each settlement branch has a matching decrement path, that sentinels are reset before reuse, and that every mutator of cached data either updates the cache or invalidates it. For reordered logic, verify the stateful function is idempotent or called exactly once.

## Variants seen
- Upward-only settlement of predicted bond capacity at inflation-decreasing boundaries [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)
- Saturation sentinel never reset, over-crediting later partial redemptions [E2](https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.7.md)
- Stale eligibility cache after an admin function zeroes balances outside the cache path [E3](https://bugbountylibrary.com/r/c4%3A2024-07-loopfi%3AM-30.md)
- Double application of a hysteresis function producing a 63 vs 64 ETH divergence [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)

## Impact and severity
Phantom capacity can be minted or over-credited [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md), user debt wrongly reduced [E2](https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.7.md), rewards lost [E3](https://bugbountylibrary.com/r/c4%3A2024-07-loopfi%3AM-30.md), and in consensus code a balance divergence that can chain-split [E1](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md). Severity rises with how much phantom value is spendable and how long the drift persists before detection.

## Why fixes fail
Fixes that only add the missing decrement branch can still miss other mutators of the same state; the sentinel and cache variants show the correction must cover every path that changes the underlying value, not just the one branch [E2](https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.7.md)[E3](https://bugbountylibrary.com/r/c4%3A2024-07-loopfi%3AM-30.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md
- E2: https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.7.md
- E3: https://bugbountylibrary.com/r/c4%3A2024-07-loopfi%3AM-30.md
