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 state for 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 like syncRewards(), 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 seeing pending / partial / attacker-timed state and starts seeing the 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 when updateMinimumBalance() and gulp() harden a manipulated partial-basket view into new-token initialization truth; GoGoPool when donated rewards only become next-depositor exchange-rate truth after syncRewards(); Zunami when a strategy-side donation plus thin-market mark becomes token-balance truth only after cacheAssetPrice(); ResupplyFi when a freshly inflated wrapper price only becomes borrowable truth after the protocol’s reciprocal-conversion path publishes exchangeRate = 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 when harvest() on the wrong vault object publishes false profit that debt logic honors as real repayment; KiloEx when a public TrustedForwarder.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 published from settled 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
  • Exploit sequence:
    1. The attacker identifies a helper that writes protocol-authored state other components later trust as canonical.
    2. 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.
    3. They hold that manipulated or misclassified state live for one block or one call frame.
    4. They call the publication helper at the most favorable moment, causing the protocol to store or refresh the attacker-shaped value.
    5. A mint, redeem, borrow, debt-repayment, or pool-pricing path immediately consumes the newly published value as if it were honest settled truth.
    6. 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.
  • 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 question why 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.

Three publication promotions reviewers should not collapse

  1. 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.
  2. Canonical local publication exists

    • A helper such as syncRewards(), harvest(), cacheAssetPrice(), updateIndex(), gulp(), or updateTotalAum() 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.
  3. 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 now is often not yet the same thing as safe 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 callable as a real policy choice: if public publication is safe, the code should show explicit movement bounds, delays, or downstream distrust.
  • 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 shaping and publication trigger in 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 -> monetize bundle. 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 publication to economically 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 published from settled. 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() or updateIndex() 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 state into 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 state from 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 cache is 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? but who 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 later gulp() 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 WAVAX only became harmful once syncRewards() 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 shallow msg.sender == trustedForwarder keeper 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 manipulated calc_withdraw_one_coin() quote became canonical AUM and then external pool-pricing truth in one transaction.
  • 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, and that 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

Last reviewed

  • 2026-07-31 UTC