Bonus — Stablecoin Economic Modeling

“Stablecoins are the highest-risk primitive in DeFi because the peg is a contract — not a Solidity contract, a social-economic contract — between code, capital, and incentives. The code can be flawless and the system can still die. Auditing a stablecoin means auditing the contract behind the contract: under which sequence of market states, behavioral responses, and oracle conditions does the peg cease to be true? If you cannot answer that question quantitatively, you cannot sign off on the audit.”

Tags: web3-security stablecoin defi cdp algorithmic peg depeg liquidation oracle economic-attack protocol-economic Learner: Past Tuan-08-DeFi-Security-AMM-Lending-Vault §5 (intro) → going deep on stablecoin design space Time: 5–7 days (4–6h/day) — denser than a normal bonus chapter; this is genuinely Week-08.5 Related: Tuan-08-DeFi-Security-AMM-Lending-Vault · Tuan-09-Oracle-MEV-Economic-Attack · Tuan-Bonus-Liquid-Staking-Restaking · Case-bZx-Price-Manipulation-2020 · Case-Euler-Finance-2023


1. Context & Why

1.1 Why a separate bonus chapter

Week 08 (Tuan-08-DeFi-Security-AMM-Lending-Vault) introduces stablecoins as one of four protocol families. That treatment is sufficient if you are auditing a consumer of a stablecoin (a lending market that lists DAI, a vault holding USDC, an AMM pool with USDT). It is not sufficient if you are auditing a stablecoin protocol itself, or a protocol whose risk is dominated by stablecoin exposure (most of DeFi, in 2026).

Three reasons stablecoin work deserves its own chapter:

  1. The bug class is economic, not lexical. Reentrancy you can find by reading code. A peg failure you can only find by writing a model and running it against shocks. The auditor who has only Slither, Echidna, and a checklist will never find the next UST. The one who has those plus a quantitative stress-test harness will.
  2. The blast radius is system-wide. A vulnerable lending protocol loses its own users. A vulnerable stablecoin takes down every protocol that holds it, every borrower who priced collateral in it, every CEX deposit denominated in it. The 2022 UST collapse vaporised ~$40B of value across LUNA, Anchor depositors, Curve LPs, and downstream lenders (Briola et al. 2022). The March 2023 USDC depeg pulled DAI, FRAX, and USDP off-peg within hours because the contagion paths were structural. (CoinDesk on Circle’s SVB exposure)
  3. The design space is alive. UST is gone but every cycle produces new algorithmic-adjacent designs (Ethena’s USDe + delta-neutral, Aave’s GHO, USDS / Sky, crvUSD’s LLAMMA, Liquity V2’s BOLD, M^0, f(x) Protocol, and a long tail of LRT-backed stables). Auditors will be asked to evaluate the next design — not just to grade Maker against Maker’s whitepaper.

1.2 The auditor’s framing

Strip away the marketing. Every stablecoin is the same machine:

peg_holds ⇔ ∀ market_states M:
              redeem_path(1 unit, M) → 1 USD of value, with bounded latency,
              AND  arbitrage_loop_closes(M)
              AND  collateral_solvent(M)

A stablecoin is “stable” iff, for every market state the protocol can plausibly encounter, (a) there exists a redemption or arbitrage path that delivers ~$1 of value per unit and (b) that path closes economically (someone makes money taking the trade) and (c) the system as a whole remains solvent.

The job of the stablecoin auditor is to enumerate the market states M and find one where any of those three properties fails. The market states include:

  • Sudden collateral price drops (10%, 30%, 50%, 80%).
  • Sudden DEX liquidity reductions (50%, 90%).
  • Oracle outage (5 min, 1 hour, indefinite).
  • Frozen redemption path (off-chain custodian, PSM cap hit, gas spike).
  • Coordinated holder exit (top-10 holders sell within an hour).
  • Correlated collateral failure (multiple ilks correlated; LST de-pegging vs ETH).
  • Governance action lag (parameter response slower than market).
  • Reflexive feedback through dependent protocols (lending → liquidation → AMM dump → oracle update → more liquidations).

Each of these is a scenario to model, not just a sentence to write. §5 of this lesson is the modeling lab.

1.3 Learning goals

By the end of this lesson, you can:

  • State the four-archetype taxonomy of stablecoins and, given an unfamiliar protocol, classify it with reasons.
  • Reproduce the UST collapse mechanism mathematically and simulate it in code; explain exactly why the design failed.
  • Audit a CDP stablecoin (Maker / LUSD / crvUSD class) for: oracle dependency, liquidation profitability, debt ceiling enforcement, redemption mechanics, governance latency.
  • Audit a hybrid stablecoin (FRAX class) for: AMO authority, collateral marking, peg-defense liquidity.
  • Audit a yield-bearing stablecoin (sDAI / sUSDe / USDS-savings class) for: rate-source integrity, share/asset rounding, redemption queue model.
  • Write a Foundry simulation that stress-tests a CDP under a collateral price drop and identifies the insolvency threshold.
  • Apply the §7 stablecoin audit checklist to a real protocol with named TVL on DeFiLlama.

1.4 Primary references

SourceURLStatus
MakerDAO Technical Docshttps://docs.makerdao.com/Current; verify ilk-list and post-Sky rename [verify]
MakerDAO Liquidation 2.0 (Dog + Clipper)https://docs.makerdao.com/smart-contract-modules/dog-and-clipper-detailed-documentationCurrent
Maker Black Thursday 2020 post-mortemhttps://blog.makerdao.com/recent-market-activity-and-next-steps/ · community recapHistorical; auction redesign was the response
Liquity V1 Whitepaperhttps://docs.liquity.org/documentation/liquity-v1-whitepaperCurrent; ETH-only, 110% MCR
Liquity V2 Docshttps://docs.liquity.org/v2-faq/general · https://docs.liquity.org/v2-overview/Current; BOLD stablecoin, multi-LST collateral, user-set interest rates [verify final mainnet params]
FRAX Docs — AMO Overviewhttps://docs.frax.finance/amo/overviewCurrent; CR transitioned toward 100% via FIP-188 (2023); verify present CR [verify] (CoinDesk on FIP-188)
crvUSD / LLAMMA Resourceshttps://resources.curve.finance/crvusd/loan-concepts/ · https://resources.curve.finance/crvusd/advanced-liquidation/Current; LLAMMA = soft liquidation via AMM bands
Briola, Vidal-Tomás, Wang, Aste — Anatomy of a Stablecoin’s Failure: Terra–Lunahttps://arxiv.org/pdf/2207.13914Academic post-mortem on UST collapse
Federal Reserve — Lessons from the SVB Failure and Its Impact on Stablecoinshttps://www.federalreserve.gov/econres/notes/feds-notes/in-the-shadow-of-bank-run-lessons-from-the-silicon-valley-bank-failure-and-its-impact-on-stablecoins-20251217.htmlCurrent; official empirical post-mortem on USDC depeg
Chainalysis — Crypto Market Reaction to SVB and USDC Depeghttps://www.chainalysis.com/blog/crypto-market-usdc-silicon-valley-bank/Historical; on-chain flow data for March 2023 depeg
Paradigm — Stablecoin Design (Robert Leshner archive, Sam Kazemian interviews, Hayden Adams notes)https://www.paradigm.xyz/writing (search “stablecoin”, “peg”)Curated research
a16z crypto — Stablecoin Researchhttps://a16zcrypto.com/research/ (filter stablecoin)Current
DeFiLlama — Stablecoins Dashboardhttps://defillama.com/stablecoinsLive data; required for Lab 3
Ethena Labs — USDe Whitepaperhttps://ethena-labs.gitbook.io/ethena-labsCurrent; delta-neutral synthetic dollar (perpetuals-funded)

2. The Stablecoin Taxonomy

The four-archetype taxonomy is the first tool. Before you audit anything you classify it, because the audit checklist depends on the class. Misclassifying a hybrid as a CDP means you under-audit the algorithmic side; misclassifying a yield-bearing wrapper as the underlying means you miss the rate-source dependency.

2.1 The four archetypes (plus a fifth wrapper layer)

flowchart TB
  subgraph A[Fiat-Backed Centralized]
    USDC[USDC]
    USDT[USDT]
    PYUSD[PYUSD]
    USDP[Pax USD]
  end
  subgraph B[Crypto-CDP / Over-Collateralized]
    DAI[DAI / Sky USDS]
    LUSD[LUSD / BOLD]
    crvUSD[crvUSD]
    GHO[GHO]
    sUSD[sUSD]
  end
  subgraph C[Algorithmic-Hybrid Fractional]
    FRAX[FRAX]
    RAI[RAI]
    FEI[FEI - retired]
  end
  subgraph D[Pure Algorithmic]
    UST[UST - collapsed 2022]
    ESD[Empty Set Dollar - retired]
    Basis[Basis Cash - retired]
  end
  subgraph E[Synthetic / Delta-Neutral]
    USDe[USDe / sUSDe]
    USDx[USDx and family]
  end
  subgraph F[Yield-Bearing Wrappers]
    sDAI[sDAI]
    sUSDS[sUSDS]
    sUSDe[sUSDe]
  end

  A --> F
  B --> F
  C --> F
  E --> F

  style D fill:#ffcccc
  style E fill:#fff2cc
  style F fill:#cfe2f3

The auditor’s classification table:

