YAM Finance rebase totalSupply inflation / governance bricking (2020-08-13)

  • Name: YAM Finance rebase totalSupply inflation / governance bricking
  • Date: 2020-08-13
  • Protocol: YAM Finance
  • Chain: Ethereum
  • Category: elastic-supply arithmetic bug / governance-liveness failure / treasury-lock incident / timelock-race rescue failure
  • Loss: no attacker treasury sweep was needed, but the protocol became effectively ungovernable and roughly $750k of yCRV in the reserve/governor path was left stranded; the original YAM v1 system had to be abandoned and migrated
  • Summary: YAM v1’s rebasing token contract computed post-rebase totalSupply as initSupply * yamsScalingFactor and forgot the final division by BASE (1e18). The first positive rebase therefore published an absurdly large totalSupply. The next rebase then propagated that bad state into the governor-side reserve mint path, inflating initSupply itself. Because YAM governance thresholds were defined as percentages of initSupply, quorum and proposal threshold became unattainably large. The community assembled a bug-fix proposal, but GovernorAlpha’s fixed 12.5 hour timelock meant it could not execute before the second rebase. After that second rebase, the corrupted initSupply made the rescue proposal fail quorum and even allowed it to be canceled, leaving the protocol frozen and the reserve funds stuck.
  • Trigger / entry point:
    • YAM v1 used an elastic-supply token model derived from Ampleforth-style rebasing.
    • YAMToken.rebase() updated yamsScalingFactor, then recomputed totalSupply from initSupply and the scaling factor.
    • The live code set totalSupply = initSupply.mul(yamsScalingFactor); instead of dividing by BASE, so the reported supply after a positive rebase was wrong by 1e18.
    • GovernorAlpha derived proposalThreshold() and quorumVotes() from initSupply, not from some separately bounded governance supply.
  • Root cause:
    • A one-line arithmetic omission in YAMToken.rebase() left the post-rebase totalSupply scaled incorrectly.
    • YAM’s governance liveness depended on supply variables that the rebase system itself could corrupt, so an accounting bug in monetary policy became a constitutional failure.
    • The rescue design also had a timing mismatch: the protocol’s bug-fix proposal still had to survive the standard GovernorAlpha queue-and-delay path even though the next rebase would make governance thresholds unreachable.
    • In effect, YAM coupled treasury recovery and governance repair to the same mutable state object that had already been corrupted.
  • Failure path:
    1. YAM launched with unaudited elastic-supply logic and reserve/governance mechanics tied to rebases.
    2. On the first positive rebase, YAMToken.rebase() updated yamsScalingFactor and then set totalSupply = initSupply.mul(yamsScalingFactor) without dividing by BASE.
    3. That first rebase published an absurdly large totalSupply, visible in transaction 0x7b9017ec92b0200455e5269380195fbecfbf91c8acda30985cc1dc413d215076.
    4. The community noticed the bug and launched the SAVE YAM coordination campaign, which asked holders to delegate roughly 1,600,000 YAM by 07:00 UTC and keep delegated voting power above 400,000 YAM through the vote window so a rescue proposal could land before the next rebase.
    5. The proposed emergency patch had two legs: pause rebases and hand control to a temporary governance contract at 0xd87c869f4672363ef2367bb817b086a25735626b with harder-coded liveness-preserving parameters.
    6. Even with enough social coordination to submit and queue the fix, GovernorAlpha still assigned the proposal an ETA of currentTimestamp + 12.5 hours, which placed execution after the already-scheduled second rebase.
    7. During the second rebase, transaction 0x32735e9e9aac51739b5725a225be6c7a3851f422be986d0f4f4bc0ec475ee286, the bad totalSupply fed into the reserve-mint path and corrupted initSupply itself.
    8. Because proposalThreshold() was 1% of initSupply and quorumVotes() was 4% of initSupply, the bug-fix proposal no longer met live governance thresholds after the second rebase.
    9. A keeper later canceled the queued proposal in transaction 0x1b98c761592b54498b9fbd41e6e4755cf6e5a1a6f13658d409081532d89d6bf0, since the proposer’s voting power had fallen below the now-exploded threshold.
    10. YAM v1 was left technically running but practically ungovernable, with the reserve-side yCRV stranded and the community forced toward migration rather than in-place repair.
  • What had to be true for the incident to become unrecoverable:
    • The rebase bug had to fire before a corrective proposal could both pass and clear the timelock delay.
    • Governance thresholds had to remain functions of mutable supply state that the rebase path itself could distort.
    • The reserve mint/rebase path had to propagate the first bad totalSupply into initSupply, not merely into a cosmetic display variable.
    • No privileged fast path, guardian override, or pre-committed emergency patch existed that could bypass ordinary governance timing once the bug was public.
  • Technical anatomy worth remembering:
    • This was not a classical attacker drain. It was a monetary-policy arithmetic bug that escalated into governance self-bricking.
    • The decisive missing operation was the final / BASE in the line recomputing totalSupply after a rebase.
    • The first rebase corrupted the displayed/live total supply; the second rebase made that corruption constitutional by poisoning initSupply, the variable used for proposal threshold and quorum.
    • The timelock mattered as much as the bug. YAM’s community could coordinate a rescue proposal, but the protocol’s own delay guarantees ensured that the fix became executable only after the second, fatal state transition.
    • The attempted rescue path is worth preserving in detail because it was not a vague someone should have moved faster story. The emergency plan was concrete: pause rebases, hand control to a temporary governor, and use hardcoded liveness-friendly parameters (10,000 unscaled YAM proposal threshold, 35,000 unscaled YAM quorum, 8 hour voting period). The design still lost because that new lane was itself downstream of the old timelock.
    • The note is a clean reminder that governance thresholds should not depend on mutable accounting state that can be broken by the subsystem governance is supposed to repair.
  • Representative artifacts:
    • Buggy line in YAMToken.rebase(): totalSupply = initSupply.mul(yamsScalingFactor);
    • Intended line described in public analysis: totalSupply = initSupply.mul(yamsScalingFactor).div(BASE);
    • First bad rebase transaction: 0x7b9017ec92b0200455e5269380195fbecfbf91c8acda30985cc1dc413d215076
    • Second rebase transaction: 0x32735e9e9aac51739b5725a225be6c7a3851f422be986d0f4f4bc0ec475ee286
    • Proposal-cancel transaction after threshold blowout: 0x1b98c761592b54498b9fbd41e6e4755cf6e5a1a6f13658d409081532d89d6bf0
    • Governance timing constant highlighted in public analysis: timelock.delay() = 12.5 hours
    • Rescue-governor address proposed during SAVE YAM: 0xd87c869f4672363ef2367bb817b086a25735626b
    • Rescue-governor parameters proposed during SAVE YAM: hardcoded proposal threshold 10,000 unscaled YAM, hardcoded quorum 35,000 unscaled YAM (1% of initial supply), voting period 8 hours
    • Governance thresholds highlighted in public analysis for the live v1 governor: proposalThreshold() = 1% of initSupply, quorumVotes() = 4% of initSupply
  • Response / mitigation:
    • YAM publicly disclosed the rebase bug and rallied the community into an emergency SAVE YAM delegation campaign aimed at passing a patch before the next rebase.
    • The concrete rescue plan was to pause rebases and transfer control to a temporary governance contract with lower hardcoded thresholds and a shorter voting period, but the proposal still had to traverse the live GovernorAlpha / timelock path first.
    • The community did manage to coordinate a bug-fix proposal, but later analysis concluded that normal GovernorAlpha delay mechanics made in-time execution impossible.
    • After the second rebase, the protocol acknowledged that YAM v1 could no longer be modified by governance, noted that roughly 75% of liquidity had been removed from the YAM/yCRV pool before the fatal rebase, and warned that any residual pool liquidity remained unsafe.
    • The project moved toward a migration / relaunch path (YAM 2.0) instead of trying to recover v1 in place.

