MakerDAO Black Thursday Near-Zero-Bid Collateral Auctions (2020-03-12)

  • Name: MakerDAO Black Thursday near-zero-bid collateral auctions
  • Date: 2020-03-12
  • Protocol: MakerDAO
  • Chain: Ethereum
  • Category: liquidation-auction liveness failure / keeper participation collapse / near-zero-bid collateral seizure / bad-debt event
  • Loss: roughly 62,892.93 ETH of collateral reportedly cleared through near-zero-bid auctions across March 12-13, 2020; public analyses also describe about 5.67M DAI of system shortfall / bad debt and roughly $8.32M of keeper windfall at then-prevailing ETH prices
  • Summary: MakerDAO’s Black Thursday failure was not a classic contract-drain bug; it was a liquidation-mechanism breakdown. As ETH crashed and Ethereum blockspace became severely congested, Maker’s Liquidations 1.2 stack pushed undercollateralized ETH-A vaults into Flipper auctions that Cat.bite() / kick() opened with bid = 0 and relied on outside keepers to discover price. In flip.sol, tend() required bid > bids[id].bid and at least beg (5%) improvement over the previous bid, but when the previous bid was literally zero that rule became functionally any positive dust bid is valid. In practice, many honest keeper bots could not react fast enough, could not reprice gas aggressively enough, or got stuck behind nonce chains and congestion. A small set of opportunistic keepers then won large collateral lots with dust-sized opening tend() bids, waited out the bid-expiry window, and used deal() to claim the ETH. Vault owners lost all or nearly all collateral in many cases, while Maker still accumulated bad debt that later had to be socialized through MKR dilution.
  • Trigger / entry point:
    • ETH’s violent price collapse on 2020-03-12 pushed many Maker vaults below required collateralization.
    • Maker’s liquidation path used Cat.bite() to seize unsafe vault collateral and kick Flip auctions in Flipper with opening bid: 0.
    • In Flipper, bids were funded from Vat.dai internal balances rather than ordinary wallet DAI, so competitive participation depended on keepers already having prefunded internal DAI plus a working transaction pipeline.
    • Ethereum congestion and gas spikes impaired both vault-defense transactions and ordinary keeper competition right when liquidation volume surged.
  • Root cause:
    • Maker treated liquidation participation as an external market-service assumption rather than a protocol-critical liveness dependency.
    • Liquidations 1.2 allowed new auctions to open from bid = 0, and Flipper.tend() only enforced bid > previous bid plus a beg minimum increment over the prior bid. Because the prior bid started at zero, the opening-bid floor was effectively any positive dust amount rather than a meaningful reserve price.
    • Short auction timers and brittle keeper infrastructure turned chain congestion into a solvency event: the protocol could seize collateral, but it could not reliably sell that collateral for enough DAI.
    • Oracle / OSM timing and market shock compressed liquidations into the same window that crippled bidding, magnifying the keeper-capacity problem.
    • In effect, Maker’s solvency logic assumed healthy offchain bidder competition during exactly the network conditions least likely to sustain it.
  • Exploit path:
    1. As ETH sold off sharply on 2020-03-12, more Maker vaults crossed liquidation thresholds and became biteable.
    2. Cat.bite() transferred collateral out of unsafe vaults and kicked ETH-A Flip auctions with bid: 0, setting up a tend phase where Flipper.tend() only required the first valid bid to be strictly greater than zero; the usual beg = 5% minimum increase did not protect the opening step because 5% * 0 is still 0.
    3. Honest keepers were expected to fund internal DAI balances, observe auction ids, and compete bids upward toward tab, but heavy congestion and gas spikes disrupted that process.
    4. Public reporting and later analyses describe keeper bots that did not adapt gas pricing well under stress and sometimes became stuck behind their own earlier pending transactions.
    5. Opportunistic keepers submitted dust-sized opening bids that were technically valid tend() calls even though they were economically negligible relative to the collateral lots.
    6. Because few or no competing bids arrived before the bid-expiry window elapsed, those bidders became the auction winners by default.
    7. Flipper permitted auctions to end in the tend phase without ever reaching tab; after tic expired, deal() transferred the entire collateral lot to the winning bidder while the unpaid tab - bid shortfall remained bad debt against the Vow.
    8. This repeated across a large share of the liquidation set, producing the cluster of so-called 0 DAI or zero-bid auctions remembered as Black Thursday.
    9. Vault owners lost collateral, Maker was left with millions of DAI of bad debt, and the protocol later had to recapitalize through debt auctions that minted and sold MKR.
  • What the attacker needed to be true:
    • Vault liquidations had to arrive during a period when keeper competition was badly impaired.
    • Auctions had to permit an economically tiny first bid rather than enforcing a meaningful reserve or protocol backstop.
    • Honest keepers needed to be unable or unwilling to replace the opening dust bid before the timer expired.
    • The bidder needed enough operational reliability to keep one live transaction path working while others stalled.
    • Maker needed to continue settling auctions mechanically even when the realized bid was nowhere near fair distressed collateral value.
  • Technical anatomy worth remembering:
    • The colloquial 0 DAI description slightly oversimplifies the contract mechanics. Flipper.tend() required the new bid to be strictly greater than the previous bid, so the decisive bids were dust-sized or near-zero rather than literally zero; the economic result was the same.
    • Cat.bite() explicitly kicked auctions with bid: 0, and flip.sol set beg = 1.05, ttl = 3 hours, and tau = 2 days by default. The crucial failure was that beg was inert on the very first bid because the comparison started from zero.
    • Flipper also allowed an auction to settle in tend without ever reaching tab, so the protocol could both hand out the entire lot and still carry the unpaid difference into system bad debt.
    • Competitive bidding depended on Vat.dai internal balances, not only on wallet balances or a keeper’s willingness to participate. That made prefunding and keeper operational readiness part of the effective security boundary.
    • This was a full-stack failure: price shock, OSM timing, blockspace congestion, keeper capital, gas repricing, nonce management, and auction math all interacted.
    • The incident is also a reminder that liquidation works in normal markets is not the same claim as liquidation preserves solvency in crisis markets.
    • It is the auction-side exemplar for liquidation-capital-provenance-and-keeper-competition-assumptions: Maker did not merely need an honest price for ETH; it needed live bidders with prefunded internal DAI, working mempool access, and enough operational headroom to keep competing while congestion was worst.
  • Representative transactions and artifacts:
    • ETH-A Flipper auction contract: 0xF32836B9E1f47a0515c6Ec431592D5EbC276407f
    • Cat liquidation module: 0x78f2c2af65126834c51822f56be0d7469d7a523e
    • Public analyses highlighted multiple winning keeper addresses, including 0x9c05a05893ada984fc20d0da0c046de5cc0e8273, 0x9631a838a81d4050c43c66bc03a0cf414243f661, 0xb00b6d69822da235a99d2242376066507c9a97b7, and 0xb400cd43dc25db30c07e665903a052ac120c30ad.
    • White Rabbit’s post-incident chain analysis reported 3,994 liquidation transactions across March 12-13, of which 1,462 — about 36.6% — cleared at a reported 100% discount / near-zero-bid outcome.
  • Response / mitigation:
    • Maker governance responded with emergency parameter changes intended to make auction capture harder, including longer bidding windows and larger lot sizing.
    • The protocol later recapitalized system bad debt through MKR debt auctions, diluting MKR holders rather than letting DAI remain undercollateralized.
    • In the longer run, Black Thursday became the canonical justification for Liquidations 2.0 and broader redesign of Maker’s liquidation and backstop assumptions.
    • The event also triggered prolonged governance debate over compensating vault owners whose collateral had been seized for effectively nothing.