ArchetypeBackingPeg mechanismTrust assumptionFailure modeExamples
Fiat-backed centralizedOff-chain USD / Treasuries / cash equivalentsIssuer redemption at 1:1; primary marketTrust the issuer + the issuer’s bank + regulatorCustodial freeze, bank collapse, regulatory action, reserve mismatchUSDC, USDT, PYUSD, USDP
Crypto-CDPOn-chain collateral, over-collateralized (110–200%+)Liquidations + arbitrage + redemption modulesTrust the oracle + the liquidator market + governanceCascade liquidation, oracle manipulation, redemption-induced unwind, PSM concentrationDAI, USDS, LUSD, BOLD, crvUSD, GHO, sUSD
Algorithmic-hybrid (fractional)Partial collateral + AMO yield + governance tokenMint/burn arbitrage + AMO operations + PSMTrust collateral + algorithmic side under stress + AMO solvencyReflexive collapse on AMO side, depeg via PSM exhaustion, governance-token death spiralFRAX (pre-2023), RAI (with PID controller)
Pure algorithmicNo exogenous backingMint/burn against a sister token (LUNA)Trust the demand side of the systemDeath spiral as sister-token supply explodesUST/Terra (collapsed), ESD, Basis Cash
Synthetic / delta-neutralCrypto long + matching short on perps; net delta ≈ 0Funding rate captures yield, hedges priceTrust perp DEX solvency + funding sign + CEX custody if hedge is on CEXNegative funding for prolonged period, perp DEX insurance failure, custodian compromiseUSDe (Ethena)
Yield-bearing wrapperWraps any of the above with a yield-accruing rate (DSR, sSR, sUSDe yield)ERC-4626 share-price math; underlying stable’s pegUnderlying peg + rate source integrity + wrapper mathUnderlying peg failure ripples through; share-price manipulation in early life; rate source bugsDAI, sUSDS, sUSDe

Note on the “wrapper” row: a yield-bearing wrapper is not itself a stablecoin design — it’s an ERC-4626 vault over a stablecoin. The audit work is the wrapper’s accounting and rate-source, plus inheritance of the underlying’s stability risk. We treat wrappers in §6.

2.2 Why each class fails differently — the one-paragraph summary

  • Fiat-backed centralized fails off-chain. The on-chain contract may be perfect; the bank holding the reserves can still collapse. The auditor’s deliverable here is mostly: trust assumptions, redemption flow, blacklist semantics, upgrade authority, mint/burn key custody. Reserves are a counsel-and-attestation question, not a Solidity question.
  • Crypto-CDP fails under correlated collateral stress. Single-collateral CDPs (LUSD V1) are the simplest to model: one asset, one liquidation curve, one stability mechanism. Multi-collateral CDPs (DAI, Maker / Sky) add per-ilk parameter risk and cross-ilk contagion (if one ilk produces bad debt, the surplus buffer is shared). Liquidation failure (Black Thursday 2020) is the canonical stress test.
  • Algorithmic-hybrid fails when reflexive feedback dominates. The system is stable in normal regimes because arbitrage closes profitably; it becomes unstable when the algorithmic side is called on at a scale that overwhelms collateral. FRAX surviving the March 2023 USDC depeg required Frax governance to pivot to 100% collateral, exactly because the algorithmic premise broke under stress.
  • Pure algorithmic always fails eventually. We will model exactly why in §4.4. There is no published example of a pure algo stable that has survived a coordinated exit. UST, ESD, Basis Cash, ampleforth-as-pseudo-stable all collapsed or de-listed.
  • Synthetic / delta-neutral fails when the hedge breaks. The system holds a long position (e.g., ETH or stETH) and an equal short position (perp short). If the short can’t be maintained (perp DEX insolvent, funding flips deeply negative for an extended period, custodian freezes the hedge collateral), the synthetic dollar is naked-long the volatile asset.
  • Yield-bearing wrappers fail in the underlying, and they propagate that failure amplified because they are the form most often used as collateral in lending protocols. sDAI is more dangerous to a lending market than DAI because liquidations of sDAI must redeem to DAI first, adding a hop with rate risk.

2.3 The audit-class decision tree

flowchart TD
  start[Stablecoin under audit] --> q1{Has off-chain backing?}
  q1 -- "Yes, primarily fiat or Treasuries" --> fiat[Fiat-backed. Audit: redemption flow, blacklist, upgrade auth, attestation cadence]
  q1 -- "Partly, but mostly on-chain" --> q2{Pure algorithmic side?}
  q1 -- "No off-chain assets" --> q3{Backed by crypto > 100%?}
  q2 -- "Yes, partial collateral + algo arbitrage" --> hybrid[Hybrid. Audit: AMO authority, PSM concentration, CR mechanism]
  q2 -- "No, ~100% collateralized" --> q3
  q3 -- "Yes, over-collateralized" --> q4{Single or multi collateral?}
  q3 -- "No, mint-burn against sister token" --> algo[Pure algorithmic. Default severity: critical. Almost certainly will depeg under stress]
  q3 -- "Hedged with derivatives short" --> synth[Synthetic. Audit: funding-rate path, hedge custody, insurance fund]
  q4 -- "Single" --> cdp_single[Single-collateral CDP — LUSD shape. Audit: stability pool, redemption, recovery mode]
  q4 -- "Multi" --> cdp_multi[Multi-collateral CDP — Maker shape. Audit: per-ilk params, cross-contagion, PSM, ESM]
  cdp_single --> wrap{Has yield wrapper?}
  cdp_multi --> wrap
  hybrid --> wrap
  fiat --> wrap
  synth --> wrap
  wrap -- "Yes" --> wrap_audit[Plus: 4626 share math, rate source, queue model, first-depositor risk]
  wrap -- "No" --> done[Audit scope is the stablecoin itself]

Run this decision tree on the first day of every stablecoin audit. The output determines your checklist.


3. Peg Mechanics — How a Stablecoin Actually Stays at $1

This is the section most auditors skip. They take “the peg holds” as axiomatic. The peg is not axiomatic; the peg is the output of a specific set of forces. You can only find peg-failure bugs if you can name those forces and reason about when they invert.

3.1 The four peg-defense mechanisms

Every stablecoin uses some combination of these four:

MechanismHow it worksStrong inWeak in
Mint/burn arbitrageMint when price > 1 (buy market, redeem at $1)All classes; trivially symmetric in centralized & CDP designs; asymmetric in algoAsymmetric arbitrage paths (mint cheap, redemption blocked)
Liquidation incentivesWhen collateral coverage drops, liquidators buy collateral at discount; restores solvencyCDP designsLiquidator capital constrained; cascade-induced spread blowout; oracle stale
Stability module (PSM)Direct swap with another known stable (DAI ↔ USDC at 1:1)Maker, FRAXConcentrates risk in the counterparty stable (DAI’s USDC dependency); needs PSM liquidity
Treasury / market operationsProtocol holds reserve assets and actively defends the peg by buying/sellingMaker’s surplus buffer, FRAX AMOs, RAI’s PIDFront-runnable; political; usually slower than market

A robust stablecoin uses more than one — DAI has all four. LUSD V1 has mint/burn + liquidation incentives + a unique redemption mechanism (anyone can redeem 1 LUSD for $1 of ETH at any time, paid for by the riskiest trove). UST had only mint/burn. Hence UST is the canonical death-spiral case.

3.2 The arbitrage closure condition

The only reason a stablecoin trades near $1 in normal markets is that arbitrageurs profit by pushing it there. The arbitrage closure condition has three parts:

  1. A path exists. There must be some sequence of operations a third party can execute that converts 1 unit of stable into something worth 1 into 1 unit of stable, in the reverse direction).
  2. The path is profitable. Net of fees, gas, slippage, time, and capital cost, the arbitrageur makes money.
  3. The path closes in time. The market doesn’t move adversely faster than the arbitrageur can complete the trade.

Test each archetype:

ArchetypePathProfitable?Closes in time?
USDC trading at $0.95Buy 1 USDC for 1$0.05 minus feesOnly if Circle accepts redemption; in March 2023 redemption was frozen, the arb did not close
DAI trading at $1.02Deposit USDC into PSM, mint DAI 1:1, sell on Curve for $1.02$0.02 minus feesAlways, unless PSM debt ceiling is hit
LUSD trading at $0.98Buy 1 LUSD for 1 of ETH$0.02 minus 0.5% redemption fee minus gasAlways (LUSD has unconditional 1:1 redemption against the riskiest trove)
UST trading at $0.95Buy 1 UST for 1 of LUNA, sell LUNAMechanism profitableCloses only if LUNA market depth absorbs the mint without crashing LUNA
crvUSD trading at $1.01Mint crvUSD against collateral, sell on PegKeeper-balanced pool, repay laterYes, marginal; PegKeepers also automate thisGenerally yes; bounded by collateral availability

The auditor’s reflex: for the protocol under audit, write down all peg arbitrage paths and ask, for each, “under what market state does this path stop being profitable, or stop existing?” The answers are your depeg-condition findings.

3.3 The Maker / Sky reference architecture (study this once, you can read any CDP)

flowchart TB
  User[User] -->|deposit ETH| Vault[CDP / Vat ilk]
  Vault -->|mint DAI up to liquidation ratio| DAI[DAI / USDS]
  Vault -->|owes stability fee| SF[Stability Fee accrues]
  SF --> Vow[Vow: Surplus Buffer]
  Vault -->|if oracle px drops, vault is unsafe| Dog[Dog]
  Dog --> Clipper[Clipper: Dutch auction]
  Clipper --> Keeper[Keeper buys collateral at decaying price]
  Vow -->|surplus > bump| Flap[Flap auction: burn MKR]
  Vow -->|deficit > sump| Flop[Flop auction: mint MKR, dilution]
  PSM[PSM: USDC ↔ DAI 1:1] --> DAI
  OSM[Oracle Security Module: 1-hour delay] --> Vault
  ESM[Emergency Shutdown Module] -.last resort.-> Vault