Pattern tags

  • mutable-threshold-state-self-bricking
  • governance-capture
  • The fatal transition was not bad proposal passes; it was bad supply state becomes the denominator that decides whether any future proposal can still pass.
  • The first rebase only published absurd supply state, but the second rebase promoted that bad state into constitutional truth by poisoning initSupply, the variable GovernorAlpha used for proposal threshold and quorum.
  • The emergency patch path remained downstream of the broken constitution. YAM’s community could coordinate socially, but the queued fix still had to survive the same timelock and threshold machinery that the next rebase was about to make unreachable.
  • Proposal-cancel rights also rode the corrupted denominator, so the protocol did not just lose the ability to pass the fix; it also let the queued rescue become cancelable after the second rebase.

Why the exploit worked

  • YAM fused monetary-policy accounting and constitutional accounting too tightly. A one-line arithmetic omission in rebase() did not stay local to supply presentation; it spilled into initSupply, which GovernorAlpha treated as the base for proposal threshold and quorum. Once that happened, the bug was no longer merely economic-state corruption; it was electorate corruption.
  • The protocol needed governance to outrun the next scheduled state transition, but its own timelock guaranteed the opposite. That is the decisive liveness failure here: the community identified the bug quickly enough to coordinate a rescue proposal, yet the canonical repair lane still forced the patch to wait until after the next rebase that would make repair mathematically unreachable.
  • The second rebase was the constitutional point of no return. Before it, governance was endangered but still legible; after it, the corrupted supply base had propagated into the exact denominator used for proposer qualification, quorum, and cancelability. The same accounting fault now defined who could still govern at all.
  • The rescue path was not independent of the broken object. The emergency plan depended on old GovernorAlpha and the old timelock to authorize a new liveness-preserving governor, so the supposedly safer lane still inherited the timing and denominator fragility of the constitution it was trying to replace.
  • This is why YAM is more than a tokenomics bug. The protocol effectively let one nearby accounting object decide both how many tokens exist and whether the system can still repair itself, so when the object broke, governance stopped being the fix and became another casualty.