Pattern tags

  • Core recurring pattern: liquidation-auction-liveness-failure.
  • The preserved shape is sharper than generic market crash or generic oracle lag: Maker’s solvency path depended on zero-start English auctions, prefunded internal Vat.dai, and live keeper competition staying healthy during the exact blockspace crisis that made liquidations surge.
  • This is the corpus’s clean canonical example of liquidation triggered correctly, recovery still failed because the first executable bid was underconstrained and the rescue capital / mempool-access assumptions were too optimistic.

Three liquidation-authority promotions this incident collapsed

  1. Maker could mechanically trigger liquidation and seize collateral from unsafe vaults

    • Cat.bite() could correctly identify undercollateralized vaults, move collateral into the liquidation path, and kick Flipper auctions.
    • By itself this only proved the protocol could start liquidations. It did not yet prove that the resulting auction state represented sane stressed recovery value.
  2. Maker promoted the first live tend() bid into canonical stressed-market price discovery

    • Because auctions opened from bid = 0, the first surviving keeper bid answered what is this collateral lot worth right now? even when that bid was just dust submitted during congestion.
    • That was the decisive promotion: one live bidder under broken competition conditions became the protocol’s authoritative distressed-price signal.
  3. That accepted dust-price signal still retained full collateral-transfer and bad-debt crystallization authority

    • Once the weak opening bid survived the timer, deal() could transfer the entire lot to the bidder while the unpaid remainder hardened into bad debt against the system.
    • A live bid existed and that live bid deserves final collateral-transfer authority plus solvency consequences were separate promotions, and Black Thursday collapsed them together.

