Reward-Debt Desynchronization / Denominator-Collapse Reward Amplification
- Name: reward-debt desynchronization / denominator-collapse reward amplification
- Category: exploit pattern / incentive-accounting failure / reward-distributor exploit / stale-accumulator bug
- Summary: This pattern hits staking, farming, and liquidity-mining systems that distribute emissions through a global
rewardPerShare-style accumulator plus per-user reward debt, writeoff, or checkpoint state. The dangerous combination is: the global accumulator updates from one view of state, the user’s debt settles against a different or stale view, and the denominator used for the global update can be driven close to zero. Once the pool’s reward accumulator is divided across dust-sized stake, even a tiny desynchronization betweenglobal reward truthanduser reward cancellationcan become an enormous mint primitive. The exploit is not simplytiny pool gets high APR; it isthe protocol lets a near-zero denominator amplify a stale-accounting mismatch into real token issuance. - What it is:
- A reward pool tracks a global accumulator such as
accRewardsPerToken,rewardPerTokenStored, or another per-share emission index. - Each user also carries reward debt, writeoff, or snapshot state intended to cancel rewards already accounted for.
- The contract updates the global accumulator from one state version, but updates the user’s debt from stale memory, an old checkpoint, or a mismatched denominator context.
- The attacker makes total stake tiny so the next global accumulator jump becomes enormous.
- Because the user-side offset no longer matches that jump, the next claim or withdrawal mints rewards far beyond scheduled emissions.
- A reward pool tracks a global accumulator such as
- Typical preconditions:
- The target pool is fresh, thin, or manipulable enough that one actor can reduce effective total stake toward zero.
- Rewards are minted or made claimable from a global per-share accumulator rather than distributed from a hard-capped fixed balance in the same transaction.
- The code path updates global pool state and user debt in different places or from different cached objects.
- No hard bound checks whether claimable rewards exceed plausible emissions for the elapsed time.
- The attacker can re-enter with a large deposit after first pinning the denominator to dust.
- Common vulnerable surfaces:
- liquidity-mining or shield-mining contracts with
accRewardsPerToken+rewardDebt/rewardWriteoff - new staking pools added before healthy stake exists
- gas-optimized contracts that cache pool structs in memory, then later mix those stale values with freshly written storage
- reward distributors where
updatePool()andsettleUser()are separate steps that can accidentally see different pool states - systems that assume
tiny denominatoris just a UX or APR oddity rather than a security boundary
- liquidity-mining or shield-mining contracts with
- Exploit sequence:
- The attacker finds or creates a pool with extremely small total stake, often by targeting a newly added pool or withdrawing until only dust remains.
- They trigger a global reward update while that denominator is tiny, causing the pool’s per-share accumulator to jump sharply.
- Because the contract still uses stale or mismatched accumulator state for the user’s reward debt, the user-side cancellation lags behind the pool-side reality.
- The attacker restores or keeps a large nominal stake associated with the under-cancelled reward position.
- On claim, withdraw, or later settlement, the contract treats the gap between the huge live accumulator and the stale user writeoff as legitimate claimable rewards.
- The protocol mints or transfers rewards far beyond intended emissions and the attacker sells or otherwise monetizes the newly created tokens.
- Variants:
- Stale memory pool cache — a
Pool memorystruct is loaded beforeupdatePool()mutates storage, but user debt later uses the stale memory copy. - Checkpoint ordering bug — user reward debt is written before the final pool accumulator update for the same action.
- Launch-state amplifier — a newly listed pool with almost no honest stake makes a modest accounting mismatch become catastrophic.
- Dust-denominator capture — the attacker withdraws to
1 weior another microscopic balance so the next accumulator update explodes. - Soft-mitigated but still live — operators add keeper updates or UI-side extra calls, but the true stale-state mismatch remains exploitable onchain.
- Stale memory pool cache — a
Three reward-authority promotions reviewers should not collapse
-
The pool’s accumulator moved under a dust denominator
- Some elapsed-emissions interval, pool-weight change, or reward-update call advanced
accRewardsPerToken/rewardPerSharewhile effective stake was tiny. - This only answers the weakest question:
pool-side reward state changed.
- Some elapsed-emissions interval, pool-weight change, or reward-update call advanced
-
That pool-side update was treated as the same state the user’s debt had already canceled against
- The protocol had to decide whether the user’s
rewardDebt,rewardWriteoff, or checkpoint was being advanced from that exact post-update accumulator version. - This is the decisive accounting question preserved by this family:
did pool-side reward truth and user-side cancellation truth still describe the same moment once the denominator spike had happened?
- The protocol had to decide whether the user’s
-
The resulting mismatch became live payout or mint authority
- Claim, withdraw, or later settlement paths then treated the uncancelled gap between the huge live accumulator and the stale user checkpoint as money-good reward entitlement.
- This is where the bug cashes out: a dust-denominator update that should only have changed internal accounting becomes direct token-issuance or reward-payout authority.
Healthy review keeps those promotions separate instead of flattening the bug into tiny pool paid too much APR.
- Detection heuristics:
- Map every reward path and verify that
global accumulator used for claimanduser debt written this actioncome from the exact same state version. - Fuzz deposit / withdraw / deposit / claim sequences where total stake temporarily falls to
1 wei,1e-8, or other dust values. - Compare total claimable rewards against theoretical emissions under adversarial low-liquidity states; if one user can claim more than elapsed emissions should allow, stop.
- Audit gas optimizations that cache pool structs in memory before calling functions that mutate storage.
- Treat pool launch state as adversarial. A new pool with nearly zero stake is a security boundary, not only an APR oddity.
- Ask whether a user can first minimize the denominator, then re-expand their position before the claim path settles the true reward debt.
- Map every reward path and verify that
- Defenses:
- Write user reward debt only from the same post-update accumulator that the pool will later use for claims.
- Fail closed or cap accrual when total stake is below a meaningful threshold instead of letting
reward / dustcreate enormous per-share jumps. - Seed new reward pools with non-burnable stake or delay activation until healthy participation exists.
- Add invariant tests that
sum(claimable rewards)cannot exceed plausible emissions plus any explicitly prefunded remainder. - Avoid mixing cached
memorypool structs with later storage writes unless the code deliberately refreshes the cache before any user accounting. - Consider pausing or rate-limiting claims if observed accumulator growth exceeds physical emission bounds.
What sound design looks like
- A healthy reward system keeps three states separate on purpose:
the pool accumulator moved,the user-side debt or writeoff was reconciled against that exact post-update state, andthe remaining delta is now payout authority. This family appears when those promotions collapse into one vague assumption that a huge pool-side update and a stale user checkpoint are close enough to mean the same thing. - Reward accounting has one canonical post-update state per action: the same accumulator value, denominator, and elapsed-emission view are used both to advance global pool state and to write each user’s debt or checkpoint.
- Pools cannot enter a security-sensitive
reward / dustregime without explicit handling. If effective stake falls too low, accrual pauses, caps, or routes through a protected bootstrap mode rather than letting tiny denominators amplify tiny accounting slips into mint authority. Fresh-market seeding and activation discipline and bootstrap-state irreversibility and supply-floor design are the broader positive-design companions for keeping launch-state and low-supply regimes from becoming attacker-owned accounting surfaces. - Emission logic is bounded by physical reality. A single claim path cannot mint more than scheduled emissions plus clearly prefunded remainder, even if accounting state becomes adversarial.
- Reward entitlements and reward accumulators should still collapse into one consumption story: the same state transition that recognizes pool-side reward growth should also leave user-side debt, writeoff, or claimed state in the canonical post-update frame before any later claim can treat the balance as real. Reward entitlement identity and batch canonicalization is the adjacent design note for the broader
one canonical reward right, one consume boundarydiscipline. - Gas optimizations that cache pool structs in memory are subordinate to accounting truth: stale cache reuse is never allowed to decide user debt after storage mutation.
Failure-prevention checklist
- Write user reward debt from the exact post-update accumulator value that storage will preserve for later claims; never settle user debt from a pre-update or stale cached pool snapshot.
- Enforce denominator floors, bootstrap seeding, or paused accrual when total stake is too small for
rewardPerSharejumps to stay economically safe. - Add invariants that aggregate pending rewards cannot exceed elapsed emissions plus any explicitly prefunded budget.
- Fuzz adversarial sequences where stake collapses to dust, then expands again before claim or withdraw settlement.
- Review every
Pool memory/storagemix for stale-state reuse afterupdatePool(),massUpdatePools(), or any helper that mutates the accumulator. - Treat newly launched or newly reactivated pools as hostile environments until they have healthy seed liquidity and tested reward-debt initialization.
- Consider rate limits, mint ceilings, or pause conditions when observed per-block accumulator growth materially exceeds design expectations.
Audit prompts
- Does every claim, deposit, withdraw, and harvest path write user debt from the same accumulator version that the pool will later use to compute rewards?
- What happens if total stake temporarily becomes
1 wei, one LP token, or another near-zero denominator right beforeupdatePool()? - Can an attacker minimize the denominator, trigger a huge accumulator jump, then restore a large position before user debt is brought current?
- Are any cached pool structs read before a storage mutation and then reused afterward for reward-debt settlement?
- Is there a hard cap or invariant that stops one user from claiming more than plausible elapsed emissions?
- How are fresh pools seeded, and what prevents launch-state dust from becoming a mint primitive before honest liquidity arrives?
- If keepers or UI workflows are part of the mitigation story, does the onchain contract remain safe when those offchain habits disappear?
Lessons that should already be standard
- Near-zero denominators are security boundaries, not just weird APR moments.
- Global accumulator math and user reward-debt cancellation are one invariant. If they read different state versions, the protocol is minting against a contradiction.
- Gas-saving memory caches deserve the same suspicion as arithmetic code in emission systems; stale reads can become unlimited issuance bugs.
- Reward systems should be judged against emission reality, not against whether the accumulator equations
look standardin isolation. - Launching a reward pool before safe seed state exists is often equivalent to launching an exploit amplifier.
Where this pattern stops
- Use reward-index-initialization-failure when the sharper bug is first-touch or zero-sentinel reward-index setup, rather than a live pool whose global accumulator and user debt later settle from different state versions.
- Use reward entitlement identity and batch canonicalization when the main failure is which reward right was being consumed — duplicate epochs, counterfeit staking contracts, or batch-claim source drift — rather than denominator-amplified stale accumulator state.
- Use supplier-index-inflation when the reusable lesson is the lending-market cousin: a global supplier index or exchange-rate-like scaler gets monopolized under tiny supply and later converts into collateral or borrow power rather than incentive-token issuance.
- Use fresh-market seeding and activation discipline when the main design problem is that a newly launched or effectively fresh pool should never have entered an attacker-ownable dust state in the first place.
- Use reward-recognition, presentation, and collateral-admission separation or sync, harvest, and cache-write authority discipline when the sharper question is not stale reward debt but who was allowed to publish pending or attacker-timed value into canonical share or collateral truth.
How to use this note with the exploit corpus
-
Start here when the question is
did the protocol let pool-side reward truth and user-side reward-cancellation truth settle from different state versions, especially after the denominator was driven toward dust? -
Pair with reward entitlement identity and batch canonicalization when the next question is how the protocol should have kept reward rights, claim-source provenance, and consumed-state updates canonical across single and batch claim paths.
-
Pair with fresh-market seeding and activation discipline and bootstrap-state irreversibility and supply-floor design when the sharper lifecycle question is why the pool was allowed to become or remain economically dust-sized at all.
-
Pair with supplier-index-inflation when the analytical value comes from comparing reward-accumulator monopolization against lending-index monopolization under the same low-denominator conditions.
-
Pair with reward-recognition, presentation, and collateral-admission separation when the reward-like value later escaped into wrapper pricing, rebasing balances, or borrowable collateral rather than staying inside a pure farming payout rail.
-
High-signal incidents:
- [[../../../incidents/accounting-issuance-claims/cover-protocol-blacksmith-stale-pool-cache-rewardwriteoff-desynchronization-infinite-cover-mint-2020-12-28|Cover Protocol Blacksmith stale-pool-cache
rewardWriteoffdesynchronization / infinite COVER mint (2020-12-28)]] — the clearest preserved case: a fresh mining pool was driven to1 wei,updatePool()raised storageaccRewardsPerTokenagainst that dust denominator, anddeposit()still computed the attacker’srewardWriteofffrom a stale cached accumulator, leaving the next claim free to mint tens of quintillions of COVER.
- [[../../../incidents/accounting-issuance-claims/cover-protocol-blacksmith-stale-pool-cache-rewardwriteoff-desynchronization-infinite-cover-mint-2020-12-28|Cover Protocol Blacksmith stale-pool-cache
Internal linkages
- Best current exemplar: cover-protocol-blacksmith-stale-pool-cache-rewardwriteoff-desynchronization-infinite-cover-mint-2020-12-28.
- Canonical reward-right companion: reward-entitlement-identity-and-batch-canonicalization — useful when the sharper design question is what exact reward entitlement object should have been authenticated, consumed, and made idempotent before any payout was counted.
- Fresh-state lifecycle companion: fresh-market-seeding-and-activation-discipline — useful when the deeper fix is to stop reward pools or wrapper paths from ever reaching attacker-owned dust state during launch or reactivation.
- Low-supply hardening companion: bootstrap-state-irreversibility-and-supply-floor-design — useful when the real control surface is making the denominator floor irreversible rather than merely hoping keepers keep the pool non-empty.
- Launch-state negative companion: fresh-market initialization failure — use that note when the most reusable lesson is
new pool launched before safe seed state existedrather than the specific reward-accounting desynchronization. - Lending analogue: supplier-index inflation — both families let an attacker monopolize a global scaler under dust-supply conditions, but here the scaler mints incentive rewards rather than borrowable collateral.
- Reward-publication companion: reward-recognition-presentation-and-collateral-separation and sync-harvest-and-cache-write-authority-discipline — useful when reward-like state later becomes share-price, cached-NAV, or collateral truth rather than a direct farming-claim mint.
- Adjacent pattern: first-deposit inflation — another family where low-liquidity bootstrap state turns a tiny initial position into disproportionate economic control, though the actual accounting bug is different.
Control surface
-
The leverage sits in pool launch state, denominator floor assumptions, accumulator update ordering, cache freshness, user reward-debt settlement, and whether token mint authority is bounded by emissions reality.
-
A reward distributor becomes dangerous when
who owns the dust denominatorandwhich accumulator version user debt trustsstop being the same security question. -
Sources:
-
Last reviewed: 2026-08-01 UTC