Three constitutional-liveness promotions this incident collapsed

  1. A rebase-authored supply denominator still existed and could keep moving

    • After the first bugged rebase, YAM still had live totalSupply, initSupply, and another scheduled rebase waiting in the near future.
    • That only meant mutable supply state still exists and the next transition is still coming.
  2. That corrupted supply state still counted as canonical constitutional threshold truth

    • YAM let GovernorAlpha continue deriving proposalThreshold() and quorumVotes() from initSupply even after the nearby monetary-policy system had already proved it could publish nonsense.
    • The decisive promotion was not merely supply is wrong; it was this same now-corrupted supply object still defines who can legitimately propose, pass, and cancel rescue governance.
  3. That accepted threshold truth still got to gate the only repair lane and treasury recovery path

    • The emergency patch still had to survive the old GovernorAlpha plus 12.5 hour timelock path, and later the same exploded denominator even made the queued rescue cancelable.
    • In other words, YAM let the broken denominator keep deciding whether rebases could be paused, whether a temporary governor could be installed, and whether stranded reserve-side assets could ever be recovered.

This is why the incident is more precise than rebase bug broke governance: YAM collapsed mutable supply state exists, that state still defines constitutional truth, and that accepted truth still deserves to gate the only rescue lane into one broken object.

Detection and audit pivots

  • Inventory every automatic accounting path that can mutate governance denominators. If rebases, reserve mints, share rescalings, or accounting syncs can touch the variables behind proposal threshold, quorum, proposer eligibility, or cancelability, they already belong in constitutional review.
  • Ask whether the next scheduled transition makes recovery easier or harder. YAM’s key failure was not only that a bug existed, but that the next ordinary rebase would make the constitution less reachable than it already was.
  • Model rescue durability, not only rescue submission. A fix proposal is not a real backstop if later denominator drift can strip proposer qualification, raise quorum out of reach, or make the proposal cancelable before execution.
  • Separate community can coordinate from protocol can execute the repair in time. Offchain urgency and onchain liveness are different objects, and YAM shows how a protocol can have the first while already losing the second.
  • Review timelock delay against adjacent automatic transitions. If the waiting period safely improves review for routine proposals but guarantees fatal delay for a liveness-preserving repair, the design needs a narrower emergency lane.
  • Alert on threshold anomalies as first-class incident signals. Implausible jumps in proposal threshold, quorum, or delegated-vote requirements should page operators the same way treasury drains or supply explosions do.