The pieces and what each contributes to peg:

  • Vat: the central accounting. Vat.urns[ilk][user] tracks each vault’s collateral (ink) and debt (art). The core invariant: art * rate <= ink * spot * 1/mat for every urn (sufficient collateralization). Bugs in Vat are existential.
  • Spotter: reads the oracle, multiplies by mat (liquidation ratio), writes spot to the Vat. Spotter is what makes the OSM delay structurally enforceable.
  • Dog and Clipper (Liquidation 2.0): when a vault is unsafe, the Dog barks the vault; the Clipper opens a Dutch auction with starting price top = osm_price * buf (e.g., 1.30× current oracle) decaying via a Calc curve over tail seconds (e.g., exponential decay). Keepers call take to buy. The auction is atomic per call — no zero-bid pathology like the old Cat/Flip design after Black Thursday. (Liquidation 2.0 docs)
  • Vow: the surplus / debt buffer. Stability fees feed it; auction shortfalls drain it. If surplus accumulates beyond bump, Flap burns MKR. If debt accumulates beyond sump, Flop mints MKR (dilutes holders).
  • PSM: deposit USDC, mint DAI 1:1 (modulo small fee). The most effective peg-defense tool MakerDAO has, and the largest concentration risk: DAI’s USDC-dependence at peak was >50% via PSM.
  • OSM (Oracle Security Module): a 1-hour delay between oracle observation and Vat use. Stops flash-loan oracle manipulation cold. Trade-off: in a fast crash, vault holders get a 1-hour grace, but the protocol gets a 1-hour disadvantage.
  • ESM (Emergency Shutdown Module): MKR-staked emergency stop. When triggered, the system freezes; DAI holders can redeem against the residual collateral basket.

For each of these you should be able to recite, in audit: (a) who can call which function; (b) what invariant it preserves; (c) under which market state it breaks.

3.4 Liquity V1 / V2 reference — the radical-redemption design

Liquity V1 is the cleanest single-collateral CDP design ever shipped. It is worth memorising for contrast against Maker.

PropertyLiquity V1Maker / Sky
CollateralETH onlyMulti-asset, parameterised per ilk
Minimum Collateral Ratio (MCR)110% (one of the lowest in DeFi)150%–170% per ilk typically
Interest rate0% (one-time borrow fee instead)Stability fee, governance-set
Liquidation mechanismStability Pool absorbs liquidations atomicallyDutch auction (Clipper)
RedemptionAnyone can redeem LUSD for ETH at $1, against the riskiest trovePSM (DAI ↔ USDC), no protocol-native ETH redemption
GovernanceNone (immutable contracts)Active governance via MKR
Recovery ModeAt system TCR < 150%, MCR rises to 150%, liquidations expandNone (Maker uses Vow buffers)

The Stability Pool is the design idea worth dwelling on: depositors stake LUSD in the pool; when a trove is liquidated, the pool’s LUSD is burned to cover the trove’s debt and the pool receives the trove’s collateral at a discount. Atomic, no auction race, no zero-bid pathology. The trade-off is that the pool must always have enough LUSD to absorb the next liquidation — if it runs dry, debt is redistributed to other trove holders (a socialised loss).

Liquity V2 (mainnet 2025) generalised this:

  • Multiple collateral types (ETH + LSTs).
  • User-set interest rates: each borrower picks their own rate; the system redeems against the lowest-rate troves first, creating market discipline that pushes rates toward the system equilibrium.
  • Multi-collateral Stability Pools (one per market).
  • BOLD as the V2 stablecoin; LUSD remains live for V1 traditionalists.

[verify final mainnet params at audit time] (Liquity V2 FAQ)

The audit angle on Liquity V2: the user-set interest rate is a new market-design primitive. The auditor’s question: under coordinated griefing (one big borrower sets a rate so low everyone redeems against him, but he can flip-flop the rate constantly), can the redemption mechanism be turned against itself? This is exactly the kind of finding that hides in economic-mechanism territory, not in Solidity.

3.5 crvUSD and LLAMMA — the soft liquidation idea

Curve’s crvUSD takes a different design path: instead of binary “safe / liquidate”, it uses soft liquidation via an AMM (the LLAMMA — Lending Liquidating AMM Algorithm).

Mechanism, in plain terms (Curve LLAMMA resources):

  • Each loan deposits collateral into a set of N price bands, each band covering a small price range.
  • As the collateral price falls into a band, that band’s collateral is gradually converted into crvUSD via the band’s AMM. When the price recovers, the AMM reverses the conversion.
  • A loan is fully “soft liquidated” if the price falls below the lowest band; at that point all collateral has been converted to crvUSD. The loan is closeable but the borrower has not paid an auction premium — only the AMM’s slippage and trading fees.
  • PegKeepers are autonomous arbitrage contracts that mint or burn crvUSD into Curve pools to bring price back to $1 when it drifts.

The audit angle on LLAMMA:

  • Oracle path. LLAMMA uses an internal oracle (typically a TWAP / smoothed feed); a fast manipulation of the input oracle moves bands and triggers soft-liquidation. Verify oracle smoothing and TWAP windows.
  • Band-cross accounting. The hardest math in LLAMMA is when the price crosses band boundaries with non-trivial trade size — the bands must conserve value across the transition. This is the precise place to put fuzz tests with invariants like “total value of (collateral + crvUSD) in all bands is non-decreasing modulo fees”.
  • Loss-to-borrower vs auction-premium trade. Soft liquidation guarantees the borrower doesn’t get caught by a one-shot auction, but in oscillating markets the borrower bleeds value via repeated round-trips (sometimes called “the LLAMMA tax”). Documenting this in a finding is appropriate — it is design-by-intent but users may not understand it.
  • PegKeeper governance and bounds. PegKeepers have limits on how much they can mint; under sustained depeg pressure, those limits are the binding constraint. Audit the limits.

3.6 FRAX, AMOs, and the hybrid story

FRAX historically operated at a Collateral Ratio (CR) less than 100% — for example, at CR = 85%, each FRAX was backed by 0.15 redeemable in FXS (the governance token, minted on demand at redemption time). The CR adjusted dynamically based on FRAX market price: if FRAX traded below $1 for an extended period, CR was raised (more collateral, less algorithmic); if it traded above, CR fell (more algorithmic, more capital-efficient).

The AMO (Algorithmic Market Operations Controller) is the mechanism that made the under-collateralized model viable: AMOs are autonomous contracts that deploy idle protocol-owned collateral into yield (Curve pools, lending protocols, Treasuries) so long as their actions don’t move the FRAX peg. They produce protocol revenue that buys back FXS, supports peg defense, and over time can increase CR organically.

The community voted (FIP-188, Feb 2023) to move toward 100% collateralisation, citing UST’s collapse and the political climate around partially-algorithmic stables. As of 2025 FRAX targets 100% CR while continuing to run AMOs as yield engines, and is positioning around the Fraxtal L2. (CoinDesk on FIP-188, FRAX AMO docs) [verify CR and AMO inventory at audit time]

The audit angle on FRAX-style designs:

  • AMO authority. Who can deploy / withdraw via an AMO? Time-locked? Multisig? Single EOA? A single-EOA-controlled AMO is a multi-hundred-million-dollar honeypot.
  • AMO mark-to-market. AMOs hold positions in Curve gauges, Aave deposits, Convex stakes. The protocol’s “100% collateralisation” claim requires marking these positions correctly. A bug that values an illiquid Curve gauge position at face means the protocol looks fully backed but isn’t.
  • AMO unwind latency. If FRAX needs collateral to meet redemptions, AMO positions must unwind. Some positions (Curve gauge boosted with veCRV, locked Convex) cannot unwind quickly. Audit the worst-case unwind time vs the redemption pressure.
  • PSM concentration. Like Maker’s, FRAX’s PSM with USDC is the principal peg-defense liquidity. USDC depeg = FRAX depeg. This is a structural finding for the user, not a code bug.

3.7 Synthetic / delta-neutral: Ethena USDe as the new design space

Ethena’s USDe takes a fundamentally different approach: hold ETH (or LSTs / stETH) as long collateral and open an equal-size short on perpetual futures (executed on Bybit, Binance, OKX-style venues via off-exchange settlement). The net delta is zero — the position doesn’t move with ETH price.

The yield comes from two sources:

  • Funding rate: perp shorts collect funding from longs when funding is positive (the default in bull markets).
  • Staking yield: the underlying ETH / stETH still earns staking yield.

When the user wraps USDe in sUSDe, they get the accrued yield as the share-price appreciates.

The auditor’s risk model for USDe (and any delta-neutral synthetic):

RiskWhat it isSeverity in stress
Funding rate flips negativeIf perp funding goes negative for an extended period, shorts pay; yield turns to bleedReal; happened in 2022 bear market. Mitigated by yield reserve fund.
Perp DEX insolvencyA CEX collapses with collateral (FTX 2022 shape)Off-exchange settlement via custodian (Copper, Ceffu) mitigates but doesn’t eliminate
Custodian compromiseOff-exchange settlement custodian is compromised or freezes collateralBlack swan; insurance fund expected to absorb
Hedge slippage in fast movesIf ETH moves 20% in minutes, the short may be auto-deleveraged before USDe can rebalanceReal; insurance fund absorbs
Oracle / liquidation cascade on collateralLST de-peg vs ETH affects long sideReal; bounded by collateral choice
Negative basis on syntheticSpot-perp basis can go negative; net carry invertsReal

The audit angles are different from CDPs: less code-bug surface, more operational and market surface. The contract reads collateral attestations from the off-chain custodian, mints USDe against them; the bug class is closer to a bridge / oracle than to a Maker vault. Audit the attestation flow as if it were a bridge oracle.


4. Depeg Dynamics — How Stablecoins Actually Die

Now the part that you need to model, not just narrate.

