Sync, Harvest, and Cache-Write Authority Discipline
- Name: sync, harvest, and cache-write authority discipline
- Category: vault reference / state-publication authority checklist / cache-timing boundary / downstream-solvency reference
- Summary: Many vault, wrapper, and AMM reserve-accounting failures do not need fake rewards or fake balances. They only need a public or weakly bounded helper that is allowed to crystallize attacker-timed state into canonical protocol truth.
syncRewards(),harvest(),checkpoint(),updateIndex(),cachePrice(),sync(),skim(),gulp(), and similar helpers are not harmless bookkeeping when they decide when pending value becomes canonical NAV,balanceOf, redemption truth, collateral-sensitive pricing, or reserve truth. Healthy systems treat these functions as publication-authority surfaces: they constrain who may call them, what inputs they may publish, how fast publication may move, when the new state becomes active, and which downstream systems may trust it. Just as importantly, they keeppublished locallyseparate fromalready active for every economically forceful consumer, and they treat cache-consumer fan-out as authority growth rather than as an implementation detail. - Why this note exists:
- reward-recognition-presentation-and-collateral-separation explains the timing-side state machine:
observed reward,recognized value,presentation truth, andcollateral truthshould often graduate at different speeds. This note exists for the narrower authority question that follows immediately: who gets to move value across those boundaries, with what effect limits? - GoGoPool, Zunami, Raft, Wise, Balancer STA, and Indexed preserve the same control-surface mistake in different wrappers: teams treat sync, cache, or reserve-refresh helpers as if they merely copy reality into storage, even though the caller is actually choosing the exact moment manipulable state becomes protocol truth.
- Zunami is also the useful reminder that cache fan-out is part of publication authority. A helper that looked like a gas optimization became much more dangerous once UZD
v1.1extended the cached mark intototalSupply,balanceOf, andallowance; the write was no longer just a pricing convenience, it had become wallet-balance truth. - The analytical win is to separate
the protocol had a rate or reserve statefromthe protocol let this caller publish that rate or reserve state, with this timing, into these downstream consumers.
- reward-recognition-presentation-and-collateral-separation explains the timing-side state machine:
The five authority boundaries a healthy design should keep explicit
-
Observation authority
- The system should distinguish
value may be observablefromvalue may be canonically published. - Anyone may be able to see a strategy gain, donation, rebasing balance increase, validator reward, or thin-market mark without anyone being entitled to finalize it into holder economics.
- If the protocol skips this distinction, every public sync helper quietly becomes a pricing oracle with user-triggered write access.
- The system should distinguish
-
Publication authority
- The protocol should know which actor or mechanism is allowed to turn pending value into canonical stored state.
Publicly callableis itself a policy choice, not the neutral absence of one.- When publication is public, safety must come from effect bounds, delayed activation, monotonic rules, or external canaries — not from wishful thinking that callers will only use the helper under benign conditions.
-
Effect-boundary authority
- The system should bound what one sync, harvest, checkpoint, or cache write is allowed to change.
- A single call should not be able to rewrite holder balances, cached NAV, redemption pricing, and cross-module collateral truth all at once unless the protocol can defend that as one safe economic state transition.
- If one helper can publish too much truth to too many consumers, the protocol has collapsed several distrust boundaries into one call edge.
-
Activation authority
- The protocol should distinguish
storedfromimmediately live everywhere. - A newly published rate may still need a delay, a smoothing window, a cap, a haircut, or a borrow-side quarantine before it becomes redemption truth,
balanceOftruth, or lending truth. - Same-block activation is often the hidden superpower, not the storage write by itself.
- The protocol should distinguish
-
Rollback and containment authority
- The system should know how an unsafe or stale publication is contained once written.
- Pauses, stale-flagging, bounded blast radius, canary mismatches, and downstream distrust defaults belong to the same control surface.
- If the only answer is
governance will notice later, then publication authority is already too broad for the damage it can do.
Three publication promotions reviewers should not collapse
-
Observed or manipulable state exists
- A balance change, thin-market mark, pending reward, reserve mismatch, or strategy-side value is visible somewhere.
- This is only an observation fact. It does not yet answer who may canonize it or which consumers deserve to trust it.
-
Canonical local publication exists
- A helper has written that observation into a local cache, index, reserve snapshot, NAV field, or share-accounting state the protocol now treats as canonical.
- This is already a stronger authority step than mere observation, but it is still not automatically proof that every downstream consumer should act on the freshest possible value.
-
Economically active downstream truth exists
- Redemptions,
balanceOf, flash loans, swaps, borrow power, liquidation, and other forceful consumers are now allowed to spend against the newly published state. - This still deserves a separate boundary.
Published nowis often not yet the same thing assafe to honor everywhere right now.
- Redemptions,
The recurring design target is to answer three separate questions cleanly: what state was visible?, what state did this helper publish as canonical locally?, and which consumers were actually entitled to trust that fresh publication immediately?
What sound design looks like
1. Publication should be narrower than observation
- The clean design question is not
can anyone call harvest or gulp?butwhat exact right does that helper exercise? - If the call merely realizes already hard-bounded internal accounting that no user-timed external state can influence, public invocation may be fine.
- If the call decides how much donated inventory, reward-side balance, thin-market valuation, pending strategy state, or reserve mismatch becomes canonical now, the protocol should treat it as a write-authority surface.
2. Public helpers need effect caps, not vibes
- Public
sync,checkpoint,skim, orgulphelpers are safest when one invocation can only move state within tightly defended bounds. - Useful caps include:
- maximum rate movement per call
- maximum cumulative movement per block or window
- monotonicity requirements tied to proven sources
- explicit floor and ceiling guards
- separate caps for presentation updates versus collateral-facing updates
- If the protocol cannot bound one publication step, it should not pretend that public invocation is operationally neutral.
3. Publication and activation should usually be different steps
- The strongest recurring fix is not always
make sync permissioned. - Often the better design is
allow publication, but delay or quarantine its dangerous consequences. - The key distinction is that a helper may be allowed to publish a local state transition before the system is willing to let every redemption, wallet-balance, reserve-spend, or collateral consumer monetize that publication.
- That includes intra-transaction or intra-batch consequences. If one route leg can refresh a cached rate and a later leg in the same batch can already spend against it, the protocol never really separated publication from activation at all.
- Examples:
- publish a new rate now, but do not let
balanceOfconsume it until the next epoch - publish cached NAV now, but keep collateral adapters on the older bounded mark until a cooldown passes
- publish harvested rewards now, but socialize them across holders only through a delayed amortization path
- publish a refreshed reserve balance now, but do not let a freshly collapsed dust balance become same-transaction pricing truth without an additional realizability or delay boundary
- publish a new rate now, but do not let
- This keeps liveness while refusing to let one attacker-timed call become same-block solvency or reserve-pricing truth.
4. Presentation and collateral consumers should not share one publication rail
- A wrapper-local accounting update and a lending-market collateral update are rarely entitled to the same timing.
- If one cache write feeds both user-facing balances and borrow power, the protocol has implicitly declared that those two consumers deserve identical trust in the freshest possible state.
- Most incidents in this family show that they do not.
5. Cache writers should be modeled as oracle publishers
- A gas-saving cache is still a price publication layer if later code trusts it.
- A reserve-refresh helper is still a price publication layer if later code immediately prices swaps or redemptions from the refreshed state.
- The right threat model is often closer to
who may publish an oracle answer into storage?than towho may call a housekeeping helper? - This is especially true for wrappers whose cached rate later feeds
convertToAssets, rebases, redemption, liquidation, or debt-cap logic, and for AMMs whose refreshed reserve state later feeds swap or join/exit math. - The consumer list matters as much as the write path. Extending a cached field from previews into
balanceOf,totalSupply,allowance, collateral adapters, or other wallet- and solvency-facing reads is a security-boundary change even if the cache function itself did not change.
6. Consumer fan-out is authority growth, not a passive refactor
- Reviewers should treat
who consumes the freshly published mark now?as a first-class design question, not as a documentation afterthought. - A cache that originally powered one local preview can become a much stronger authority surface when later versions route wallet-balance semantics, redemption math, nested-pool pricing, or collateral adapters through the same stored value.
- Same-transaction fan-out matters too. If step
Npublishes a constituent rate and stepsN+1...N+kin the same route can already spend against that new mark, the protocol has granted one caller both publication and multi-consumer activation authority. - The sharp review move is to inventory each additional consumer separately:
preview,balanceOf,totalSupply,allowance,redeem,swap pricing,borrow power,liquidation, andnested-route pricingshould not be flattened into one genericcache usagestory.
Strong incident cluster for this note
- GoGoPool ggAVAX — public
syncRewards()timing let the attacker choose when donatedWAVAXand pending rewards became next-depositor pricing truth. - Zunami — a manipulable strategy-side mark became cached NAV, and the publication step itself turned thin-market donation state into expanded token balances; Ackee’s reconstruction is also the useful reminder that extending the cache into
balanceOf/totalSupply/allowancechanged the effect boundary materially. - Penpie —
batchHarvestMarketRewards()looked like harmless reward bookkeeping, but it was really a harvest/publication authority surface: the protocol measured balances around a market-controlledredeemRewards()callback, then published attacker-supplieddepositMarket()inflows as if they were rewards earned by that same market. - Raft Finance — donation-sensitive index updates let presentation-layer
balanceOf()become immediate collateral truth inside the same protocol boundary. - Wise Lending — even without one neat
syncRewards()button, attacker-authored exchange-rate growth still crossed too quickly into collateral-sensitive valuation. - Balancer STA —
gulp()did not create the accounting mismatch, but it gave the attacker publication authority over exactly when Balancer’s false internal reserve state would collapse to dust and become live spot-price truth. - Indexed Finance —
updateMinimumBalance()and latergulp()hardened attacker-shaped partial-basket state into new-token initialization truth that mint and burn paths then honored as real NAV. - Balancer Boosted Pools — a useful cache-heavy cousin where writable stored rate state, not just raw reserve math, became the bridge from transient manipulation into reusable pricing truth, and where one hop in a
batchSwap()refreshed the constituent cache for later hops in that same route. - ResupplyFi — the sharp downstream reminder that once a freshly published wrapper mark is taken too literally by lending math, inverse normalization can collapse into impossible states like zero-cost debt.
Design questions worth asking first
- Which functions are allowed to convert observed value into canonical stored value?
- Can any public caller decide the exact block when pending rewards, donations, or thin-market marks become cached NAV or exchange-rate truth?
- What maximum state change may one sync, harvest, checkpoint, or cache write publish?
- Does publication take effect immediately for redemption,
balanceOf, liquidation, and collateral logic, or do those consumers have their own distrust boundary? - If publication is public, where do the real safety guarantees live: monotonic inputs, capped movement, delayed activation, separate collateral adapters, or external canaries?
- What stale or invalid-publication containment exists once the new value is in storage?
Red flags
Anyone can call it, so it cannot be privileged.It only updates a cache.It only refreshes reserves to the actual balance.Sync is harmless because the underlying balance change is real.Presentation and collateral both read the same freshest mark because consistency is cleaner.Harvest or gulp is permissionless, but no one should have a reason to time it adversarially.If the update is wrong, governance can pause later.
Failure-prevention checklist
- Inventory every
sync,harvest,checkpoint,gulp,skim,cachePrice,updateIndex,updateReserve, and similar helper that can turn observed state into canonical stored truth; review them as publication-authority surfaces, not housekeeping. - Inventory the consumers of each published field too. A cache that originally only served previews or non-critical views may become a materially stronger authority surface once later versions route
balanceOf,totalSupply,allowance, collateral adapters, redemption logic, nested-pool pricing, or same-route swap execution through the same stored value. - Separate observation from publication. A raw balance increase, reward appearance, reserve mismatch, or thin-market valuation should not become canonical holder, redemption, or collateral truth merely because a helper copied it into storage.
- Bound per-call publication impact with hard rate, reserve, or index movement caps; if one invocation can move economically meaningful state arbitrarily far, public invocation is already too strong.
- Decouple publication from activation. Newly stored rates, NAV values, reserve states, or indices should usually face cooldowns, smoothing, or downstream quarantine before redemption,
balanceOf, flash-loan capacity, liquidation, swap pricing, or collateral consumers trust them. - Ask explicitly which consumers are still supposed to distrust a fresh publication even after the local cache or index is canonical; without that second question,
published locallyquietly turns intoactive everywhereby accident. - Review route ordering and batching semantics explicitly. If step
Ncan publish a new rate that stepN+1immediately consumes inside the same multicall orbatchSwap, the dangerous activation boundary may live entirely inside one transaction rather than between transactions. - Preserve an explicit gate from
canonical local publicationtoeconomically active downstream truth; one helper should not silently promote a fresh mark through both steps unless the protocol can defend that full combined transition. - Keep presentation, redemption, and collateral consumers on distinct distrust boundaries where possible. A gas cache or UX-facing update should not automatically double as same-block lending truth.
- Treat cache-write permissions and trigger timing as oracle governance. Whoever chooses when a manipulable state snapshot becomes canonical often controls what the protocol will later pay out or lend against.
- Include per-route and per-batch timing in that threat model.
Same caller can refresh then immediately consumeis already publication authority, even if no one else ever reads the cache in between. - Refuse same-step publication of multiple economic meanings unless the protocol can defend the combined transition. One helper should not simultaneously socialize rewards, rewrite balances, refresh reserves, and unlock new collateral power by accident.
- Add stale-value and invalid-publication containment: pause paths, per-consumer fallbacks, canary mismatches, haircut modes, and borrow-side distrust defaults should limit blast radius after a bad write lands.
- Test attacker-timed publication around donations, thin liquidity, reward arrival, fee accrual, rebases, strategy exits, and reserve desynchronization rather than only around honest periodic syncs.
- Monitor production for publication anomalies: sudden cached-rate jumps, large reserve-state deltas, same-block sync-and-borrow patterns, or single-transaction transitions from pending value to systemwide solvency truth.
Audit prompts
- Which exact functions are allowed to publish observed value into canonical stored state, and what consumers trust each published field afterward?
- Since the last review, did any cache or index start feeding new consumers such as
balanceOf,totalSupply,allowance, redeem math, nested-pool pricing, or same-route batch execution? - Can an arbitrary caller choose the block, transaction, or price environment in which pending rewards, donations, or reserve mismatches become canonical?
- What hard numeric limits exist on one publication step: rate delta, reserve delta, NAV delta, rebase delta, or collateral-value delta?
- Does publication become immediately live for redemptions,
balanceOf, flash loans, swaps, borrowing, liquidation, and pricing, or do those consumers keep separate activation boundaries? - After local publication lands, which consumers are still intentionally distrusting it, and where is that second boundary enforced?
- If a cache or reserve refresh is public, where do the real safety guarantees live: bounded inputs, delayed activation, monotonic proofs, external canaries, or downstream haircuts?
- Which publication paths read raw balances, thin-market marks, strategy-side values, or externally timed accounting that an attacker could shape just before the write?
- Could one helper accidentally publish to more economic domains than intended, such as holder accounting plus collateral valuation plus reserve-pricing truth?
- After a bad publication, what exactly contains the damage before governance responds: pause rights, consumer-side distrust, bounded caps, stale flags, or local circuit breakers?
- Are protocol-owned or keeper-owned publication helpers reviewed like oracle operators, or are they still mentally classified as mere maintenance jobs?
- What invariant proves that one publication call cannot turn locally real but manipulable state into immediately cashable cross-module truth?
Lessons that should already be standard
Permissionless syncis still a governance choice about who may publish economic truth.- A cache is not just a gas optimization once later code spends, lends against, or liquidates from it.
- Expanding cache consumers is a security change. Routing a stored mark into wallet-balance semantics, approvals, collateral adapters, or nested-route pricing widens publication authority even if the helper name and access control stay the same.
- Same-transaction consumer fan-out is already activation authority. If one caller can refresh a mark and then spend against it through several downstream legs before anyone else can react, the write path and the reserve-spend path are effectively one permission.
- The dangerous moment is often not when value first appears, but when some helper is allowed to declare that value canonical.
- Same-block publication plus same-block activation is usually a much stronger privilege than teams admit.
Published locallyis still not the same thing asmoney-good everywhere; every forceful consumer deserves an explicit answer about when the fresh state becomes active for it.- Real balances are not automatically safe balances. Donation-sensitive or thin-market-sensitive state can be economically real and still unsafe to canonize immediately.
- Presentation truth and collateral truth rarely deserve the same freshness policy.
- If the only rollback story is
pause after noticing, publication authority is already broader than the protocol’s containment design. - Whoever controls crystallization timing sits inside the solvency boundary, even if the function name sounds operational or harmless.
Where this note stops
- Use reward-recognition-presentation-and-collateral-separation when the sharper question is the state machine itself: what should count as observed, recognized, presented, or collateral-admissible value.
- Use donation-share-price-manipulation when the main question is how the attacker printed the wrapper rate in the first place.
- Use fee-on-transfer-accounting-mismatch when the deeper failure is that reserve accounting already drifted from reality before a later
sync/gulp/ reserve-refresh helper made that drift canonical. - Use protocol-authored-market-state-oracle-failure when the sharper question is that a protocol-authored local reserve or basket state became reserve-spending oracle truth, especially in AMM or index-pool systems.
- Use collateral-admission-lag-and-borrow-side-quarantine when the sharper downstream question is when a fresh wrapper mark should become lending truth, even after publication.
- Use recursive-borrow-suppression-for-fresh-collateral when the sharper downstream question is which borrow and liquidation rails should stay closed after partial admission.
- Use oracle-migration-quarantine-and-canary-design when the same authority problem lives in explicit oracle rollout or defensive overlay deployment rather than in wrapper sync/cached-NAV publication.
- Use balance-delta-provenance-failure when the deeper bug is that raw balance change was misread as earned value before any publication timing question appears.
How to use this note with the exploit corpus
- Start here when the question is
who was allowed to crystallize pending or attacker-timed value into canonical share truth? - Pair with reward-recognition-presentation-and-collateral-separation when the next question is
what state should that value have been in before publication mattered at all? - Pair with vault-share-truth-security-object when the broader question is
what one healthy share object should the vault have kept intact? - Pair with collateral-admission-lag-and-borrow-side-quarantine when the sharper follow-up is
even if publication was valid, why did lending logic honor it immediately? - Pair with recursive-borrow-suppression-for-fresh-collateral when the sharper follow-up is
which same-thesis leverage and liquidation rails should still have been closed after publication?
Internal linkages
- Broader timing-state companion: reward-recognition-presentation-and-collateral-separation.
- Broader vault-object companion: vault-share-truth-security-object.
- Core exploit family: donation-share-price-manipulation.
- Downstream lending companion: collateral-admission-lag-and-borrow-side-quarantine.
- Recursive leverage companion: recursive-borrow-suppression-for-fresh-collateral.
- Cache / pricing cousin: oracle-configuration-failure.
- Reserve-publication cousin: protocol-authored-market-state-oracle-failure.
- Fee-accounting cousin: fee-on-transfer-accounting-mismatch.
- Ambient-balance cousin: balance-delta-provenance-failure.
Control surface
-
The real control surface is not just
what is the right rate or reserve state? -
It is
who may publish which rate or reserve state, with what effect bounds, at what time, into which consumers. -
The consumer inventory is part of the authority surface, not post-hoc documentation. A mark that only informed previews yesterday but now also drives
balanceOf,allowance, redemption, or nested-route pricing is a stronger security object today. -
The healthy ladder is
state observed,state published canonically here, and only laterstate becomes economically active for this specific consumer. -
Protocols get into trouble when they mistake public sync rights, reserve-refresh helpers, or cache writes for passive bookkeeping instead of recognizing them as authority over when fragile value becomes economically live.
-
Sources:
- https://www.euler.finance/blog/exchange-rate-manipulation-in-erc4626-vaults
- https://www.openzeppelin.com/news/erc-4626-tokens-in-defi-exchange-rate-manipulation-risks
- https://docs.openzeppelin.com/contracts/4.x/erc4626
- https://eips.ethereum.org/EIPS/eip-4626
- https://ackee.xyz/blog/zunami-hack-post-mortem/
-
Last reviewed: 2026-08-01 UTC