Why this incident matters

  • YAM preserves a rare but important failure mode where tokenomics math did not just misprice a pool or mint excess tokens; it destroyed the protocol’s own ability to govern and repair itself.
  • It is a sharp case study in how emergency governance is only as fast as its slowest timelock, and why community can coordinate quickly is not the same as protocol can patch quickly.
  • The note is also useful whenever a protocol lets mutable supply, share, or reserve variables feed directly into constitutional thresholds. Once those state variables are wrong, governance may stop being a repair mechanism and become another casualty.

Failure-prevention checklist

  • Derive proposal threshold, quorum, proposer eligibility, and cancellation rights from a bounded constitutional electorate base rather than live rebasing or reserve-accounting state such as initSupply.
  • Inventory every non-governance code path that can still mutate governance-critical denominator state, including rebases, reserve-mint paths, share rescalings, accounting syncs, and migration helpers.
  • Add a narrow liveness-preserving emergency lane that can pause rebases or reserve-side supply mutation without waiting for the full GovernorAlpha-style queue-and-delay path.
  • Ensure an admitted emergency repair cannot become cancelable or non-viable merely because the bug being fixed later explodes the denominator used for threshold and proposer checks.
  • Rehearse bug discovered before next scheduled transition scenarios until responders can name the exact authority that halts the next rebase, epoch roll, or accounting sweep.
  • Treat threshold telemetry as incident telemetry: alert on implausible jumps in quorum, proposal threshold, or delegated voting power requirements before defenders discover the problem through a failed rescue.
  • Keep treasury recovery and constitutional recovery decoupled enough that the same corrupted accounting object cannot strand reserves and simultaneously arithmetic-away the only legal repair path.

Audit prompts

  • Which exact variables determine proposal threshold, quorum, proposer eligibility, and proposal cancelability, and which rebase or reserve paths can still mutate them?
  • If the next scheduled rebase or reserve-mint event fires while the protocol is already in a broken state, does governance become more or less able to repair the damage?
  • Once an emergency repair is submitted or queued, can later denominator drift cause it to lose proposer qualification, fail quorum, or become cancelable?
  • What narrow authority can halt the next liveness-destroying automatic transition without handing operators an open-ended replacement constitution?
  • Is timelock delay still buying review safety here, or is it simply guaranteeing the bug another state transition before the patch can land?
  • If supply or reserve state jumped by absurd multiples in one block, would operators know which state path caused the change and what fallback threshold semantics apply?

Lessons that should already be standard

  • Monetary-policy state and constitutional state are different control surfaces; rebases should not get to redefine who can still govern.
  • The community coordinated a fix is not a safety story if the protocol’s own timelock guarantees the next fatal transition lands first.
  • A queued rescue that can be arithmetic-canceled by the same bug it is trying to repair is not a real rescue path.
  • Treasury-side asset recovery and governance-side liveness recovery should not depend on the same mutable denominator once that denominator is already suspect.
  • Governance thresholds deserve balance-sheet-grade monitoring because they decide whether repair remains possible at all.
  • The healthy design question is blunt: if a nearby accounting bug fires right now, what keeps the constitution from becoming mathematically unreachable before the patch executes?

Internal linkages

  • Core recurring pattern: mutable-threshold-state-self-bricking.
  • Positive-design companion: constitutional-electorate-bounding-and-threshold-independence-discipline — the sharper healthy-side question is what separately bounded constitutional electorate object, threshold freeze semantics, and rescue-proposal durability rules should have kept YAM governable even after rebase accounting broke.
  • Adjacent governance-process note: timelock-action-bundle-commitment — not because YAM approved the wrong payload, but because the rescue failed on the more basic question of whether governance’s delayed action path could still land before the fatal next state transition.
  • Adjacent backstop / fast-path companion: governance-backstop-scope-and-intervention-latency-discipline — the sharper design question one layer out is what narrower emergency authority should have been able to pause rebases or preserve constitutional liveness before GovernorAlpha became mathematically unreachable.
  • Adjacent governance family: governance-capture is mainly a useful contrast: YAM was not captured by hostile voters; it was incapacitated by supply-state corruption that made constitutional thresholds unreachable.
  • Adjacent accounting family: reward-index-initialization-failure is another example of how a tiny boundary or scaling mistake can turn an ordinary governance upgrade or accounting path into catastrophic system-wide failure, even though the mechanics differ.

Control surface