# Stale global total accounting drift

A balance moves on one path while its matching global total, checkpoint, or sync hook is skipped

_Area: DeFi accounting and math bugs. Seen in 125 disclosed reports; latest 2026-06-15. Updated 2026-10-23._

## How it works
Contracts keep two layers of accounting: per-user or per-ledger balances, and a global total (totalSupply, totalAssets, totalDebt, circSupply, idleETH, effectiveBond, or a second ledger like EVM StateDB vs bank). The invariant is that the global total equals the sum of per-user balances plus any documented exclusion. A path mutates one layer and not the other, so the total drifts from reality. Downstream math (share price, utilization, interest, pro-rata redemption, reward accrual) then computes against a number that no longer reflects holdings, and value is minted, inflated, or stranded.

## Where it shows up
- Paired deposit/withdraw or join/leave functions where only one side updates totals [E4](https://bugbountylibrary.com/r/c4%3A2022-07-fractional%3AH-10.md)
- Balance-derived functions with a subtraction term (idleETH) where an asset-moving path skips the term [E3](https://bugbountylibrary.com/r/c4%3A2022-11-stakehouse%3AH-10.md)
- Loops or handlers that filter by message/order kind and skip supply updates for filtered kinds [E5](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-02.md)
- Accumulators using a per-record timestamp where an early-continue skips the timestamp write [E2](https://bugbountylibrary.com/r/sherlock%3A2023-02-union%3AH-2.md)
- Pre-credit/reconcile logic that only corrects in one direction [E7](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)
- Two ledgers bridged by a sync hook that only some balance mutators call [E1](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AH-03.md)
- claim()/mint() pairs where mint pricing is not recomputed after claim [E6](https://bugbountylibrary.com/r/dhl%3A20260615-thetanuts.md)
- A tracked token address swapped (upgrade) while totals still reference the old token [E8](https://bugbountylibrary.com/r/c4%3A2025-08-flare-fasset%3AM-03.md)

## How to review
For every function that changes a user balance or moves the underlying asset, list the global variables it must update and confirm each is updated on every path, including early returns, refunds, and filtered branches. Grep for setters and writers of totalSupply/totalAssets/totalDebt/circSupply/idleETH and for `return`/`continue` before accounting writes. For cross-ledger designs, enumerate every mutation of the underlying balance set and confirm each calls the sync hook [E1](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AH-03.md). The invariant: global total == sum of per-user balances (+ documented exclusions), holding before and after every external call. Diff paired functions line by line. Check both directions of any prediction/reconciliation delta. Confirm the exclusion term is symmetric across every asset-moving path. Distinguish transient drift (corrected later in the same tx) from persistent drift (survives to the next read).

## Variants seen
- Refund path omits total decrement, enabling join+withdraw inflation [E4](https://bugbountylibrary.com/r/c4%3A2022-07-fractional%3AH-10.md)
- Asset returned to pool but excluded term not incremented, overstating rewards [E3](https://bugbountylibrary.com/r/c4%3A2022-11-stakehouse%3AH-10.md)
- Filtered message kind still carries value, supply never reduced [E5](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-02.md)
- Early-continue skips timestamp refresh, inflating an accumulator [E2](https://bugbountylibrary.com/r/sherlock%3A2023-02-union%3AH-2.md)
- One-directional reconciliation leaves phantom capacity [E7](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md)
- Staking/multisend paths skip StateDB sync, desyncing two ledgers [E1](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AH-03.md)
- Claim releases components without decrementing totalAssets, so remint is cheap [E6](https://bugbountylibrary.com/r/dhl%3A20260615-thetanuts.md)
- Token address upgrade orphans rewards from totalCollateral [E8](https://bugbountylibrary.com/r/c4%3A2025-08-flare-fasset%3AM-03.md)

## Why fixes fail
Fixes that patch one path leave sibling paths (refund vs deposit, Call vs non-Call, staking vs transfer) inconsistent; the invariant must be enforced across all paths, not the reported one.

## Impact and severity
Led to theft of pool ETH, inflated supply, phantom mintable capacity, and overstated rewards [E1](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AH-03.md)[E3](https://bugbountylibrary.com/r/c4%3A2022-11-stakehouse%3AH-10.md)[E4](https://bugbountylibrary.com/r/c4%3A2022-07-fractional%3AH-10.md)[E5](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-02.md)[E7](https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md). Severity rises when drift is repeatable in a loop or affects share pricing, letting an attacker amplify rather than merely misreport; a claim/remint cycle drained 105k USDC [E6](https://bugbountylibrary.com/r/dhl%3A20260615-thetanuts.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AH-03.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2023-02-union%3AH-2.md
- E3: https://bugbountylibrary.com/r/c4%3A2022-11-stakehouse%3AH-10.md
- E4: https://bugbountylibrary.com/r/c4%3A2022-07-fractional%3AH-10.md
- E5: https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-02.md
- E6: https://bugbountylibrary.com/r/dhl%3A20260615-thetanuts.md
- E7: https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AM-09.md
- E8: https://bugbountylibrary.com/r/c4%3A2025-08-flare-fasset%3AM-03.md