Why the exploit worked

  • Cat.bite() opened Flipper auctions from bid = 0, and Flipper.tend() only required the next bid to be strictly greater than the previous one while improving it by beg; that meant the opening step was effectively any positive dust amount rather than a meaningful reserve price.
  • Maker’s stressed recovery path depended on external keepers already holding usable internal Vat.dai balances plus the operational ability to observe auction ids, reprice gas, and keep nonce chains healthy during extreme Ethereum congestion. Those were hidden but very real solvency assumptions.
  • Flipper could still settle an auction in the tend phase without ever reaching tab, so the protocol could both transfer the whole collateral lot to the winner and keep the unpaid remainder as bad debt. The liquidation engine therefore lacked a hard guarantee that collateral seizure implied adequate debt recovery.
  • OSM timing, price shock, and mempool stress bunched many unsafe vaults into the same crisis window, which meant the weakest part of the design — price discovery through opportunistic external bidding — was asked to do the most work when bidder competition was least reliable.
  • The later MKR debt auctions confirmed the deeper failure mode: Maker had a path to socialize the loss after auctions failed, but not a sufficiently robust mechanism to stop dust first bid + no competition from becoming live collateral-transfer authority in the first place.

Detection and audit pivots

  • Audit the exact first-bid rule rather than the headline increment rule. If an English auction starts from zero, ask what protects the opening step when only one bidder is live.
  • Separate collateral can be seized from collateral sale restores solvency. Any liquidation engine that can close while still underrecovering debt should be treated as a recapitalization-risk surface, not just an auction surface.
  • Stress keeper operations as part of protocol security: internal balance prefunding, gas repricing, nonce replacement, mempool connectivity, and monitoring latency all mattered here as much as auction math.
  • Model oracle-lagged liquidation bursts, not only smooth mark-to-market deterioration. A system that looks safe under staggered liquidations can still fail when delayed recognition compresses many auctions into one blockspace crisis.
  • Ask what the protocol’s single-bidder lower bound looks like. If one surviving bidder can still walk away with economically absurd collateral at the opening step, the design is not stressed-safe.
  • Review the recapitalization path before the event. If the protocol’s real answer to failed liquidation recovery is governance-token dilution or emergency socialization, that should be explicit in risk review rather than discovered during crisis.

