State-Publication Authority Failure / Attacker-Timed Canonicalization
- Name: state-publication authority failure / attacker-timed canonicalization
- Category: exploit pattern / publication-authority failure / attacker-timed state canonicalization / cache-write abuse / sync-harvest-checkpoint-gulp abuse
- Summary: These exploits pay out when a protocol mistakes
publishing statefor passive bookkeeping. The attacker does not always need to forge balances, steal keys, or invent a fake oracle answer from scratch. Often they only need a stale reserve gap, a manipulable strategy quote, a donated balance, a wrong vault pointer, a weakly authenticated keeper path, or some other attacker-shaped intermediate state — plus a public or weakly bounded helper likesyncRewards(),harvest(),cacheAssetPrice(),updateIndex(),updateMinimumBalance(),gulp(),updateTotalAum(), or a meta-tx / keeper update relay that turns that intermediate state into canonical truth. Once the helper runs, downstream minting, redemption, collateral, debt, or pool-pricing code stops seeingpending / partial / attacker-timed stateand starts seeingthe protocol's own published answer. - Fast routing: start with Balancer STA when the latent accounting lie only becomes spendable after
gulp()collapses recorded reserves to dust-state reality; Indexed whenupdateMinimumBalance()andgulp()harden a manipulated partial-basket view into new-token initialization truth; GoGoPool when donated rewards only become next-depositor exchange-rate truth aftersyncRewards(); Zunami when a strategy-side donation plus thin-market mark becomes token-balance truth only aftercacheAssetPrice(); ResupplyFi when a freshly inflated wrapper price only becomes borrowable truth after the protocol’s reciprocal-conversion path publishesexchangeRate = 0; Balancer Boosted Pools when a constituent linear-pool mark only becomes reusable swap truth after a nested pool rate cache crystallizes it; Alchemix alETH whenharvest()on the wrong vault object publishes false profit that debt logic honors as real repayment; KiloEx when a publicTrustedForwarder.execute()path can impersonate the keeper chain and publish absurd internal mark prices as if they were authorized protocol updates; and Makina when permissionless AUM publication turns a manipulated strategy unwind quote into external pool-pricing truth in the same transaction. - What it is:
- The protocol has some state that is not yet supposed to be economically final: pending rewards, a cached rate candidate, stale internal reserves, a manipulable unwind quote, a reindex threshold, or a harvest result.
- A helper function exists to copy, crystallize, or publish that state into canonical storage or canonical reserve accounting.
- The protocol treats this helper as operational housekeeping instead of as an authority surface.
- The attacker shapes the underlying state first, then chooses the exact moment the helper publishes it.
- Downstream consumers immediately trust the published result as share price, redemption value, debt relief, reserve truth, or tradeable oracle truth.
- Typical preconditions:
- A protocol-maintained helper can promote observed or inferred state into canonical storage, reserve records, or accounting indices.
- The published state depends on something attacker-timable: direct donations, thin-market spot marks, stale internal reserves, wrong module pointers, or partial basket state.
- Publication is public, insufficiently bounded, or same-block with the downstream monetization path.
- Downstream consumers do not distinguish
freshly publishedfromsettled and trustworthy; they use the new value immediately for mint, burn, redeem, borrow, liquidate, or swap pricing. - One published write can move economically meaningful state faster than the protocol’s defenses, keepers, or delays can react.
- Common vulnerable surfaces:
- vault
syncRewards(),harvest(),checkpoint(),cacheAssetPrice(),updateIndex(), and cached-NAV writers - AMM
gulp(), reserve-refresh, or basket-initialization helpers - controller functions that set new-token thresholds or extrapolated pool-value state before mint / burn paths read them
- strategy or AUM update paths that publish execution-path quotes into wrapper exchange rates
- debt-forgiveness or reward-distribution functions that convert module-local accounting output into system-wide solvency truth
- any path that lets one actor both shape the candidate state and trigger its publication before settlement or normalization
- vault
- Exploit sequence:
- The attacker identifies a helper that writes protocol-authored state other components later trust as canonical.
- They find a way to shape the candidate input to that helper: donations, thin-market trades, stale-reserve drift, wrong module selection, or temporary basket imbalance.
- They hold that manipulated or misclassified state live for one block or one call frame.
- They call the publication helper at the most favorable moment, causing the protocol to store or refresh the attacker-shaped value.
- A mint, redeem, borrow, debt-repayment, or pool-pricing path immediately consumes the newly published value as if it were honest settled truth.
- The attacker cashes out against real assets, real debt forgiveness, or real pool liquidity before the state can be corrected.
- Variants:
- Reserve-refresh collapse — stale internal accounting is already wrong, but a public sync like
gulp()decides exactly when that wrongness becomes active pricing truth. Balancer STA is the clean exemplar. - Initialization-threshold publication — a controller publishes manipulated thresholds or weights that a new asset initialization path then treats as fair basket truth. Indexed is the clean exemplar.
- Reward / donation recognition publication — donated or attacker-supplied inventory becomes canonical share-value or reward state only once a sync helper recognizes it. GoGoPool is the clean bootstrap exemplar.
- Cached-NAV snapshot abuse — a transient strategy-side mark is frozen into a cached asset price, index, or exchange rate that remains live long enough to monetize after the manipulative leg is unwound. Zunami is the clean cached-price exemplar.
- Harvest profit misclassification — the protocol itself computes false profit because modules disagree about which deposits or vault object are canonical, then a harvest path publishes that fiction as debt-repayment truth. Alchemix alETH is the clean internal-accounting exemplar.
- Write-path authentication collapse — the protocol’s own price-update path is reachable through a weakly authenticated forwarder, relayer, or keeper chain, so the attacker can publish absurd internal prices without moving any external market. KiloEx is the clean exemplar.
- Same-transaction AUM relay — a manipulable execution-path valuation becomes canonical AUM or exchange-rate truth and then immediately reaches an external consumer in the same transaction. Makina is the clean modern exemplar.
- Reserve-refresh collapse — stale internal accounting is already wrong, but a public sync like
- Quick classifier:
- If the main question is
who was allowed to make this attacker-shaped intermediate state canonical right now?, start here. - If the main question is
how was the exchange rate or share price inflated in the first place?, start with donation-share-price-manipulation or first-deposit-inflation and return here for the publication step. - If the main question is
why did the protocol's own reserve or basket state behave like a manipulable oracle?, compare with protocol-authored-market-state-oracle-failure. Use that note when the pricing object itself is the central preserved lesson; use this note when the sharper lesson is that a sync / cache / init helper made the object canonical at attacker-chosen timing. - If the main question is
the token transfer semantics already made reserve accounting false, start with fee-on-transfer accounting mismatch and then pull this note in for the follow-up questionwhy did gulp matter so much?. - If the main question is
the valuation surface itself was a manipulable strategy unwind quote or controller path, compare with vault-strategy-path-valuation-manipulation. Use that note for the bad valuation object; use this note for the publication power that made the bad valuation economically live. - If the main question is broader design hardening rather than failure classification, move to sync-harvest-and-cache-write-authority-discipline.
- If the main question is
Three publication promotions reviewers should not collapse
-
Attacker-shaped or fragile candidate state exists
- A donation, thin-market quote, stale reserve view, wrong module pointer, delayed reward, or hypothetical unwind mark is visible somewhere in the system.
- This is only an observation fact. It still does not answer who may bless it as canonical or which money-moving consumers deserve to trust it.
-
Canonical local publication exists
- A helper such as
syncRewards(),harvest(),cacheAssetPrice(),updateIndex(),gulp(), orupdateTotalAum()writes that candidate state into canonical storage, canonical reserve accounting, or canonical exchange-rate / NAV state. - This is already a much stronger authority step than merely noticing the value. But it is still not automatically proof that every downstream consumer should honor the freshest possible publication right away.
- A helper such as
-
Economically active downstream truth exists
- Mint, burn, redeem, borrow, liquidate, forgive debt, or swap-pricing code is now allowed to spend against the freshly published state.
- This deserves its own boundary too.
Published nowis often not yet the same thing assafe to honor everywhere right now.
The recurring reviewer task is to answer three separate questions cleanly: what fragile state was visible?, what exact helper published it as canonical locally?, and which consumers were actually entitled to trust that fresh publication immediately?
- Detection heuristics:
- Search for functions named
sync,harvest,checkpoint,cache,gulp,updateIndex,updateAum,updateMinimumBalance, or similar and ask what exact economic truth they publish. - Ask whether the same actor can both move the candidate state and trigger the helper that records it.
- Look for same-block or same-transaction paths where published state is immediately consumed by mint, redeem, borrow, liquidation, or swap logic.
- Review whether the helper publishes state derived from thin AMM quotes, direct donations, partial baskets, or module-local bookkeeping that could still be wrong.
- Test whether reverting the manipulated market state after publication still leaves the protocol exploitable because the cached or stored value survives long enough to matter.
- Treat
publicly callableas a real policy choice: if public publication is safe, the code should show explicit movement bounds, delays, or downstream distrust.
- Search for functions named
- Defenses:
- Model sync, harvest, cache-write, and reserve-refresh helpers as oracle-publishing authority, not as neutral housekeeping.
- Separate observation from publication and publication from activation; a freshly written value should not automatically become immediate collateral, debt, or pool-pricing truth.
- Bound per-call impact with deviation caps, monotonicity rules, rate-of-change guards, and cooldowns.
- Make downstream consumers distrust freshly published values until a delay, smoothing window, or independent canary confirms them.
- Restrict publication authority when the candidate state comes from manipulable strategy paths, thin markets, or module-local accounting that can still be wrong.
- Keep internal accounting objects canonical before harvest or debt-relief logic can spend them; wrong vault pointers and stale module topology should never become publishable profit.
- Add adversarial tests that explicitly combine
state shapingandpublication triggerin one transaction.
What sound design looks like
- One observation boundary should stay explicit: a protocol may observe donations, thin-market marks, stale reserve deltas, hypothetical unwind quotes, or pending rewards without yet treating them as money-good truth.
- One publication boundary should be explicit: helpers that write reserves, NAV, rates, reward indices, or debt-relief state should be treated as oracle publishers with narrowly defined authority, not as passive bookkeeping.
- One activation boundary should stay separate from publication: a freshly written value should not automatically become immediate solvency, withdrawal, redemption, or pool-pricing truth without delay, smoothing, quarantine, or independent confirmation.
- One actor should not control the whole
shape -> publish -> monetizebundle. The cleaner design separates who can influence candidate state, who can publish it, and who can spend against it. - One publication step should have bounded blast radius: deviation caps, epoch caps, monotonicity rules, or amortized updates should keep a single cache write or sync call from rewriting too much economic truth at once.
- One bad publication should be containable: caches should be invalidatable, downstream consumers pausable, and just-published values quarantineable before they can cascade into systemwide loss.
Failure-prevention checklist
- Inventory every function that can turn candidate state into canonical protocol truth:
sync,harvest,checkpoint,cache,gulp, reserve refresh, AUM update, reward recognition, index update, and initialization-threshold writers. Treat that inventory as solvency-boundary code, not ops trivia. - For each publisher, write down the candidate input it is allowed to crystallize and how an attacker could shape that input: donation, thin-market spot, stale reserve drift, partial basket state, wrong module pointer, delayed reward, or strategy unwind quote.
- Split observation, publication, and activation into separate stages whenever possible. A value being writable to storage should not automatically make it same-block redemption, collateral, debt-forgiveness, or swap-pricing truth.
- Inventory downstream consumers for each publication helper, not just the helper itself. Review exactly which redemptions, wallet-balance surfaces, collateral paths, debt-relief flows, or pool-pricing rails will trust the freshly published value.
- Put hard movement limits on one publication step: deviation caps, monotonicity guards, epoch limits, rate-of-change bounds, or capped amortization. If one call can move too much truth, the helper is over-authorized.
- Preserve an explicit gate from
canonical local publicationtoeconomically active downstream truth; one helper should not silently promote a fragile value through both steps unless the protocol can defend that full combined transition. - Prevent the same actor from both shaping the candidate state and immediately publishing it into canonical storage unless the downstream effect is tightly bounded.
- Require downstream consumers to distinguish
freshly publishedfromsettled. Lending, liquidation, withdrawal, and reserve-paying paths should be able to quarantine, haircut, delay, or ignore just-written values. - Keep module topology canonical before publication. Vault pointers, strategy registries, basket constituents, and denominator state should not be mutable enough that
harvest()orupdateIndex()can publish profit or solvency from the wrong object. - Adversarially test one-transaction bundles that do
manipulate -> publish -> monetize -> normalize. If the exploit still pays after the market leg is unwound but the published state remains live, the publication boundary is too weak. - Monitor for publication-risk signals in production: outsized one-call rate jumps, unusual sync timing after donations or thin-market trades, same-block cache updates followed by large mint / redeem / borrow flows, and emergency helpers being used as economic timing rails.
- Make rollback explicit. If a bad publication lands, the protocol should know which consumers can be paused, which caches can be invalidated, and how blast radius is capped before governance catches up.
Audit prompts
- Which exact functions are allowed to convert
candidate stateinto canonical state, and what downstream code immediately trusts their output? - Can one actor both create the favorable state and choose the publication moment in the same transaction or same block?
- What manipulative inputs feed the publisher: donations, stale reserves, thin-market quotes, hypothetical unwind math, partial basket views, reward deltas, or wrong module selection?
- Does publication itself have effect bounds, or is the real safety assumption merely
nobody will time this adversarially? - Which downstream consumers treat a freshly published value as live truth for minting, redeeming, borrowing, liquidating, debt relief, or pool pricing without an additional distrust boundary?
- If the manipulated market state normalizes right after publication, does the protocol still lose because the cached or stored value survives long enough to be monetized?
- Are there any helpers that look operational —
harvest,sync,gulp,cache,checkpoint,updateAum,updateMinimumBalance— but in practice exercise authority over solvency or reserve truth? - Can stale or wrong module topology make honest-looking publication code write dishonest economic truth, as in wrong-vault harvest or bad constituent initialization cases?
- What exactly prevents
observe bad state -> publish bad state -> spend against bad statefrom happening in one attacker-controlled bundle? - When a publication is later found unsafe, how is it contained: invalidation bit, cooldown, pause, downstream stale check, bounded per-asset loss cap, or nothing?
Lessons that should already be standard
-
It only updates a cacheis not a defense. If later code trusts the cache for money movement, the cache writer is an oracle publisher. -
Public housekeeping is still authority when the caller chooses when fragile state becomes canonical. Timing rights are economic rights.
-
A protocol’s own published answer is often more dangerous than the raw manipulation that preceded it, because every downstream module is inclined to trust protocol-authored storage more than a visibly noisy market.
-
Observation, canonical local publication, and economically active downstream truth are different trust levels. The safest design often lets the system notice fresh state quickly, publish it narrowly, and force money-moving consumers to accept it only later or only in bounded form.
-
Same-block publication plus same-block monetization is the recurring catastrophe shape. If the protocol cannot defend that bundle, it should break the bundle.
-
Sync and harvest code deserve the same adversarial review as mint, burn, borrow, and liquidation code. In this family, the helper is the monetization boundary.
-
Wrong internal topology is as dangerous as wrong prices. A harvest against the wrong vault or an initialization routine against the wrong constituent can publish fiction without any external market manipulation at all.
-
The durable question is not just
can this value be manipulated?butwho is allowed to bless it as real, with what effect, and how quickly do other modules believe them? -
High-signal incidents:
- gulp pool drain (2020-06-28) — the reserve-accounting mismatch existed already, but
gulp()let the attacker choose the exact moment Balancer’s dust-state reality became live spot-price truth. - CC10 single-token pool-value extrapolation mint-burn drain (2021-10-14) —
updateMinimumBalance()and latergulp()hardened a flash-manipulated partial-basket view into canonical initialization and share-minting truth. - harvest debt-erasure incident (2021-06-16) —
harvest()on the wrong vault object published false profit that the protocol immediately honored as real borrower debt relief. - GoGoPool ggAVAX first-depositor share-price inflation near miss (2023-01-03) — donated
WAVAXonly became harmful oncesyncRewards()recognized it quickly enough to poison the next depositor’s exchange rate. - zETH SDT-donation price-caching share-inflation drain (2023-08-13) — strategy-side donation plus thin-market pricing only became spendable because
cacheAssetPrice()froze the manipulated NAV into token-balance truth. - leveraged drain (2025-04-14) —
TrustedForwarder.execute()plus a shallowmsg.sender == trustedForwarderkeeper check let an attacker self-publish absurd internal ETH prices and cash out perp PnL without moving any outside oracle. - [[../../../incidents/oracle-market-abuse/makina-finance-dusd-usdc-curve-machineshareoracle-permissionless-updatetotalaum-spot-price-manipulation-drain-2026-01-20|Makina Finance DUSD/USDC Curve MachineShareOracle permissionless
updateTotalAum()spot-price manipulation drain (2026-01-20)]] — a manipulatedcalc_withdraw_one_coin()quote became canonical AUM and then external pool-pricing truth in one transaction.
- gulp pool drain (2020-06-28) — the reserve-accounting mismatch existed already, but
-
Why this pattern matters:
- Security reviews often notice the bad candidate input but underweight the helper that makes it canonical. The publication helper is frequently the monetization multiplier.
- This pattern explains why some incidents look like different families up front — fee-on-transfer drift, donation attacks, strategy-valuation bugs, deployment mistakes — yet rhyme at the point where a helper blesses the bad intermediate state.
- It is a durable reminder that a protocol’s own authored write is often more dangerous than the raw market manipulation that preceded it, because downstream modules trust the protocol’s published answer more than any external spot price.
Internal linkages
- Positive-design companion: sync-harvest-and-cache-write-authority-discipline — this note classifies the failure family; that note is the checklist for how publication authority should have been bounded.
- Adjacent pattern: donation-share-price-manipulation — use that note when the exchange-rate lie itself is the analytical center and this note when the sharper follow-up is who was allowed to crystallize that lie into canonical share truth.
- Adjacent pattern: first-deposit-inflation — GoGoPool sits on the seam where first-depositor capture becomes much worse because reward publication is attacker-timed.
- Adjacent pattern: protocol-authored-market-state-oracle-failure — both families preserve
the protocol published its own bad price; this note is narrower about the publication helper as the exploitable control surface. - Adjacent pattern: vault-strategy-path-valuation-manipulation — Makina and Zunami preserve bad strategy-side valuation objects; this note preserves the separate question of who could publish them and when.
- Adjacent pattern: fee-on-transfer accounting mismatch — Balancer STA shows the seam where broken transfer semantics create latent false reserves and state-publication authority turns that latent drift into immediate drainability.
- Adjacent pattern: solvency-check bypass — Alchemix shows how a false publication can become direct debt forgiveness or solvency fiction even without a market-price attack.
- Best concrete routing cluster so far: Balancer STA for reserve-refresh collapse, Indexed Finance for initialization-threshold publication, GoGoPool for reward-sync publication, Zunami for cached-NAV snapshot abuse, Alchemix alETH for harvest profit misclassification, KiloEx for write-path authentication collapse, and Makina Finance for same-transaction AUM relay.
Control surface
- The leverage sits in the authority to publish protocol-authored state, not just in the ability to move external prices or balances.
- The key ladder is
candidate state exists,candidate state becomes canonical local publication, andthat publication becomes economically active downstream truth. - The key question is always: who can decide when a tentative, manipulable, stale, or misclassified value climbs each rung of that ladder?
- Once downstream modules trust that published answer for minting, redemption, borrowing, debt reduction, or pool pricing, the helper that wrote it is part of the solvency boundary.
Sources
- https://medium.com/balancer-protocol/incident-with-non-standard-erc20-deflationary-tokens-95a0f6d46dea
- https://ndxfi.medium.com/indexed-attack-post-mortem-b006094f0bdc
- https://code4rena.com/reports/2022-12-gogopool
- https://ackee.xyz/blog/zunami-hack-post-mortem/
- https://forum.alchemix.fi/public/d/137-incident-report-06162021
- https://makinafi.substack.com/p/post-mortem-january-20th-2026-incident
- https://medium.com/@KiloEx/kiloex-security-incident-root-cause-analysis-post-mortem-3d899caac08c
- https://www.euler.finance/blog/exchange-rate-manipulation-in-erc4626-vaults
Last reviewed
- 2026-07-31 UTC