4.1 The four shock types

Every depeg starts with one of four shocks:

ShockMechanismExample
Demand shock (sell-side)Holders sell faster than arbitrage can buy backUSDC March 2023
Supply shock (mint-side)New units minted faster than market can absorbUST May 2022 once burn-mint inverted; minor cases of unauthorized mint via bugs
Liquidation cascadeFalling collateral price triggers liquidations whose dumps push price further downDAI March 2020 (Black Thursday); CDP under stress
Run on redemptionHolders all redeem at once; redemption path becomes capacity-constrainedDAI/USDC March 2023 (PSM redemption from DAI to USDC); centralised stables under regulator stress

All real depegs are combinations. UST was demand-shock + supply-shock + reflexive loss of confidence in LUNA collateral. USDC March 2023 was demand-shock + redemption freeze (Friday/weekend SVB closure prevented Circle from honouring redemptions). DAI March 2020 was liquidation cascade plus zero-bid auction pathology.

4.2 Liquidation cascade — the mechanics

Consider a CDP system with N vaults at varying health factors. Assume oracle price drops by ΔP. The cascade:

  1. Some fraction of vaults become unsafe (HF < 1).
  2. Liquidators are triggered. Each liquidation sells collateral on the market (or, in Maker’s Clipper case, exposes collateral to buyers who themselves need to sell shortly).
  3. The collateral selling pushes spot price further down — call this the market impact of liquidations.
  4. The lower spot is propagated into the oracle (with some lag — Maker’s OSM 1-hour delay, Chainlink’s deviation threshold, etc.).
  5. More vaults become unsafe; goto step 1.

This is a positive-feedback loop. It terminates when either (a) all remaining vaults are deep above water; (b) the protocol runs out of liquidations to perform; or (c) the protocol becomes insolvent (collateral < debt across the system).

The auditor’s modeling question: at what collateral price drop ΔP does the system tip from “loss to some vaults” to “bad debt to the protocol”? This is solvable analytically for a single-asset CDP with closed-form distribution of vault health factors, and solvable numerically for any protocol. The Lab in §5 does it for a toy single-asset CDP.

4.3 Black Thursday (DAI, March 2020) — the canonical liquidation pathology

March 12–13, 2020: ETH dropped ~50% in 24 hours during the COVID market crash. Maker’s auction system (Cat/Flip at the time) suffered:

  • Gas prices spiked to >200 gwei due to network congestion; many keepers’ transactions failed or timed out.
  • Flip auctions had no minimum bid, allowing a keeper to submit bid = 0 and win if no one else bid.
  • A single keeper realised this and won ~$8.32M of ETH for zero DAI (community post-mortem).
  • Maker accumulated ~$5.7M of bad debt; MKR was minted via Flop auction to cover, diluting holders.

The lessons embedded in Liquidation 2.0 (Dog + Clipper):

  • Atomic Dutch auction: settles in one transaction; no zero-bid window.
  • Top-down price decay: starts above oracle (the buf parameter); keeper can wait but price always finds a buyer eventually.
  • Per-keeper incentive (chip and tip): explicit reward for triggering the auction, separate from auction profit, decouples the trigger from the bid.
  • Liquidation hop limit (Dog.Hole): per-ilk and global caps on liquidation throughput per block; prevents network congestion from cascading.

This single redesign moved Maker from “vulnerable to a 50% drop” to “survived a 30% intraday drop in March 2023 without bad debt”. The general lesson for CDP auditors: the liquidation mechanism is the protocol’s heart; its failure mode under congestion is the protocol’s failure mode. Auditing a liquidation function without a high-gas / high-volatility scenario is malpractice.

4.4 The UST death spiral — the math

UST’s mechanism (simplified):

mint 1 UST ↔ burn $1 of LUNA at oracle price
burn 1 UST ↔ mint $1 of LUNA at oracle price

Equivalently, define S_U = UST supply, S_L = LUNA supply, P_L = LUNA price. A user can always convert: 1 UST → $1 of LUNA = 1 / P_L LUNA. So burning UST mints 1 / P_L LUNA per unit.

Suppose UST trades below $1 — say, at price p_U < 1. Arbitrage:

  1. Buy 1 UST in the market for p_U dollars.
  2. Burn the UST; receive 1 / P_L LUNA, worth $1.
  3. Sell LUNA for $1.
  4. Profit: 1 - p_U per UST burned.

This pushes UST price up (buying pressure) and LUNA price down (selling pressure). The mechanism is correct in form — arbitrage closes when UST returns to $1.

The death spiral. Let D be UST supply being burned (in $); D / P_L is the LUNA being minted; mass selling of LUNA pushes P_L down by some impact function f(D). As P_L drops:

LUNA needed to absorb $1 of UST burn = 1 / P_L → ∞ as P_L → 0

If users keep burning UST (because they want out at $1, and arbitrage still appears profitable), LUNA supply explodes hyperbolically while LUNA price collapses. The arb is “profitable” up to the moment LUNA market depth refuses to absorb the new supply — at which point burning UST mints LUNA the arbitrageur cannot sell, and the mechanism halts.

In May 2022, this took ~3 days. LUNA went from ~1 to ~40B of market cap was destroyed (Briola et al. 2022, arXiv).

The auditor’s takeaway, generalised: any stablecoin whose collateral or backing asset is itself created by the system has a reflexive failure mode. If you can mint backing by burning debt, the mechanism is unstable in the limit. Always ask: “what is the exogenous backing, and what is the market depth of that backing under stress?”

The Anchor 20% yield was the demand sink that masked the instability — UST had artificial demand because Anchor paid 20% APY on deposits, mostly subsidised by the Luna Foundation Guard and protocol-controlled reserves. When the subsidy looked at risk (or when speculators concentrated their sells), the demand sink dried up faster than the supply mechanism could catch up.

4.5 USDC March 2023 — when fiat-backed fails off-chain

Friday March 10, 2023: Silicon Valley Bank was placed into receivership by the California Department of Financial Protection and Innovation. Circle disclosed that ~$3.3B of USDC reserves (~8% of total backing) was held at SVB. Until the FDIC’s Sunday March 12 announcement that all depositors would be made whole, USDC’s reserves were materially uncertain. (Federal Reserve note, 2025)

Market response:

  • USDC dropped to ~$0.87 by Saturday March 11 (CoinDesk timeline, Chainalysis on-chain analysis).
  • DAI followed to ~$0.89 because of PSM concentration: arbitrageurs took DAI from the PSM to redeem against USDC, but no one wanted the USDC; the price equilibrated at the USDC price.
  • FRAX (then at ~92% CR with USDC) followed similarly.
  • Tether (USDT), which had no SVB exposure, rose briefly to $1.01 (flight to perceived safety).
  • USDC’s primary market (Circle redemption) was effectively frozen because Friday banking hours closed and Circle could not move funds over the weekend.

Sunday March 12, the FDIC announced full coverage; USDC retraced to ~1 by Monday.

The lessons:

  • Off-chain banking is a peg dependency. The on-chain contract was perfect. USDC didn’t depeg because of Solidity; it depegged because the redemption window closed.
  • PSM concentration is contagion. DAI’s PSM made it functionally equivalent to USDC for the duration. The auditor finding “DAI is over 50% backed by USDC via PSM” is structurally a finding of Medium severity at minimum.
  • Arbitrage closure requires the redemption path. USDC arbitrage was a paper trade for 48 hours — buy at 1 — but the redemption was frozen. Always ask: “is the redemption path always open, or is it conditional on off-chain liveness?”
  • Time matters. The depeg lasted 48 hours; it became a non-event because the FDIC intervened. Without intervention, redemption flight would have continued. A protocol whose liquidations price USDC at “market” during those 48 hours would have under-collateralized loans en masse.

4.6 Other named events (auditor’s case-bibliography)

EventDateClassCauseOutcome
Maker Black ThursdayMar 2020CDP cascadeETH 50% drop + zero-bid auction bug + gas congestion$5.7M bad debt; auction redesign
UST collapseMay 2022Pure algoDemand-sink withdrawal + arbitrage hyperbolic~$40B destroyed
Tether brief dipMultipleFiat-backedReserve attestation concernsRecovered within hours each time
USDC depegMar 2023Fiat-backedSVB closure froze redemptionRecovered after FDIC intervention; DAI/FRAX contagion
sUSD (Synthetix) depegMar 2020, Mar 2023CDP with SNX backingCorrelated SNX collapse + thin liquidityRecovered each time; SNX has high collateralization (~500%)
Tron-USDD depegMultipleHybrid → algoInsufficient peg defense; reserve composition opaqueHas not recovered to par; trades persistently below $1
MIM (Magic Internet Money)2022CDPCollateral was UST (used as MIM collateral via Abracadabra)Cascade following UST collapse
deUSD / lybra / other small algos2023–2024VariousGenerally insufficient peg defense at scaleMost de-listed or remain micro-cap

Every one of these is worth a paragraph in your audit notes. Patterns repeat.


5. Lab — Stress-Test a Stablecoin Yourself

Three labs. The first two are mandatory; the third is a real-world audit exercise. Total ~12–18 hours.

5.1 Lab structure

~/web3-sec-lab/bonus-stablecoin/
├── 01-cdp/                   # Build & stress a single-collateral CDP
├── 02-ust-simulator/         # Model the UST collapse in code
└── 03-real-protocol-audit/   # Pick a stablecoin from DeFiLlama; write a risk report

5.2 Lab 1 — Build a single-collateral CDP and find its insolvency threshold

We build a minimal CDP system (single collateral = WETH, single stablecoin = USDk). Then we write a simulator that drops the WETH price across a range of values and identifies the price at which the protocol becomes insolvent (collateral value < total debt).

5.2.1 Setup