Why this incident matters

  • It is the cleanest major example of a DeFi protocol failing not because collateral prices moved, but because the liquidation market that was supposed to save the system stopped functioning under stress.
  • It shows that mempool access, gas strategy, and bidder ops are part of protocol security whenever solvency depends on offchain keepers.
  • It remains one of the best case studies for why auction liveness, reserve logic, and precommitted backstop liquidity matter as much as collateral ratios and oracle correctness.

Failure-prevention checklist

  • Do not let the opening liquidation step inherit a meaningless reserve from 0. If the first executable bid can seize the whole collateral lot, the system needs a real opening-price floor, bounded protocol backstop, or alternate sale path that still works when only one bidder is live.
  • Treat keeper funding rails as part of the security boundary. If bidding requires prefunded internal balances like Vat.dai, the protocol should monitor, diversify, and rehearse that capital path instead of assuming it will appear during a blockspace crisis.
  • Size lot configuration, auction timing, and liquidation throughput against stressed bidder capacity rather than normal-market participation. A safe auction should remain competitive when many vaults are liquidated together, gas spikes, and nonce chains are under strain.
  • Distinguish liquidation can start from liquidation can restore solvency. If an auction may end before reaching tab, the protocol needs explicit bounds on acceptable underrecovery plus a precommitted recapitalization path.
  • Keep emergency modes ready before the crisis: longer bidding windows, smaller or more adaptive lot sizing, circuit breakers for stalled auctions, and pre-funded or pre-authorized backstop capital when organic keeper competition disappears.
  • Stress test mempool-access and transaction-ops assumptions directly. Bidder health should include replacement-transaction reliability, gas repricing speed, bundle/private-orderflow access, and the ability to keep multiple simultaneous auctions live.
  • Review oracle timing and liquidation batching together. Delayed recognition that dumps many unsafe vaults into one narrow auction window can turn a manageable liquidation design into a keeper-capacity failure.
  • Make the residual-loss surface explicit to governance and users. If failed auctions ultimately socialize losses through dilution or insurance, that is part of the liquidation design, not an afterthought.

Audit prompts

  • What economically meaningful lower bound applies to the very first bid when an auction opens, and what prevents a single surviving bidder from buying a large collateral lot for dust under congestion?
  • If keepers must hold internal balances, bridge assets, or specific venue access before they can bid, how many genuinely independent operators already satisfy those prerequisites during stress?
  • Can the auction close without reaching tab, and if so, what exact bad-debt ceiling, recapitalization path, and market-pause rule contain that underrecovery?
  • How many simultaneous liquidations can the system actually clear at stressed gas prices before bidder competition collapses below the level the solvency model assumes?
  • Which parts of liquidation readiness depend on mempool conditions, private relay access, nonce management, or gas-replacement behavior rather than on-chain math alone?
  • Does lot sizing reduce bidder-friction under crisis conditions, or does it accidentally force keepers to show up with more capital than the market can realistically mobilize at once?
  • If oracle delay or batch liquidation pushes many vaults into auction together, what automatic controls narrow the system before too many auctions at once becomes a solvency event?
  • Before governance promises user compensation or post-event dilution, what design change actually prevents healthy liquidation trigger, failed debt recovery from recurring?

Lessons that should already be standard

  • Liquidation is a liveness system, not just a pricing system. Correctly identifying an unsafe vault is not enough if the protocol cannot reliably convert seized collateral into debt repayment.
  • Opening from bid = 0 is not neutral. In a stressed market it means the real reserve price is whatever the last surviving bidder can get away with.
  • Keeper capital, mempool access, and transaction operations are security dependencies whenever solvency depends on offchain bidding.
  • An auction that may legally settle before covering the debt is already a recapitalization design. The question is whether that loss path is bounded and intentional.
  • Crisis-time liquidation assumptions must be written for the worst blockspace conditions, not for ordinary days with healthy competition.
  • Socialized recovery after failed liquidations can be necessary, but it is not the same thing as a liquidation mechanism that preserved solvency on its own.

Internal linkages

Control surface