mkdir -p ~/web3-sec-lab/bonus-stablecoin/01-cdp && cd $_
forge init --no-commit single-cdp
cd single-cdp
forge install OpenZeppelin/openzeppelin-contracts --no-commit

5.2.2 The CDP contract

// src/SimpleCDP.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
 
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
/// @notice A minimal single-collateral CDP for stress-testing. Educational only.
contract USDk is ERC20 {
    address public minter;
    constructor() ERC20("USDk", "USDk") { minter = msg.sender; }
    function mint(address to, uint256 amount) external {
        require(msg.sender == minter, "only minter");
        _mint(to, amount);
    }
    function burn(address from, uint256 amount) external {
        require(msg.sender == minter, "only minter");
        _burn(from, amount);
    }
}
 
/// @notice Mock oracle. Owner can set price for stress testing.
contract MockOracle {
    uint256 public price;       // 8 decimals like Chainlink
    address public owner;
    constructor(uint256 _p) { price = _p; owner = msg.sender; }
    function setPrice(uint256 _p) external {
        require(msg.sender == owner, "only owner");
        price = _p;
    }
}
 
contract SimpleCDP {
    // Configuration
    uint256 public constant MCR = 150;            // 150% minimum collateral ratio
    uint256 public constant LIQ_BONUS = 105;      // 5% liquidator bonus
    uint256 public constant PRICE_DECIMALS = 1e8;
    uint256 public constant DUST_LIMIT = 100e18;  // 100 USDk minimum debt
 
    // State
    ERC20 public immutable weth;       // collateral token
    USDk public immutable usdk;        // debt token
    MockOracle public immutable oracle;
 
    struct Vault {
        uint256 collateral;   // wei of WETH
        uint256 debt;         // wei of USDk
    }
    mapping(address => Vault) public vaults;
    uint256 public totalDebt;
 
    constructor(address _weth, address _usdk, address _oracle) {
        weth = ERC20(_weth);
        usdk = USDk(_usdk);
        oracle = MockOracle(_oracle);
    }
 
    function _ethPriceUSD() internal view returns (uint256) {
        return oracle.price();  // 8-decimal
    }
 
    /// @notice Healthy iff collateral value * 100 >= debt * MCR.
    function isHealthy(address user) public view returns (bool) {
        Vault memory v = vaults[user];
        if (v.debt == 0) return true;
        uint256 collateralUSD = (v.collateral * _ethPriceUSD()) / PRICE_DECIMALS;
        return collateralUSD * 100 >= v.debt * MCR;
    }
 
    function deposit(uint256 amount) external {
        weth.transferFrom(msg.sender, address(this), amount);
        vaults[msg.sender].collateral += amount;
    }
 
    function borrow(uint256 amount) external {
        require(amount > 0, "zero borrow");
        Vault storage v = vaults[msg.sender];
        v.debt += amount;
        totalDebt += amount;
        require(v.debt >= DUST_LIMIT, "below dust");
        require(isHealthy(msg.sender), "would be unsafe");
        usdk.mint(msg.sender, amount);
    }
 
    function repay(uint256 amount) external {
        Vault storage v = vaults[msg.sender];
        require(amount <= v.debt, "over-repay");
        usdk.burn(msg.sender, amount);
        v.debt -= amount;
        totalDebt -= amount;
        require(v.debt == 0 || v.debt >= DUST_LIMIT, "leaves dust");
    }
 
    function withdraw(uint256 amount) external {
        Vault storage v = vaults[msg.sender];
        require(amount <= v.collateral, "over-withdraw");
        v.collateral -= amount;
        require(isHealthy(msg.sender), "would be unsafe");
        weth.transfer(msg.sender, amount);
    }
 
    /// @notice Liquidator pays debt in USDk, receives collateral worth debt*LIQ_BONUS/100.
    /// @dev This is the simplest possible liquidation. In a real protocol you'd
    ///      have partial liquidations, auctions, etc.
    function liquidate(address user) external {
        require(!isHealthy(user), "healthy");
        Vault storage v = vaults[user];
        uint256 debt = v.debt;
        // Collateral seized = debt * LIQ_BONUS / 100 / ETH price
        uint256 collateralToSeize = (debt * LIQ_BONUS * PRICE_DECIMALS) / (100 * _ethPriceUSD());
        uint256 actualSeize = collateralToSeize > v.collateral ? v.collateral : collateralToSeize;
        // Burn liquidator's USDk equal to debt
        usdk.burn(msg.sender, debt);
        // Reduce vault state
        v.collateral -= actualSeize;
        v.debt = 0;
        totalDebt -= debt;
        // Transfer collateral to liquidator
        weth.transfer(msg.sender, actualSeize);
        // If collateral fully consumed but debt was higher → bad debt; in this
        // educational version we just emit an event. Production: track explicitly.
        if (collateralToSeize > v.collateral + actualSeize) {
            // (bad debt left in protocol; not handled here)
        }
    }
}

Notes already worth a finding in a real audit: dust on liquidation is not enforced, partial liquidation is missing, bad debt is silently absorbed (or rather, ignored). We will use this contract precisely because it’s flawed — to demonstrate the modeling workflow.

5.2.3 The stress-test simulator

// test/StressTest.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
 
import "forge-std/Test.sol";
import "../src/SimpleCDP.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
 
contract WETH is ERC20 {
    constructor() ERC20("WETH", "WETH") {}
    function mint(address to, uint256 amount) external { _mint(to, amount); }
}
 
contract StressTest is Test {
    WETH weth;
    USDk usdk;
    MockOracle oracle;
    SimpleCDP cdp;
 
    // Borrowers
    address[20] borrowers;
 
    function setUp() public {
        weth = new WETH();
        usdk = new USDk();
        oracle = new MockOracle(2000e8);  // ETH = $2000 initially
        cdp = new SimpleCDP(address(weth), address(usdk), address(oracle));
        usdk.transferOwnership; // skip ownership transfer; minter is test contract
        // hack: minter is whoever deployed USDk (test contract); transfer minter authority
        vm.store(address(usdk), bytes32(uint256(5)), bytes32(uint256(uint160(address(cdp)))));
        // ^ implementation-dependent; check storage slot in your Foundry run
 
        // 20 borrowers with varying collateralization
        for (uint256 i = 0; i < 20; i++) {
            address b = address(uint160(0x1000 + i));
            borrowers[i] = b;
            weth.mint(b, 10 ether);
            vm.startPrank(b);
            weth.approve(address(cdp), type(uint256).max);
            cdp.deposit(10 ether);
            // Borrow varying amounts: borrower i borrows (100 + i*50)% / MCR of their collateral
            // i=0 → 100% / 1.5 = 66% LTV; i=19 → close to MCR limit
            uint256 maxBorrowUSD = (10 ether * 2000e8 * 100) / (1e8 * cdp.MCR());
            uint256 borrowUSD = (maxBorrowUSD * (50 + i * 2)) / 100;
            // Convert from "USD units, 1e18 scale" — for the lab we pretend 1 USDk = $1
            cdp.borrow(borrowUSD);
            vm.stopPrank();
        }
    }
 
    function test_insolvency_threshold() public {
        // Drop ETH price in steps; check after each drop:
        //   - how many borrowers are liquidatable
        //   - what's the system collateral value vs system debt
        //   - is the system insolvent?
        uint256[] memory pricePoints = new uint256[](20);
        for (uint256 i = 0; i < 20; i++) {
            pricePoints[i] = 2000e8 - (i * 100e8);  // $2000 → $100
        }
 
        for (uint256 i = 0; i < pricePoints.length; i++) {
            oracle.setPrice(pricePoints[i]);
            uint256 unhealthy = 0;
            uint256 totalCollateral = 0;
            for (uint256 j = 0; j < 20; j++) {
                if (!cdp.isHealthy(borrowers[j])) unhealthy++;
                (uint256 c, ) = cdp.vaults(borrowers[j]);
                totalCollateral += c;
            }
            uint256 collateralUSD = (totalCollateral * pricePoints[i]) / 1e8;
            uint256 td = cdp.totalDebt();
            bool insolvent = collateralUSD < td;
            emit log_named_uint("price (USD)", pricePoints[i] / 1e8);
            emit log_named_uint("unhealthy vaults", unhealthy);
            emit log_named_uint("collateral USD", collateralUSD);
            emit log_named_uint("total debt", td);
            emit log_named_string("insolvent", insolvent ? "YES" : "no");
            emit log("---");
            if (insolvent) {
                emit log_named_uint("INSOLVENCY AT PRICE", pricePoints[i] / 1e8);
                return;
            }
        }
    }
}

Run:

forge test --match-test test_insolvency_threshold -vv

5.2.4 Expected output and interpretation

You should see a table:

price (USD): 2000  unhealthy: 0  collateralUSD: 400000  debt: ~177000  insolvent: no
price (USD): 1900  unhealthy: 0   ...
...
price (USD): 1400  unhealthy: 5   ...
price (USD): 1000  unhealthy: 15  collateralUSD: 200000  debt: 177000  insolvent: no
price (USD): 800   unhealthy: 20  collateralUSD: 160000  debt: 177000  insolvent: YES
INSOLVENCY AT PRICE: 800

(Numbers depend on your borrower configuration; replicate the trend, not the exact figures.)

Interpretation:

  • The system is healthy when collateral USD ≥ total debt; “unhealthy vaults” measures who would be liquidated, not insolvency. Liquidation transfers the unhealthy collateral to liquidators at a discount; the protocol’s solvency depends on whether total seized collateral still exceeds total debt.
  • The system transitions from “loss to borrower” (vaults below MCR but above 100%) to “bad debt to protocol” when collateral USD < debt. In our 150% MCR design with up to ~75% LTV vaults, this happens around a 50–60% price drop (collateral worth less than debt).
  • This is the toy version; real protocols add a stability fee accrual (compounds debt), a liquidation bonus (collateral leaves protocol faster than debt drops), and dust limits (un-liquidatable scraps left over).

Stretch tasks:

  1. Add a stability fee (e.g., 5% APY accrual on debt) and re-run. The insolvency threshold rises (debt grows over time).
  2. Add a liquidation bonus payout in collateral (already in the contract) and observe that protocol-side collateral drops faster than debt — making insolvency happen at higher prices than naive math suggests.
  3. Add a simulated liquidator who can’t always show up (e.g., 30% of liquidations succeed) and observe the cascade.
  4. Add a market impact function: each liquidation reduces oracle price by 0.5% (simulating that liquidators dump on the market). Observe the reflexive feedback.

The last stretch is the most important. In a stressed market liquidations cause further price drops; the “static price drop” analysis underestimates risk by 2–3x.

5.3 Lab 2 — UST collapse simulator

We model the UST mechanism: the mint/burn arbitrage against LUNA, with a finite-depth LUNA market.

# ust_simulator.py
# Run with: python3 ust_simulator.py
import math
 
def simulate_ust_collapse(
    initial_ust_supply=18_000_000_000,    # $18B at peak
    initial_luna_supply=350_000_000,       # ~350M LUNA pre-collapse
    initial_luna_price=80.0,                # ~$80
    sell_pressure_pct_per_step=0.02,        # 2% of remaining UST sold per step
    luna_market_depth_usd=200_000_000,      # depth that absorbs $X without major slippage
    arb_aggression=0.5,                     # what fraction of UST below peg is arb'd per step
    steps=300,
):
    """
    Model: each step, some fraction of holders sell UST. Arbitrageurs buy
    UST in the market and burn it for LUNA at the oracle price. The LUNA
    is sold, which moves LUNA price down according to market impact.
    """
    ust_supply = initial_ust_supply
    luna_supply = initial_luna_supply
    luna_price = initial_luna_price
    ust_price = 1.0
 
    history = []
 
    for step in range(steps):
        # 1) Sell pressure: some fraction of UST holders sell into market
        sell_volume_usd = ust_supply * sell_pressure_pct_per_step
        # naive: each $1 of sell pressure pushes UST price down by some elasticity
        ust_price_impact = sell_volume_usd / (10 * luna_market_depth_usd)
        ust_price = max(0.01, ust_price - ust_price_impact)
 
        # 2) Arbitrage: arbs buy UST in market, burn for LUNA
        if ust_price < 0.999:
            arb_size_usd = ust_supply * sell_pressure_pct_per_step * arb_aggression
            ust_burned = arb_size_usd / ust_price
            luna_minted = (ust_burned * 1.0) / luna_price  # burn 1 UST → $1 of LUNA
            luna_supply += luna_minted
            ust_supply -= ust_burned
 
            # Arbs sell LUNA; market impact on LUNA price
            luna_sell_usd = luna_minted * luna_price
            luna_impact_pct = luna_sell_usd / luna_market_depth_usd
            luna_price = luna_price * max(0.001, (1 - luna_impact_pct))
 
            # Each UST burned also slightly pushes UST price up (less supply)
            ust_price = min(1.0, ust_price + 0.1 * (ust_burned / ust_supply))
 
        history.append({
            "step": step,
            "ust_supply": ust_supply,
            "ust_price": ust_price,
            "luna_supply": luna_supply,
            "luna_price": luna_price,
        })
 
        if luna_price < 0.01 or ust_price < 0.05:
            print(f"--- Collapse complete at step {step} ---")
            break
 
    return history
 
if __name__ == "__main__":
    h = simulate_ust_collapse()
    for i, snap in enumerate(h):
        if i % 10 == 0 or i == len(h) - 1:
            print(f"step={snap['step']:3}  ust_supply=${snap['ust_supply']/1e9:6.2f}B  "
                  f"ust_px=${snap['ust_price']:.4f}  "
                  f"luna_supply={snap['luna_supply']/1e6:8.1f}M  "
                  f"luna_px=${snap['luna_price']:6.4f}")

Run:

python3 ust_simulator.py

You should see, with default parameters:

  • UST supply slowly declining (burns dominate); UST price oscillating then sinking.
  • LUNA supply exploding (from 350M to billions then trillions).
  • LUNA price collapsing toward zero hyperbolically.
  • Termination when LUNA price hits ~$0.01.

Tasks:

  1. Tune sell_pressure_pct_per_step to see how fast collapse happens under various run scenarios.
  2. Tune luna_market_depth_usd upward — at what depth does the system not collapse? (Hint: in 2022 LUNA’s market depth was insufficient by orders of magnitude.)
  3. Add a protocol_buyback term: each step the protocol can spend $X buying UST in market. Find the protocol-buyback rate at which the spiral is averted. This is what LFG’s BTC reserve attempted — and failed — to do.
  4. Add anchor_run: at step T, simulate the Anchor 20% yield protocol’s reserves running dry (a step-function drop in UST demand). Observe the impact.
  5. Compare to a CDP-backed stable: replace the LUNA-mint with a “deposit collateral worth $X to mint X UST”, and observe that the death spiral does not occur — the supply-mint side is exogenously bounded.

The audit takeaway: in your audit report, this model becomes a quantitative finding. “Under sell pressure of >X% per period and LUNA-equivalent market depth of <Y, the system collapses in <Z steps” is dramatically more credible than “the algo design is fragile”. Auditors paid the top end of the market write the model.

5.4 Lab 3 — Real-protocol risk audit

Pick a stablecoin from DeFiLlama Stablecoins that is not USDC, USDT, or DAI. Suggested candidates (any era): GHO, crvUSD, BOLD, USDe, USDS, eUSD, sUSD. Pick one with >$50M circulating supply and accessible documentation.

Write a 1500–2500 word risk report addressing, for that protocol:

  1. Classification: which of the four (or five) archetypes from §2.1.
  2. Backing decomposition: by asset class, with %s. Identify the riskiest backing component and rationale.
  3. Peg-defense mechanisms: which of the four from §3.1; how each works in this protocol.
  4. Failure scenarios: write three scenarios — demand shock, collateral shock, redemption freeze — that would depeg this protocol. For each, name the specific parameter or path that would fail.
  5. Governance and emergency surface: who can change critical params? Time-locks? Multisig composition?
  6. Audit findings list: 5–10 items in the style of an audit report (Critical / High / Medium / Low), with rationale.
  7. One quantitative model: stress this protocol with a chosen shock and produce a numerical insolvency or depeg threshold.

This is the deliverable that an auditor would actually submit. Treat it as a writing exercise as much as an analytical one.


6. Yield-Bearing Wrappers — sDAI, sUSDe, sUSDS, and Composability Risk

A yield-bearing stablecoin wraps an underlying stable in an ERC-4626-style vault that accrues yield by a defined mechanism. This is the dominant pattern in 2024–2026 because it lets DeFi protocols hold a single “yield-bearing dollar” without needing to manage rate accrual themselves.

WrapperUnderlyingYield source
sDAIDAIDai Savings Rate (DSR), set by Maker governance
sUSDSUSDSSky Savings Rate, set by Sky governance
sUSDeUSDeFunding rate + staking yield captured by Ethena
sFRAXFRAXProtocol fees + AMO yield
sFRXUSDFRXUSDFrax v3 successor wrapper

6.1 The ERC-4626 share math

Recall from Tuan-07-Token-Standards-Integration-Risk (and Week 08 §4): an ERC-4626 vault holds underlying asset, mints shares to depositors, and tracks the share price via convertToAssets(shares) and convertToShares(assets).

The four rounding-direction rules:

FunctionRoundingWhy
deposit(assets) returns sharesfloorUser pays N assets, receives ≤ N×rate shares; vault rounds in its favor
mint(shares) returns assetsceilUser wants exactly N shares, pays ≥ N/rate assets; vault rounds in its favor
withdraw(assets) returns sharesceilUser wants exactly N assets, burns ≥ N×rate shares; vault rounds in its favor
redeem(shares) returns assetsfloorUser burns N shares, receives ≤ N/rate assets; vault rounds in its favor

The pattern: rounding always favors the vault (existing depositors), never the depositor. An audit finding for any wrapper that rounds the other way.

6.2 The first-depositor / inflation attack on yield wrappers

A clean ERC-4626 wrapper has a critical edge case at zero TVL: if totalSupply == 0, the first depositor can be donation-attacked.

// Attacker: deposit 1 wei → receives 1 share.
// Attacker: donate 1,000 underlying directly to vault (transfer, not deposit).
// Now: 1 share = 1001 underlying.
// Victim deposits 999 → receives floor(999 * 1 / 1001) = 0 shares.
// Attacker withdraws their 1 share → receives all 1000 underlying.

Mitigations (every modern wrapper uses one or more):

  • Virtual shares: OpenZeppelin’s ERC4626 uses decimalsOffset to inflate the initial share supply; an attacker would need to donate 10^offset underlying to skew the math meaningfully.
  • Permanent initial deposit: protocol or governance deposits a non-trivial amount and locks the resulting shares.
  • Donation-blocking accounting: track totalAssets from internal state, not asset.balanceOf(address(this)).

Audit reflex: when reading any 4626 vault, the first thing you check is convertToShares(amount) and convertToAssets(shares) when totalSupply == 0. If those return naïve values (e.g., 1:1), the inflation attack is open.

6.3 Rate-source dependency

A wrapper’s share price depends on the wrapper’s yield-source claim. Three rate-source patterns:

  1. Periodic mint (DSR style): the protocol mints new underlying into the vault at the savings rate; share price tracks accumulated mint per share.
  2. Internal accumulator (sUSDe style): a vesting mechanism unlocks yield linearly between epochs; the wrapper’s totalAssets increases over time without external mints.
  3. External rebase (sFRAX-like): the wrapper holds a rebasing underlying and the rebase rate is the yield.

Audit angles:

  • Rate source authority: who can change the rate? sUSDe’s yield comes from off-chain trading; the protocol publishes attestations. sDAI’s rate is on-chain DSR. The off-chain ones have more trust assumptions.
  • Rate-source manipulation: can a rate jump be front-run? A naive vault that updates rate via setRate(r) and immediately re-prices shares lets the caller MEV the difference. Mitigation: rate changes apply over time, not instantly.
  • Cross-protocol rate inheritance: a lending protocol that accepts sUSDe as collateral and uses its convertToAssets() view as the price implicitly inherits Ethena’s funding-rate risk. The lending market may not be aware.

6.4 Composability — the audit chains across wrappers

A modern lending protocol might:

  • Accept sUSDe as collateral, valued via convertToAssets().
  • sUSDe is backed by USDe, valued at 1:1 internally.
  • USDe is backed by ETH + perp short, marked via custodian attestations.
  • The custodian holds off-exchange collateral with Bybit / Binance / OKX-style venues.

Risks propagate downward through this chain. A custodian compromise affects USDe, which affects sUSDe’s redemption, which affects the lending market’s collateral value, which affects every lender’s solvency. Every wrapper is a dependency edge in your audit’s trust graph.

For each wrapper your audit’s protocol depends on, write:

  • The underlying.
  • The yield source and authority.
  • The redemption path and its conditional liveness.
  • The worst-case shock that breaks the share-price assumption.

This is mechanical work; do it for every wrapper, every audit.


7. The Stablecoin Audit Checklist

Apply this checklist to every stablecoin or stablecoin-adjacent protocol. Items are grouped by topic. Severity calibration assumes a stablecoin with >$10M circulating supply; smaller deployments may downgrade some items.

7.1 Classification & taxonomy

  • Protocol’s archetype is documented (CDP / hybrid / pure algo / synthetic / fiat-backed); decision-tree result from §2.3 attached.
  • All listed collateral types enumerated with %, liquidity profile, depeg history.
  • Yield source (if any) documented with authority and rate-change mechanism.
  • Cross-protocol dependencies graphed (oracles, AMMs, custodians, partner protocols).

7.2 Collateral and backing

  • Backing ratio is documented and machine-readable (a contract view, not just a marketing page).
  • For each collateral type: oracle source, fallback oracle, deviation thresholds, staleness checks.
  • Per-collateral debt ceiling enforced in code (Maker Vat.line style).
  • Per-collateral liquidation ratio enforced; no silent ratio change.
  • For correlated collaterals: documented; correlation parameter (eMode, pool-type) used to avoid contagion.
  • For off-chain backing: attestation cadence; on-chain proof-of-reserves; failure mode if attestation pauses.
  • LST collateral has explicit handling for slashing, rebase semantics, and withdrawal-queue latency.

7.3 Oracle and price path

  • Spot price for liquidation distinct from spot for borrowing (avoids self-feed manipulation).
  • OSM / TWAP / smoothing for price-feed-to-state transition (1-hour Maker style or equivalent).
  • Oracle staleness check with revert if last update older than X minutes.
  • Fallback oracle wired and tested.
  • No external view function is used as a price source for another protocol if that view is updatable mid-transaction (read-only reentrancy, Tuan-05-Vulnerability-Classes-Part-1 §2.2.4).

7.4 Mint / burn / supply

  • All mint paths enumerated and gated by collateral check + access control.
  • No path increases totalSupply without a deposit (or its accounting equivalent).
  • No path decreases totalSupply outside redemption / repayment.
  • PSM-style modules have explicit caps (gem-equivalent debt ceilings).
  • Mint-rate limits per block / per epoch for protective throttling.

7.5 Liquidation

  • Liquidation function exists, is permissionless, and remains profitable at current parameters.
  • Partial liquidation supported; close-factor encoded.
  • Dust limit on remaining debt after liquidation; no “uneconomical to liquidate” trove residue.
  • Auction (if any) is atomic — no zero-bid window (Black Thursday lesson).
  • Per-block / per-asset liquidation throughput cap (Maker Dog.Hole).
  • Stability Pool–style fallback or socialized-loss accounting if liquidation auctions fail.
  • Liquidator capital constraints modeled: would a generic liquidator find this profitable at 10× market gas? at 100×?
  • Cross-collateral isolation: bad debt in one ilk does not seize collateral from another.

7.6 Redemption

  • Redemption path defined; counterparty enumerated.
  • Redemption fee accrues to protocol or LPs (not lost).
  • Redemption rate-limit / cooldown / queue if applicable; behavior under maximum redemption pressure.
  • Off-chain redemption dependencies (banking, custodian) documented and noted in trust assumptions.

7.7 Stability / yield modules

  • PSM caps (debt ceiling, swap fees, both directions).
  • AMO authority is time-locked or multisig; no single-EOA AMOs.
  • AMO mark-to-market is correct under illiquid positions.
  • Treasury / surplus buffer is sized vs historical drawdowns.
  • Backstop mechanism (MKR-mint flop, FXS dilution, etc.) is documented; dilution math correct.

7.8 Governance and parameters

  • Privileged setters enumerated; each protected by timelock (typically 24–48h minimum).
  • Parameter bounds enforced on-chain (e.g., MCR cannot be set below 105%).
  • Emergency Shutdown / pause is documented; authorisation is multi-party (not single EOA).
  • Ramping mechanism for sensitive parameters (A in StableSwap; LTV in lending) to avoid step-shock.
  • Governance attack surface considered (flash-loan governance — see Tuan-08-DeFi-Security-AMM-Lending-Vault §3.7 and Beanstalk).

7.9 Stress and contagion

  • §5.2-style stress test run for primary collateral: insolvency price threshold documented.
  • Market-impact-on-liquidation modeled (reflexive feedback).
  • PSM concentration risk documented (e.g., “X% of supply backed by USDC via PSM; USDC depeg = stablecoin depeg”).
  • Cross-protocol exposure documented (lending markets, AMM pools, vault holdings of this stable).

7.10 Yield wrapper (if applicable)

  • ERC-4626 rounding directions correct (4 rules).
  • First-depositor inflation attack mitigated (virtual shares or permanent initial deposit).
  • Rate-source authority and change cadence documented.
  • convertToAssets / convertToShares are not external-reentrancy-readable mid-state-transition.
  • If wrapped underlying can depeg, the wrapper’s pricing handles it (rate manipulation, share price floor).

7.11 Off-chain / operational

  • For fiat-backed: attestation firm, cadence, banking jurisdiction, regulatory posture.
  • For synthetic: custodian compromise scenario; perp DEX insolvency scenario; funding-flip risk.
  • Incident-response runbook: who can pause, who can trigger ESM, what does each step take in time.
  • Bug bounty in place; severity matched to TVL.

8. Anti-patterns Catalog

Add to your master audit checklist (audit-checklist-master):

  • Pure algorithmic supply mechanism with no exogenous backing. Default Critical. There is no published example of one surviving stress.
  • Backing asset partly minted by the system itself (e.g., UST backed by LUNA which is freely mintable by burning UST). Reflexive instability — default High to Critical.
  • Single oracle with no fallback for collateral pricing.
  • Oracle read directly from a manipulable AMM (Uniswap V2 spot, V3 slot0 without TWAP, AMM get_virtual_price during liquidity ops).
  • No price-update delay (OSM equivalent) between oracle and liquidation engine in a CDP. Flash-loan liquidations possible.
  • Liquidation auction with no minimum bid / zero-bid window (Black Thursday pathology).
  • Liquidation throughput unlimited per block — congestion-driven cascade can run unbounded.
  • PSM with no debt ceiling — unbounded mint of stablecoin against a single off-chain asset.
  • PSM concentration > 30% of stablecoin supply backed by one fiat-stable. Document as Medium-or-higher contagion finding.
  • AMO controlled by single EOA or untimelocked multisig.
  • AMO mark-to-market via Chainlink only, when AMO holds illiquid LP positions whose execution price differs from the Chainlink ETH/USD-style feed.
  • Stability fee accrual not applied on every state-changing function — debt drift over time.
  • Recovery Mode / ESM key in a single multisig without on-chain redundancy.
  • Stablecoin used as own collateral in a lending market on the same protocol without isolation — Euler-style donation surface.
  • ERC-4626 wrapper with naïve initial share price (1:1 at zero TVL) — inflation attack.
  • ERC-4626 wrapper computing totalAssets() via asset.balanceOf(address(this)) without donation guard.
  • Yield wrapper’s rate updateable in one tx without smoothing — MEV-able rate jump.
  • No public dashboard / view for real-time backing composition.
  • Redemption path requires off-chain liveness without documented contingency.
  • Liquid Restaking Token (LRT) used as CDP collateral without modeling restaking slashing pass-through.
  • Cross-chain stablecoin minted on multiple chains without unified supply accounting (Multichain / Wormhole-style risk).
  • Borrow-side or collateral-side stablecoin is itself rebasing without explicit unit-economic-handling in the consumer.

9. Trade-offs and Open Debates

DecisionOption AOption BAuditor’s view
Collateral diversitySingle asset (LUSD V1)Multi-asset (Maker / Sky)Single is simpler to audit and reason about; multi is more capital-efficient and resilient via diversification, but each new collateral type doubles audit surface. Default: insist on per-asset isolation modes for any new ilk.
Liquidation styleAuction (Maker Clipper)Stability Pool (Liquity)SP is more elegant and atomic; auction handles deeper liquidations and broader keeper participation. For a multi-billion stable, want both.
Liquidation incentiveFixed bonus (5%)Dynamic / curve-decay (Clipper)Fixed is simpler; dynamic finds the marginal liquidator under stress. Prefer dynamic for any stable with >$100M circulating.
Soft liquidationOff (binary)On (crvUSD LLAMMA)Soft reduces user-visible liquidation events but bleeds value in oscillating markets. Document the user-side trade-off in the audit.
PSMYes (Maker, FRAX)No (LUSD, crvUSD relies on PegKeepers + redemption)PSM is the most effective short-term defense but concentrates risk in the counterparty stable. Hybrid: cap PSM at 30% of supply.
Yield modelNone (LUSD V1, BOLD-ish)Native yield wrapper (sDAI, sUSDe)Yield brings composability and demand; it also adds rate-source risk and a 4626 audit surface. Both reasonable; document trade-off.
GovernanceNone (LUSD V1 immutable)Active (Maker / Sky MKR)Immutable simplifies audit and removes governance attack surface but cannot respond to novel risk. Most protocols need governance; the audit task is to bound governance authority via timelocks and parameter caps.
Algorithmic sideZero (post-FRAX FIP-188)Partial (pre-2023 FRAX)Post-UST, market and regulatory pressure favor zero. Hard to argue for an algo side in 2026 without strong novel mitigations.
Stablecoin pricing in a wrapper4626 convertToAssetsOracle on the wrapper itselfThe 4626 view inherits any underlying depeg silently — bad for collateral pricing. Use an oracle that prices the wrapper directly when used as collateral, even if it’s an internal one.

10. Quiz (≥80% to advance)

  1. Q: A protocol describes itself as “decentralised algorithmic stablecoin with partial collateralisation”. What is the highest-severity audit finding you can write before reading any code? A: The hybrid / algorithmic class has a structural reflexive failure mode (UST-style death spiral under demand shock) when the algorithmic side is called on at scale. Default severity: at least High, depending on what fraction is algorithmic and how the system handles sustained sub-peg pressure. The finding stands even with perfectly-audited Solidity.

  2. Q: DAI traded at $0.89 during March 2023. Was this a code bug, an economic-design issue, or an off-chain failure? Severity in an audit? A: An economic-design issue rooted in PSM concentration: DAI was over 50% backed by USDC via the PSM, so a USDC depeg passed through directly. No code bug. Severity in an audit of DAI: Medium-to-High structural finding (PSM concentration risk). In an audit of a protocol holding DAI as collateral: a Medium finding that DAI’s pricing should account for its USDC inheritance under stress.

  3. Q: A CDP protocol uses Chainlink ETH/USD for both the borrow-time and the liquidation-time price, with no OSM-style delay. What’s the specific attack? A: Flash-loan oracle manipulation: an attacker manipulates a thin AMM that contributes to the Chainlink aggregator (or, more directly, manipulates a less-defended price-feed if the protocol uses one) within one transaction; opens a position at the manipulated price; takes profit. Less directly: a flash-crash market move can liquidate vaults that would have been safe under a delayed price. The OSM (1-hour delay) defends against both. Finding: High severity in a CDP audit.

  4. Q: Explain in 60 seconds why UST’s mechanism failed even though arbitrage “worked correctly”. A: UST → LUNA burn-mint was symmetric and mechanically sound. The failure was that LUNA was the only backing, and LUNA’s market depth was finite. As UST holders fled, arbitrage burned UST and minted LUNA; LUNA supply expanded hyperbolically while LUNA price collapsed. Each marginal $1 of UST burned required mining 1 / P_L LUNA — diverging as P_L → 0. The mechanism’s correctness in form became its destruction in scale. The Anchor 20% yield had masked the instability by manufacturing UST demand; when that subsidy looked at risk, the demand dried up and the spiral ran.

  5. Q: A new “Liquid Restaking Stable” launches: backed 100% by eETH (an ether.fi LRT), with 130% MCR. Top three audit findings? A: (1) Slashing pass-through risk: eETH’s value can be impaired by AVS slashing events not reflected in spot oracles instantly; 130% MCR may be insufficient for the worst-case slashing scenarios. (2) Withdrawal queue mismatch: liquidations need to convert eETH to ETH, which goes through ether.fi’s withdrawal queue (days to weeks under stress); liquidator capital constrained by queue. (3) Restaking dependency graph: backing depends on AVS solvency, validator integrity, and ether.fi governance — a multi-level trust stack. Each level needs explicit trust-assumption statement.

  6. Q: An ERC-4626 wrapper for an off-chain-yield stablecoin computes share price via totalAssets() = asset.balanceOf(address(this)). What’s the bug and how to fix? A: A direct token transfer to the vault inflates totalAssets() without minting shares, donation-attacking the share price. Fix: track totalAssets from internal accounting (storage variable updated on deposit / withdraw / yield accrual), not from token balance. Optionally also use virtual-shares (OZ’s decimalsOffset) to make the initial-deposit attack uneconomical.

  7. Q: You’re auditing a fiat-backed stablecoin. What three off-chain questions go in the trust-assumptions section? A: (1) Where are reserves banked, in what jurisdictions, with what FDIC / regulatory backstop? (2) What is the redemption process — direct on-chain redemption to a verified user, or off-chain wire after KYC? — and what is its uptime guarantee (weekend, holiday, regulatory action)? (3) Who has mint / burn / blacklist authority on-chain, and is the key custody documented (HSM, MPC, multi-signature)?

  8. Q: A CDP protocol’s liquidation function reverts when collateral price drops > 30% in a single block. Why might this exist, and is it good or bad? A: Existence rationale: protect against oracle-manipulation flash liquidations. Trade-off: in a real fast crash (Black Thursday, March 2020), legitimate liquidations are now blocked, leading to growing bad debt. Auditor verdict: the protection should be time-bounded (don’t liquidate for K blocks if drop > 30%, then resume) and combined with an OSM-style smoothed price, not a hard revert. A permanent revert is a likely-High finding.

  9. Q: A stablecoin protocol claims “fully decentralised, no admin keys”. The audit reveals a transferOwnership function on the oracle contract is still callable. Severity? A: Critical. The oracle is the single most leveraged point in a CDP. Centralised ownership of the oracle = trivial protocol drain via setting prices to extreme values then opening or liquidating positions. Severity High-to-Critical depending on how the function is gated and what the timelock is, but a finding that contradicts the marketing claim is always at least Medium severity.

  10. Q: Compare LLAMMA’s “soft liquidation” to Maker’s Clipper Dutch auction. What user-side trade-off do borrowers make under LLAMMA that they don’t under Clipper? A: Under Clipper, a single liquidation event closes the loan with a one-shot collateral discount (~5–13%). The borrower loses a fixed bonus but doesn’t continue to bleed. Under LLAMMA, the position is continuously converted between collateral and stable across bands as the price oscillates; in a sideways-volatile market, the user can experience repeated round-trip losses (“LLAMMA tax”) that can compound. Trade-off: LLAMMA softens the binary-event risk at the cost of continuous-event risk, which is rationally better in trending markets and worse in choppy ones. Audit angle: surface this clearly in the user-facing risk documentation; flag if the protocol claims LLAMMA is “strictly safer” without nuance.


11. Deliverables

  • Lab 1: SimpleCDP stress test run; insolvency threshold identified; results documented.
  • Lab 1 stretch (≥2 of 4 stretch tasks): stability fee, liquidation bonus impact, sporadic liquidators, market-impact reflexive feedback.
  • Lab 2: UST simulator run with default parameters showing death spiral; one parameter sweep showing the threshold at which the spiral averts (or never does).
  • Lab 3: real-protocol risk report (1500–2500 words) with the §5.4 structure.
  • Audit-checklist update: §7’s stablecoin checklist merged into your audit-checklist-master under a “stablecoin / monetary-protocol” section.
  • Notes file: written walkthrough of how you would classify and risk-rank, in 60 seconds, an unfamiliar stablecoin pitched to you over a call.

12. Where this leads

This bonus chapter ends the core stablecoin material in the course. Cross-references that build on it:

  • Tuan-09-Oracle-MEV-Economic-Attack — every stablecoin failure is downstream of an oracle event; this bonus relies on that week’s oracle taxonomy.
  • Tuan-Bonus-Liquid-Staking-Restaking — LSTs and LRTs are increasingly common stablecoin collateral; the slashing-pass-through risks discussed there are inputs to your stablecoin audit.
  • Case-Euler-Finance-2023 — the donation-attack lesson applies to stablecoin protocols with internal share accounting; the same shape appears in CDP “internal balance” primitives.
  • Tuan-15-Audit-Methodology-Tooling (later) — the methodology section will revisit invariant testing with Foundry, Echidna, and Halmos; the stablecoin invariants from §7 of this chapter become input.

The shift in mindset, summarised one more time: stablecoin auditing is the discipline of modeling the contract behind the contract. The Solidity code is necessary but not sufficient. You audit the equations, the economic incentives, the off-chain dependencies, the reflexive feedback loops, the governance latency, the operational runbook — and only then the function bodies. An audit that finds only function-body bugs and misses a PSM concentration finding has failed the user, even if the function bodies are perfect.

The next bonus chapter you should read is Tuan-Bonus-Liquid-Staking-Restaking, which goes deep on the LST/LRT space that increasingly underpins both stablecoin collateral and stablecoin demand (via sUSDe-style synthetic dollars).


Last updated: 2026-05-16 See also: Roadmap · References · MOC-Web3-Security-Mastery · Tuan-08-DeFi-Security-AMM-Lending-Vault · Tuan-Bonus-Liquid-Staking-Restaking