Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Part 2 β€” Module 6: Stablecoins & CDPs

Difficulty: Advanced

Estimated reading time: ~35 minutes | Exercises: ~2-3 hours


πŸ“š Table of Contents

The CDP Model and MakerDAO/Sky Architecture

Liquidations, PSM, and DAI Savings Rate

Build Exercise: Simplified CDP Engine

Stablecoin Landscape and Design Trade-offs


πŸ’‘ The CDP Model and MakerDAO/Sky Architecture

On the surface, a CDP (Collateralized Debt Position) looks like a lending protocol β€” deposit collateral, borrow an asset. But there’s a fundamental difference: in a lending protocol, borrowers withdraw existing tokens from a pool that suppliers deposited. In a CDP system, the borrowed stablecoin is minted into existence when the user opens a position. There are no suppliers. The protocol is the issuer.

This changes everything about the design: there’s no utilization rate (because there’s no supply pool), no supplier interest rate, and the stability of the stablecoin depends entirely on the protocol’s ability to maintain the peg through mechanism design β€” collateral backing, liquidation efficiency, and monetary policy via the stability fee and savings rate.

MakerDAO (now rebranded to Sky Protocol) pioneered CDPs and remains the largest decentralized stablecoin issuer, with over $7.8 billion in DAI + USDS liabilities. Understanding its architecture gives you the template for how on-chain monetary systems work.

πŸ’‘ Concept: How CDPs Work

The core lifecycle:

  1. Open a Vault. User selects a collateral type (called an β€œilk” β€” e.g., ETH-A, WBTC-B, USDC-A) and deposits collateral.
  2. Generate DAI/USDS. User mints stablecoins against the collateral, up to the maximum allowed by the collateral ratio (typically 150%+ for volatile assets). The stablecoins are newly minted β€” they didn’t exist before.
  3. Accrue stability fee. Interest accrues on the minted DAI, paid in DAI. This is the protocol’s revenue.
  4. Repay and close. User returns the minted DAI plus accrued stability fee. The returned DAI is burned (destroyed). User withdraws their collateral.
  5. Liquidation. If collateral value drops below the liquidation ratio, the Vault is liquidated via auction.

The critical insight: DAI’s value comes from the guarantee that every DAI in circulation is backed by more than $1 of collateral, and that the system can liquidate under-collateralized positions to maintain this backing.

πŸ“– MakerDAO Contract Architecture

MakerDAO’s codebase (called β€œdss” β€” Dai Stablecoin System) uses a unique naming convention inherited from formal verification traditions. The core contracts:

Vat β€” The core accounting engine. Stores all Vault state, DAI balances, and collateral balances. Every state-changing operation ultimately modifies the Vat. Think of it as the protocol’s ledger.

Vat stores:
  Ilk (collateral type): Art (total debt), rate (stability fee accumulator), spot (price with safety margin), line (debt ceiling), dust (minimum debt)
  Urn (individual vault): ink (locked collateral), art (normalized debt)
  dai[address]: internal DAI balance
  sin[address]: system debt (bad debt from liquidations)

Key Vat functions:

  • frob(ilk, u, v, w, dink, dart) β€” The fundamental Vault operation. Modifies collateral (dink) and debt (dart) simultaneously. This is how users deposit collateral and generate DAI.
  • grab(ilk, u, v, w, dink, dart) β€” Seize collateral from a Vault (used in liquidation). Transfers collateral to the liquidation module and creates system debt (sin).
  • fold(ilk, u, rate) β€” Update the stability fee accumulator for a collateral type. This is how interest accrues globally.
  • heal(rad) β€” Cancel equal amounts of DAI and sin (system debt). Used after auctions recover DAI.

Normalized debt: The Vat stores art (normalized debt), not actual DAI owed. Actual debt = art Γ— rate. The rate accumulator increases over time based on the stability fee. This is the same index pattern from Module 4 (lending), applied to stability fees instead of borrow rates.

πŸ” Deep Dive: MakerDAO Precision Scales (WAD / RAY / RAD)

MakerDAO uses three fixed-point precision scales throughout its codebase. Understanding these is essential for reading any dss code:

WAD = 10^18  (18 decimals) β€” used for token amounts
RAY = 10^27  (27 decimals) β€” used for rates and ratios
RAD = 10^45  (45 decimals) β€” used for internal DAI accounting (= WAD Γ— RAY)

Why three scales?
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ink (collateral)  = WAD   e.g., 10.5 ETH = 10.5e18     β”‚
β”‚  art (norm. debt)  = WAD   e.g., 5000 units = 5000e18   β”‚
β”‚  rate (fee accum.) = RAY   e.g., 1.05 = 1.05e27         β”‚
β”‚  spot (price/LR)   = RAY   e.g., $1333 = 1333e27        β”‚
β”‚                                                          β”‚
β”‚  Actual debt = art Γ— rate = WAD Γ— RAY = RAD (10^45)      β”‚
β”‚  Vault check = ink Γ— spot  vs  art Γ— rate                β”‚
β”‚                WAD Γ— RAY      WAD Γ— RAY                  β”‚
β”‚                = RAD           = RAD        ← same scale! β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why RAY for rates? 18 decimals isn’t enough precision for per-second compounding. A 5% annual stability fee is ~1.0000000015 per second β€” you need 27 decimals to represent that accurately.

Why RAD? When you multiply a WAD amount by a RAY rate, you get a 45-decimal number. Rather than truncating, the Vat keeps the full precision for internal accounting. External DAI (the ERC-20) uses WAD.

πŸ” Deep Dive: Vault Safety Check β€” Step by Step

Let’s trace a real example to understand how the Vat checks if a vault is safe:

Scenario: User deposits 10 ETH, mints 15,000 DAI
  ETH price:     $2,000
  Liquidation ratio: 150% (so LR = 1.5)

Step 1: Compute spot (price with safety margin baked in)
  spot = oracle_price / liquidation_ratio
  spot = $2,000 / 1.5 = $1,333.33
  In RAY: 1333.33e27

Step 2: Store vault state
  ink = 10e18  (10 ETH in WAD)
  art = 15000e18  (15,000 normalized debt in WAD)
  rate = 1.0e27  (fresh vault, no fees yet β€” 1.0 in RAY)

Step 3: Safety check β€” is ink Γ— spot β‰₯ art Γ— rate?
  Left side:  10e18 Γ— 1333.33e27 = 13,333.3e45 (RAD)
  Right side: 15000e18 Γ— 1.0e27  = 15,000e45  (RAD)
  13,333 < 15,000 β†’ ❌ UNSAFE! Vault would be rejected.

  The user can only mint up to:
  max_art = ink Γ— spot / rate = 10 Γ— 1333.33 / 1.0 = 13,333 DAI

Step 4: After 1 year with 5% stability fee
  rate increases: 1.0e27 β†’ 1.05e27
  Actual debt: art Γ— rate = 15000 Γ— 1.05 = 15,750 DAI
  (User now owes 750 DAI more in stability fees)

  Safety check with same ink and spot:
  ink Γ— spot = 13,333 (unchanged)
  art Γ— rate = 15000 Γ— 1.05 = 15,750
  Even more unsafe β†’ liquidation trigger!

πŸ”— Connection: This is the same index-based accounting from Module 4 (Aave’s liquidity index, Compound’s borrow index). The pattern: store a normalized amount, multiply by a growing rate to get the actual amount. No per-user updates needed β€” only the global rate changes.

Spot β€” The Oracle Security Module (OSM) interface. Computes the collateral price with the safety margin (liquidation ratio) baked in: spot = oracle_price / liquidation_ratio. The Vat uses this directly: a Vault is safe if ink Γ— spot β‰₯ art Γ— rate.

Jug β€” The stability fee module. Calls Vat.fold() to update the rate accumulator for each collateral type. The stability fee (an annual percentage) is converted to a per-second rate and compounds continuously.

Dai β€” The ERC-20 token contract for external DAI. Internal DAI in the Vat (dai[]) is not the same as the ERC-20 token. The DaiJoin adapter converts between them.

Join adapters β€” Bridge between external ERC-20 tokens and the Vat’s internal accounting:

  • GemJoin β€” Locks collateral ERC-20 tokens and credits internal gem balance in the Vat
  • DaiJoin β€” Converts internal dai balance to/from the external DAI ERC-20 token

CDP Manager β€” A convenience layer that lets a single address own multiple Vaults via proxy contracts (UrnHandlers). Without it, one address can only have one Urn per Ilk.

πŸŽ“ Intermediate Example: Simplified Vault Accounting

Before diving into the full Vat flow (which uses terse formal-verification naming), let’s see the same logic with readable names:

// The core CDP check, readable version:
function isSafe(address user) public view returns (bool) {
    uint256 collateralValue = collateralAmount[user] * oraclePrice / PRECISION;
    uint256 debtValue = normalizedDebt[user] * stabilityFeeRate / PRECISION;
    uint256 minCollateral = debtValue * liquidationRatio / PRECISION;
    return collateralValue >= minCollateral;
}

// The Vat does exactly this, but in one line:
//   ink Γ— spot β‰₯ art Γ— rate
// where spot = oraclePrice / liquidationRatio (safety margin baked in)

This is the same check. The Vat just pre-computes spot = price / LR so the safety check is a single comparison. Once you see this, the Vat code becomes readable.

The Full Flow: Opening a Vault

  1. User calls GemJoin.join() β€” transfers ETH (via WETH) to GemJoin, credits internal gem balance in Vat
  2. User calls CdpManager.frob() (or Vat.frob() directly) β€” locks gem as ink (collateral) and generates art (normalized debt)
  3. Vat verifies: ink Γ— spot β‰₯ art Γ— rate (Vault is safe) and total debt ≀ debt ceiling
  4. Vat credits dai to the user’s internal balance
  5. User calls DaiJoin.exit() β€” converts internal dai to external DAI ERC-20 tokens

πŸ’» Quick Try:

On a mainnet fork, read MakerDAO state directly:

IVat vat = IVat(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B);
(uint256 Art, uint256 rate, uint256 spot, uint256 line, uint256 dust) = vat.ilks("ETH-A");
// Art = total normalized debt for ETH-A vaults
// rate = stability fee accumulator (starts at 1.0 RAY, grows over time)
// spot = ETH price / liquidation ratio (in RAY)
// Actual total debt = Art * rate (in RAD)

Observe that rate is > 1.0e27 β€” that difference from 1.0 represents all accumulated stability fees since ETH-A was created. Every vault’s actual debt is art Γ— rate.

πŸ“– Read: Vat.sol

Source: dss/src/vat.sol (github.com/sky-ecosystem/dss)

This is one of the most important contracts in DeFi. Focus on:

  • The frob() function β€” understand each check and state modification
  • How spot encodes the liquidation ratio into the price
  • The authorization system (wards and can mappings)
  • The cage() function for Emergency Shutdown

The naming convention is terse (derived from formal specification): ilk = collateral type, urn = vault, ink = collateral amount, art = normalized debt, gem = unlocked collateral, dai = stablecoin balance, sin = system debt, tab = total debt for auction.

πŸ“– How to Study the MakerDAO/dss Codebase

The dss codebase is one of DeFi’s most important β€” and one of the hardest to read due to its terse naming. Here’s how to approach it:

  1. Build a glossary first β€” Before reading any code, memorize the core terms: ilk (collateral type), urn (vault), ink (locked collateral), art (normalized debt), gem (free collateral), dai (internal stablecoin), sin (bad debt), rad/ray/wad (precision scales: 45/27/18 decimals). Write these on a card and keep it visible while reading.

  2. Read frob() line by line β€” This single function IS the CDP system. It modifies collateral (dink) and debt (dart) simultaneously. Trace each require statement: what’s it checking? Map them to: vault safety check (ink Γ— spot β‰₯ art Γ— rate), debt ceiling check (Art Γ— rate ≀ line), dust check, and authorization. Understanding frob() means understanding the entire protocol.

  3. Trace the authorization system β€” wards mapping controls admin access. can mapping controls who can modify whose vaults. The wish() function checks both. This is unusual compared to OpenZeppelin’s AccessControl β€” understand how hope() and nope() grant/revoke per-user permissions.

  4. Read the Join adapters β€” GemJoin.join() and DaiJoin.exit() are the bridges between external ERC-20 tokens and the Vat’s internal accounting. These are short (~30 lines each) and clarify how the internal gem and dai balances relate to actual token balances.

  5. Study grab() and heal() β€” grab() is the forced version of frob() used during liquidation β€” it seizes collateral and creates sin (system debt). heal() cancels equal amounts of dai and sin. Together, they form the liquidation and recovery cycle: grab creates bad debt, auctions recover DAI, heal cancels the bad debt.

Don’t get stuck on: The formal verification annotations in comments. The dss codebase was designed for formal verification (which is why the naming is so terse β€” it maps to mathematical specifications). You can ignore the verification proofs and focus on the logic.

🎯 Build Exercise: CDP Model and MakerDAO

Workspace: workspace/src/part2/module6/ β€” starter files: SimpleVat.sol, SimpleJug.sol, tests: SimpleVat.t.sol, SimpleJug.t.sol | Shared: VatMath.sol, SimpleStablecoin.sol, SimpleGemJoin.sol, SimpleDaiJoin.sol

Exercise 1: On a mainnet fork, trace a complete Vault lifecycle:

  • Join WETH as collateral via GemJoin
  • Open a Vault via CdpManager, lock collateral, generate DAI via frob
  • Read the Vault state from the Vat (ink, art)
  • Compute actual debt: art Γ— rate (fetch rate from Vat.ilks(ilk))
  • Exit DAI via DaiJoin
  • Verify you hold the expected DAI ERC-20 balance

Exercise 2: Read the Jug contract. Calculate the per-second rate for a 5% annual stability fee. Call Jug.drip() on a mainnet fork and verify the rate accumulator updates correctly. Compute how much more DAI a Vault owes after 1 year of accrued fees.

πŸ” Deep Dive: Stability Fee Per-Second Rate

A 5% annual fee needs to be converted to a per-second compound rate:

Annual rate: 1.05 (5%)
Seconds per year: 365.25 Γ— 24 Γ— 60 Γ— 60 = 31,557,600

Per-second rate = 1.05 ^ (1 / 31,557,600)
               β‰ˆ 1.000000001547125957...

In RAY (27 decimals): 1000000001547125957000000000

Verification: 1.000000001547125957 ^ 31,557,600 β‰ˆ 1.05 βœ“

This is stored in Jug as the `duty` parameter per ilk.
Each time drip() is called:
  rate_new = rate_old Γ— (per_second_rate ^ seconds_elapsed)

πŸ”— Connection: This is the same continuous compounding from Module 4 β€” Aave and Compound use the same per-second rate accumulator for borrow interest. The math is identical; only the context differs (stability fee vs borrow rate).

πŸ” Deep Dive: rpow() β€” Exponentiation by Squaring

The Jug needs to compute per_second_rate ^ seconds_elapsed. With seconds_elapsed potentially being millions (weeks between drip() calls), you can’t loop. MakerDAO uses exponentiation by squaring β€” an O(log n) algorithm:

Goal: compute base^n (in RAY precision)

Standard approach: base Γ— base Γ— base Γ— ... (n multiplications) β†’ O(n) β€” too expensive

Exponentiation by squaring: O(log n) multiplications
  Key insight: x^10 = x^8 Γ— x^2  (use binary representation of exponent)

Example: 1.000000001547^(604800)  [1 week in seconds]

  604800 in binary = 10010011101010000000

  Step through each bit (right to left):
  bit 0 (0): skip                    base = baseΒ²
  bit 1 (0): skip                    base = baseΒ²
  ...
  For each '1' bit: result *= current base
  For each bit: base = base Γ— base (square)

  Total: ~20 multiplications instead of 604,800
// Simplified rpow (MakerDAO's actual implementation in jug.sol):
function rpow(uint256 x, uint256 n, uint256 base) internal pure returns (uint256 z) {
    assembly {
        z := base                          // result = 1.0 (in RAY)
        for {} n {} {
            if mod(n, 2) {                 // if lowest bit is 1
                z := div(mul(z, x), base)  // result *= x (RAY multiplication)
            }
            x := div(mul(x, x), base)      // x = xΒ² (square the base)
            n := div(n, 2)                  // shift exponent right
        }
    }
}
// Jug.drip() calls: rpow(duty, elapsed_seconds, RAY)
// where duty = per-second rate (e.g., 1.000000001547... in RAY)

Why assembly? Two reasons: (1) overflow checks β€” the intermediate mul(z, x) can overflow uint256, and the assembly version handles this via checked division, (2) gas efficiency β€” this is called frequently and the savings matter.

Where you’ll see this: Every protocol that compounds per-second rates uses this pattern or a variation. Aave’s MathUtils.calculateCompoundedInterest() uses a 3-term Taylor approximation instead (see Module 4) β€” faster but less precise for large exponents.


πŸ“‹ Summary: CDP Model and MakerDAO

βœ“ Covered:

  • CDP model: mint stablecoins against collateral (not lending from a pool)
  • MakerDAO architecture: Vat (accounting), Jug (fees), Join adapters, CDP Manager
  • Precision scales: WAD (18), RAY (27), RAD (45) and why each exists
  • Vault safety check: ink Γ— spot β‰₯ art Γ— rate with step-by-step example
  • Normalized debt and rate accumulator pattern (same as Module 4 lending indexes)
  • Code reading strategy for the terse dss codebase

Next: Liquidation 2.0 (Dutch auctions), PSM for peg stability, and DSR/SSR for DAI demand


πŸ’‘ Liquidations, PSM, and DAI Savings Rate

πŸ’» Quick Try:

Before studying the liquidation architecture, read live auction state on a mainnet fork:

IDog dog = IDog(0x135954d155898D42C90D2a57824C690e0c7BEf1B);
IClipper clipper = IClipper(0xc67963a226eddd77B91aD8c421630A1b0AdFF270); // ETH-A Clipper

// Check if there are any active auctions
uint256 count = clipper.count();
emit log_named_uint("Active ETH-A auctions", count);

// Read the circuit breaker state
(uint256 Art,,, uint256 line,) = IVat(0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B).ilks("ETH-A");
(, uint256 chop, uint256 hole, uint256 dirt) = dog.ilks("ETH-A");
emit log_named_uint("Liquidation penalty (chop, RAY)", chop); // e.g., 1.13e27 = 13% penalty
emit log_named_uint("Per-ilk auction cap (hole, RAD)", hole);
emit log_named_uint("DAI currently in auctions (dirt, RAD)", dirt);

Even if count is 0 (no active auctions), you’ll see the circuit breaker parameters β€” hole caps how much DAI can be raised simultaneously, preventing the cascade that caused Black Thursday.

πŸ’‘ Concept: Liquidation 2.0: Dutch Auctions

MakerDAO’s original liquidation system (Liquidation 1.2) used English auctions β€” participants bid DAI in increasing amounts, with capital locked for the duration. This was slow and capital-inefficient, and it catastrophically failed on β€œBlack Thursday” (March 12, 2020) when network congestion prevented liquidation bots from bidding, allowing attackers to win auctions for $0 and causing $8.3 million in bad debt.

πŸ” Deep Dive: Black Thursday Timeline (March 12, 2020)

Timeline (UTC):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 06:00  ETH at ~$193. Markets stable. MakerDAO holds $700M+ in vaults. β”‚
β”‚                                                                         β”‚
β”‚ 08:00  COVID panic sell-off begins. ETH starts sliding.                β”‚
β”‚                                                                         β”‚
β”‚ 10:00  ETH drops below $170. First liquidations trigger.               β”‚
β”‚        ⚠ Ethereum gas prices spike 10-20x (>200 gwei)                 β”‚
β”‚                                                                         β”‚
β”‚ 12:00  ETH hits $130. Massive cascade of vault liquidations.           β”‚
β”‚        ⚠ Network congestion: liquidation bots can't get txs mined     β”‚
β”‚        ⚠ Keeper bids fail with "out of gas" or stuck in mempool       β”‚
β”‚                                                                         β”‚
β”‚ 13:00  KEY MOMENT: Auctions complete with ZERO bids.                   β”‚
β”‚        β†’ Attackers win collateral for 0 DAI                            β”‚
β”‚        β†’ Protocol takes 100% loss on those vaults                      β”‚
β”‚        β†’ English auction requires active bidders β€” none could bid      β”‚
β”‚                                                                         β”‚
β”‚ 14:00  ETH bottoms near $88. Total of 1,200+ vaults liquidated.       β”‚
β”‚        $8.3 million in DAI left unbacked (bad debt).                   β”‚
β”‚                                                                         β”‚
β”‚ Post-  MakerDAO auctions MKR tokens to cover the deficit.             β”‚
β”‚ crisis Governance votes for Liquidation 2.0 redesign.                  β”‚
β”‚        English auctions β†’ Dutch auctions (no bidders needed)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Root causes:
  1. English auction REQUIRES competing bidders β€” no bidders = $0 wins
  2. Network congestion prevented keeper bots from submitting bids
  3. Capital lockup: bids locked DAI for auction duration β†’ less liquidity
  4. No circuit breakers: all liquidations fired simultaneously

Why this matters for protocol designers: Any mechanism that relies on external participants acting in real-time (bidding, liquidating, updating) can fail when the network is congested β€” which is exactly when these mechanisms are needed most. This is the β€œcoincidence of needs” problem in DeFi crisis design.

Liquidation 2.0 replaced English auctions with Dutch auctions:

Dog β€” The liquidation trigger contract (replaces the old β€œCat”). When a Vault is unsafe:

  1. Keeper calls Dog.bark(ilk, urn, kpr)
  2. Dog calls Vat.grab() to seize the Vault’s collateral and debt
  3. Dog calls Clipper.kick() to start a Dutch auction
  4. Keeper receives a small incentive (tip + chip percentage of the tab)

Clipper β€” The Dutch auction contract (one per collateral type). Each auction:

  1. Starts at a high price (oracle price Γ— buf multiplier, e.g., 120% of oracle price)
  2. Price decreases over time according to a price function (Abacus)
  3. Any participant can call Clipper.take() at any time to buy collateral at the current price
  4. Instant settlement β€” no capital lockup, no bidding rounds

Abacus β€” Price decrease functions. Two main types:

  • LinearDecrease β€” price drops linearly over time
  • StairstepExponentialDecrease β€” price drops in discrete steps (e.g., 1% every 90 seconds)

πŸ” Deep Dive: Dutch Auction Price Decrease

Price
  β”‚
  β”‚ ●  Starting price = oracle Γ— buf (e.g., 120% of oracle)
  β”‚  \
  β”‚   \  LinearDecrease: straight line to zero
  β”‚    \
  β”‚     \
  β”‚      \        StairstepExponentialDecrease:
  β”‚  ─────●       drops 1% every 90 seconds
  β”‚       │───●
  β”‚            │───●
  β”‚                 │───●
  β”‚                      │───●  ← "cusp" floor (e.g., 40%)
  β”‚ Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· Β· ← tail (max duration)
  └───────────────────────────────────────── Time
  0     5min    10min    15min    20min

Liquidator perspective:
  - At t=0: price is ABOVE market β†’ unprofitable, nobody buys
  - Price falls... falls... falls...
  - At some point: price = market price β†’ breakeven
  - Price keeps falling β†’ increasingly profitable
  - Rational liquidator buys when: auction_price Γ— (1 - gas%) > market_price
  - First buyer wins β†’ no gas wars like in English auctions

Why Dutch auctions fix Black Thursday:

  • No capital lockup β€” buy instantly, no bidding rounds
  • Flash loan compatible β€” borrow DAI β†’ buy collateral β†’ sell collateral β†’ repay
  • Natural price discovery β€” the falling price finds the market clearing level
  • MEV-compatible β€” composable with other DeFi operations (β†’ Module 5 flash loans)

Circuit breakers:

  • tail β€” maximum auction duration before reset required
  • cusp β€” minimum price (% of starting price) before reset required
  • hole / Hole β€” maximum total DAI being raised in auctions (per-ilk and global). Prevents runaway liquidation cascades.

The Dutch auction design fixes Black Thursday’s problems: no capital lockup means participants can use flash loans, settlement is instant (composable with other DeFi operations), and the decreasing price naturally finds the market clearing level.

πŸ” Deep Dive: Dutch Auction Liquidation β€” Numeric Walkthrough

Setup:
  Vault: 10 ETH collateral, 15,000 DAI debt (normalized art = 15,000, rate = 1.0)
  ETH price drops from $2,000 β†’ $1,800
  Liquidation ratio: 150% β†’ spot = $1,800 / 1.5 = $1,200 (RAY)
  Safety check: ink Γ— spot = 10 Γ— $1,200 = $12,000 < art Γ— rate = $15,000 β†’ UNSAFE

Step 1: Keeper calls Dog.bark(ETH-A, vault_owner, keeper_address)
  β†’ Vat.grab() seizes collateral: 10 ETH moved to Clipper, 15,000 DAI of sin created
  β†’ tab (total to recover) = art Γ— rate Γ— chop = 15,000 Γ— 1.0 Γ— 1.13 = 16,950 DAI
    (chop = 1.13 RAY = 13% liquidation penalty)
  β†’ Keeper receives: tip (flat, e.g., 300 DAI) + chip (% of tab, e.g., 0.1% = 16.95 DAI)

Step 2: Clipper.kick() starts Dutch auction
  β†’ Starting price (top) = oracle_price Γ— buf = $1,800 Γ— 1.20 = $2,160 per ETH
    (buf = 1.20 = start 20% above oracle to ensure initial price is above market)
  β†’ lot = 10 ETH (collateral for sale)
  β†’ tab = 16,950 DAI (amount to recover)

Step 3: Price decreases over time (StairstepExponentialDecrease)
  β†’ t=0:    price = $2,160  (above market β€” nobody buys)
  β†’ t=90s:  price = $2,138  (1% drop per step)
  β†’ t=180s: price = $2,117
  β†’ t=270s: price = $2,096
  β†’ ...
  β†’ t=900s: price = $1,953  (still above market $1,800)
  β†’ t=1800s: price = $1,767  (now BELOW market β€” profitable!)
    ($2,160 Γ— 0.99^20 = $2,160 Γ— 0.8179 = $1,767)

Step 4: Liquidator calls Clipper.take() at t=1800s (price = $1,767)
  β†’ Liquidator offers: 16,950 DAI (the full tab)
  β†’ Collateral received: 16,950 / $1,767 = 9.59 ETH
  β†’ Remaining: 10 - 9.59 = 0.41 ETH returned to vault owner

  Liquidator P&L (with flash loan from Balancer):
    Received: 9.59 ETH
    Sell on DEX at $1,800: 9.59 Γ— $1,800 = $17,262
    After 0.3% swap fee: $17,262 Γ— 0.997 = $17,210
    Repay flash loan: 16,950 DAI
    Profit: $17,210 - $16,950 = $260
    Gas: ~$10-30
    Net: ~$230-250

  Vault owner outcome:
    Lost: 9.59 ETH ($17,262 at market price)
    Recovered: 0.41 ETH ($738)
    Penalty paid: 9.59 Γ— $1,800 - 15,000 = $2,262 (~15.1% effective penalty)
    β†’ The 13% chop + buying below market price = higher effective cost

  Protocol outcome:
    Recovered: 16,950 DAI (covers 15,000 debt + 1,950 penalty)
    The 1,950 DAI penalty goes to Vow (protocol surplus buffer)
    sin (bad debt) cleared via Vat.heal()

Key insight: The liquidator doesn’t need to wait for the absolute best price β€” they just need auction_price < market_price - swap_fees - gas. The competition between liquidators (and MEV searchers) pushes the buy time earlier, reducing the vault owner’s penalty. More competition = better outcomes for everyone except the liquidator margins.

πŸ’‘ Concept: Peg Stability Module (PSM)

The PSM allows 1:1 swaps between DAI and approved stablecoins (primarily USDC) with a small fee (typically 0%). It serves as the primary peg maintenance mechanism:

  • If DAI > $1: Users swap USDC β†’ DAI at 1:1, increasing DAI supply, pushing price down
  • If DAI < $1: Users swap DAI β†’ USDC at 1:1, decreasing DAI supply, pushing price up

The PSM is controversial because it makes DAI heavily dependent on USDC (a centralized stablecoin). At various points, over 50% of DAI’s backing has been USDC through the PSM. This tension β€” decentralization vs peg stability β€” is one of the fundamental challenges in stablecoin design.

Contract architecture: The PSM is essentially a special Vault type that accepts USDC (or other stablecoins) as collateral at a 100% collateral ratio and auto-generates DAI. The tin (fee in) and tout (fee out) parameters control the swap fees in each direction.

πŸ’‘ Concept: Dai Savings Rate (DSR)

The DSR lets DAI holders earn interest by locking DAI in the Pot contract. The interest comes from stability fees paid by Vault owners β€” it’s a mechanism to increase DAI demand (and thus support the peg) by making holding DAI attractive.

Pot contract: Users call Pot.join() to lock DAI and Pot.exit() to withdraw. Accumulated interest is tracked via a rate accumulator (same pattern as stability fees). The DSR is set by governance as a monetary policy tool.

Sky Savings Rate (SSR): The Sky rebrand introduced a parallel savings rate for USDS using an ERC-4626 vault (sUSDS). This is significant because ERC-4626 is the standard vault interface β€” meaning sUSDS is natively composable with any protocol that supports ERC-4626.

πŸ’‘ Concept: The Sky Rebrand: What Changed

In September 2024, MakerDAO rebranded to Sky Protocol. Key changes:

  • DAI β†’ USDS (1:1 convertible, both remain active)
  • MKR β†’ SKY (1:24,000 conversion ratio)
  • SubDAOs β†’ β€œStars” (Spark Protocol is the first Star β€” a lending protocol built on top of Sky)
  • USDS adds a freeze function for compliance purposes (controversial in the community)
  • SSR uses ERC-4626 standard

The underlying protocol mechanics (Vat, Dog, Clipper, etc.) remain the same. For this module, we’ll use the original MakerDAO naming since that’s what the codebase uses.

πŸ“– Read: Dog.sol and Clipper.sol

Source: dss/src/dog.sol and dss/src/clip.sol

In Dog.bark(), trace:

  • How the Vault is validated as unsafe
  • The grab call that seizes collateral
  • How the tab (debt + liquidation penalty) is calculated
  • The circuit breaker checks (Hole/hole, Dirt/dirt)

In Clipper.kick(), trace:

  • How the starting price is set (oracle price Γ— buf)
  • The auction state struct
  • How take() works: price calculation via Abacus, partial fills, refunds

πŸ“– How to Study MakerDAO Liquidation 2.0

  1. Start with Dog.bark() β€” This is the entry point. Trace: how does it verify the vault is unsafe? (Calls Vat.urns() and Vat.ilks(), checks ink Γ— spot < art Γ— rate.) How does it call Vat.grab() to seize collateral? How does it compute the tab (total debt including penalty)?

  2. Read Clipper.kick() β€” After bark() seizes collateral, kick() starts the auction. Focus on: how top (starting price) is computed as oracle_price Γ— buf, how the auction struct stores the state, and how the keeper incentive (tip + chip) is calculated and paid.

  3. Understand the Abacus price functions β€” Read LinearDecrease first (simpler: price drops linearly over time). Then read StairstepExponentialDecrease (price drops in discrete steps). The key question: given a tab (debt to cover) and the current auction price, how much collateral does the take() caller receive?

  4. Trace a complete take() call β€” This is where collateral is actually sold. Follow: price lookup via Abacus β†’ compute collateral amount for the DAI offered β†’ handle partial fills (buyer wants less than the full lot) β†’ refund excess collateral to the vault owner β†’ cancel sin via Vat.heal().

  5. Study the circuit breakers β€” tail (max auction duration), cusp (min price before reset), Hole/hole (max simultaneous DAI in auctions). These exist because of Black Thursday β€” without caps, a cascade of liquidations can overwhelm the system.

Don’t get stuck on: The redo() function initially β€” it’s for restarting stale auctions. Understand bark() β†’ kick() β†’ take() first, then come back to redo() and the edge cases.

🎯 Build Exercise: Liquidations and PSM

Workspace: workspace/src/part2/module6/exercise3-simple-dog/ β€” starter file: SimpleDog.sol, tests: SimpleDog.t.sol | Also: SimplePSM.sol, tests: SimplePSM.t.sol

Exercise 1: On a mainnet fork, simulate a liquidation:

  • Open a Vault with ETH collateral near the liquidation ratio
  • Mock the oracle to drop the price below the liquidation threshold
  • Call Dog.bark() to start the auction
  • Call Clipper.take() to buy the collateral at the current auction price
  • Verify: correct amount of DAI paid, correct collateral received, debt cleared

Exercise 2: Read the PSM contract. Execute a USDC β†’ DAI swap through the PSM on a mainnet fork. Verify the 1:1 conversion and fee application.

πŸ’Ό Job Market Context

What DeFi teams expect you to know:

  1. β€œWhy did MakerDAO switch from English to Dutch auctions?”

    • Good answer: English auctions were slow and required capital lockup
    • Great answer: Black Thursday proved English auctions fail under network congestion β€” bots couldn’t bid, allowing $0 wins and $8.3M bad debt. Dutch auctions fix this: instant settlement, flash-loan compatible, no bidding rounds. The falling price naturally finds the market clearing level.
  2. β€œWhat’s the trade-off of the PSM?”

    • Good answer: It stabilizes the peg but makes DAI dependent on USDC
    • Great answer: The PSM creates a hard peg floor/ceiling but at the cost of centralization β€” at peak, >50% of DAI was backed by USDC through the PSM. This is the stablecoin trilemma in action: DAI chose stability over decentralization. The Sky rebrand introduced USDS with a freeze function, pushing further toward the centralized end.

Interview Red Flags:

  • 🚩 Not knowing how CDPs differ from lending (minting vs redistributing)
  • 🚩 Confusing normalized debt (art) with actual debt (art Γ— rate)
  • 🚩 Thinking liquidation auctions are the same as in traditional finance

Pro tip: If you can trace a frob() call through the Vat’s safety checks and explain each parameter, you demonstrate deeper protocol knowledge than 95% of DeFi developers. MakerDAO’s architecture shows up directly in interview questions at top DeFi teams.


πŸ“‹ Summary: Liquidations, PSM, and DSR

βœ“ Covered:

  • Liquidation 2.0: Dutch auctions (Dog + Clipper), why they replaced English auctions
  • Auction price functions: LinearDecrease vs StairstepExponentialDecrease
  • Circuit breakers: tail, cusp, Hole/hole β€” preventing liquidation cascades
  • PSM: 1:1 peg stability, centralization trade-off, tin/tout fees
  • DSR/SSR: demand-side lever for peg maintenance
  • Sky rebrand: DAIβ†’USDS, MKRβ†’SKY, sUSDS as ERC-4626

Next: Building a simplified CDP engine from scratch


🎯 Build Exercise: Simplified CDP Engine

SimpleCDP.sol

The exercises across this module build a minimal CDP system that captures the essential mechanisms: SimpleVat (accounting engine with frob/fold/grab), SimpleJug (stability fee compounding via rpow and drip), SimpleDog (liquidation trigger + Dutch auction), and SimplePSM (peg stability swaps with fee). Shared contracts (join adapters, stablecoin ERC-20, math library) are pre-built.

Workspace: All exercise scaffolds and tests are in the exercise folders linked from the Day 1, Day 2, and Day 4 exercise sections above. Each exercise has a scaffold with TODOs and a complete test suite.


πŸ“‹ Summary: SimpleCDP

βœ“ Covered:

  • Building the core CDP contracts: SimpleVat, SimpleJug, SimpleDog, SimplePSM
  • Implementing the vault safety check, stability fee accumulator, and liquidation trigger
  • Testing: full lifecycle, fee accrual, liquidation, debt ceiling, dust check, multi-collateral

Next: Comparing stablecoin designs across the landscape β€” overcollateralized, algorithmic, delta-neutral


πŸ’‘ Stablecoin Landscape and Design Trade-offs

πŸ’‘ Concept: Taxonomy of Stablecoins

1. Fiat-backed (USDC, USDT) β€” Centralized issuer holds bank deposits or T-bills equal to the stablecoin supply. Simple, stable, but requires trust in the issuer and is subject to censorship (addresses can be blacklisted).

2. Overcollateralized crypto-backed (DAI/USDS, LUSD) β€” Protocol holds >100% crypto collateral. Decentralized and censorship-resistant (depending on collateral composition), but capital-inefficient (you need $150+ of ETH to mint $100 of stablecoins).

3. Algorithmic (historical: UST/LUNA, FRAX, ESD, BAC) β€” Attempt to maintain peg through algorithmic supply adjustment without full collateral backing. Most have failed catastrophically.

4. Delta-neutral / yield-bearing (USDe by Ethena) β€” Holds crypto collateral and hedges price exposure using perpetual futures short positions. The yield comes from positive funding rates. Novel design but carries exchange counterparty risk and funding rate reversal risk.

πŸ’‘ Concept: Liquity: A Different CDP Design

Liquity (LUSD) takes a minimalist approach compared to MakerDAO:

Key differences from MakerDAO:

  • No governance. Parameters are immutable once deployed. No governance token, no parameter changes.
  • One-time fee instead of ongoing stability fee. Users pay a fee at borrowing time (0.5%–5%, adjusted algorithmically based on redemption activity). No interest accrues.
  • 110% minimum collateral ratio. Much more capital-efficient than MakerDAO’s typical 150%+.
  • ETH-only collateral. No multi-collateral complexity.
  • Redemption mechanism: Any LUSD holder can redeem LUSD for $1 worth of ETH from the riskiest Vault (lowest collateral ratio). This creates a hard price floor.
  • Stability Pool: LUSD holders can deposit into the Stability Pool, which automatically absorbs liquidated collateral at a discount. No auction needed β€” liquidation is instant.

Liquity V2 (2024-25): Introduces user-set interest rates (borrowers bid their own rate), multi-collateral support (LSTs like wstETH, rETH), and a modified redemption mechanism.

πŸ” Deep Dive: Liquity Redemption β€” Numeric Walkthrough

Scenario: LUSD trades at $0.97 on DEXes. An arbitrageur spots the opportunity.

System state β€” all active Troves, sorted by collateral ratio (ascending):
  Trove A: 2 ETH collateral, 2,800 LUSD debt β†’ CR = (2 Γ— $2,000) / 2,800 = 142.8%
  Trove B: 3 ETH collateral, 3,500 LUSD debt β†’ CR = (3 Γ— $2,000) / 3,500 = 171.4%
  Trove C: 5 ETH collateral, 4,000 LUSD debt β†’ CR = (5 Γ— $2,000) / 4,000 = 250.0%

Step 1: Arbitrageur buys 3,000 LUSD on DEX at $0.97
  Cost: 3,000 Γ— $0.97 = $2,910

Step 2: Call redeemCollateral(3,000 LUSD)
  System starts with the RISKIEST Trove (lowest CR = Trove A)

  β†’ Trove A: has 2,800 LUSD debt
    Redeem 2,800 LUSD β†’ receive $2,800 worth of ETH = 2,800 / $2,000 = 1.4 ETH
    Trove A: CLOSED (0 debt, 0.6 ETH returned to Trove A owner)
    Remaining to redeem: 3,000 - 2,800 = 200 LUSD

  β†’ Trove B: has 3,500 LUSD debt (next riskiest)
    Redeem 200 LUSD β†’ receive $200 worth of ETH = 200 / $2,000 = 0.1 ETH
    Trove B: debt reduced to 3,300, collateral reduced to 2.9 ETH
    CR now = (2.9 Γ— $2,000) / 3,300 = 175.8% (improved!)
    Remaining: 0 LUSD βœ“

Step 3: Redemption fee (0.5% base, increases with volume)
  Fee = 0.5% Γ— 3,000 = 15 LUSD (deducted in ETH: 0.0075 ETH)
  Total ETH received: 1.4 + 0.1 - 0.0075 = 1.4925 ETH

Step 4: Arbitrageur P&L
  Received: 1.4925 ETH Γ— $2,000 = $2,985
  Cost: $2,910 (buying LUSD on DEX)
  Profit: $75 (2.58% return)

  This profit closes the peg gap:
  - 3,000 LUSD bought on DEX β†’ buying pressure β†’ LUSD price rises toward $1
  - 3,000 LUSD burned via redemption β†’ supply decreases β†’ further price support

Why riskiest-first? It improves system health β€” the lowest-CR troves pose the most risk. Redemption either closes them or pushes their CR higher. The base fee (0.5%, rising with redemption volume) prevents excessive redemptions that would disrupt healthy vaults.

The peg guarantee: Redemptions create a hard floor at ~$1.00 (minus the fee). If LUSD trades at $0.97, anyone can profit by redeeming. This arbitrage force pushes the price back up. The ceiling is softer β€” at ~$1.10 (the minimum CR), it becomes attractive to open new Troves and sell LUSD.

⚠️ The Algorithmic Stablecoin Failure Pattern

UST/LUNA (Terra, May 2022) is the canonical example. The mechanism:

  • UST was pegged to $1 via an arbitrage loop with LUNA
  • When UST > $1: burn $1 of LUNA to mint 1 UST (increase supply, push price down)
  • When UST < $1: burn 1 UST to mint $1 of LUNA (decrease supply, push price up)

The death spiral: when confidence in UST dropped, holders rushed to redeem UST for LUNA. Massive LUNA minting cratered LUNA’s price, which reduced the backing for UST, causing more redemptions, more LUNA minting, more price collapse. $40+ billion in value was destroyed in days.

The lesson: Without external collateral backing, algorithmic stablecoins rely on reflexive confidence. When confidence breaks, there’s nothing to stop the spiral. Every algorithmic stablecoin that relies purely on its own governance/seigniorage token for backing has either failed or abandoned that model.

πŸ’‘ Concept: Ethena (USDe): The Delta-Neutral Model

Ethena mints USDe against crypto collateral (primarily staked ETH) and simultaneously opens a short perpetual futures position of equal size. The net exposure is zero (delta-neutral), meaning the collateral value doesn’t change with ETH price movements.

How it works:

  1. User deposits stETH (or ETH, which gets staked)
  2. Ethena opens an equal-sized short perpetual position on centralized exchanges
  3. ETH price goes up β†’ collateral gains, short loses β†’ net zero
  4. ETH price goes down β†’ collateral loses, short gains β†’ net zero
  5. Revenue: staking yield (~3-4%) + funding rate income (shorts get paid when funding is positive)

Revenue breakdown:

  • Staking yield: ~3-4% APR (consistent)
  • Funding rate: historically ~8-15% APR average, but highly variable
  • Combined: sUSDe has offered 15-30%+ APR at times

Risk factors:

  • Funding rate reversal: In bear markets, funding rates go negative (shorts PAY longs). Ethena’s insurance fund covers short periods, but prolonged negative funding would erode backing. During the 2022 bear market, funding was negative for months.
  • Exchange counterparty risk: Positions are on centralized exchanges (Binance, Bybit, Deribit) via custodians (Copper, Ceffu). If an exchange fails or freezes, positions can’t be managed.
  • Basis risk: Spot price and futures price can diverge, creating temporary unbacking.
  • Custodian risk: Assets held in β€œoff-exchange settlement” custody, not directly on exchanges.
  • Insurance fund: ~$50M+ reserve for negative funding periods. If depleted, USDe backing degrades.

πŸ”— Connection: The funding rate mechanics here connect directly to Part 3 Module 2 (Perpetuals), where you’ll study how funding rates work in detail. Ethena is essentially using a DeFi primitive (perpetual funding) as a stablecoin backing mechanism.

πŸ’‘ Concept: GHO: Aave’s Native Stablecoin

GHO is a decentralized stablecoin minted directly within Aave V3. It extends the lending protocol with stablecoin issuance β€” users who already have collateral in Aave can mint GHO against it without removing their collateral from the lending pool.

Key design choices:

  • No separate CDP system β€” GHO uses Aave V3’s existing collateral and liquidation infrastructure
  • Facilitators β€” entities authorized to mint/burn GHO. Aave V3 Pool is the primary facilitator, but others can be added (e.g., a flash mint facilitator)
  • Interest rate is governance-set β€” not algorithmic. MakerDAO’s stability fee is also governance-set, but Aave’s rate doesn’t depend on utilization since there’s no supply pool
  • stkAAVE discount β€” AAVE stakers get a discount on GHO borrow rates (incentivizes AAVE staking)
  • Built on existing battle-tested infrastructure β€” Aave’s oracle, liquidation, and risk management systems

Why it matters: GHO shows how a lending protocol can evolve into a stablecoin issuer without building separate infrastructure. Since you studied Aave V3 in Module 4, GHO is a natural extension of that knowledge.

πŸ’‘ Concept: crvUSD: Curve’s Soft-Liquidation Model (LLAMMA)

Curve’s stablecoin crvUSD introduces a novel liquidation mechanism called LLAMMA (Lending-Liquidating AMM Algorithm) that replaces the traditional discrete liquidation threshold with continuous soft liquidation.

How LLAMMA works:

  • Collateral is deposited into a special AMM (not a regular lending pool)
  • As collateral price drops, the AMM automatically converts collateral to crvUSD (soft liquidation)
  • As price recovers, the AMM converts back from crvUSD to collateral (de-liquidation)
  • No sudden liquidation event β€” instead, a gradual, continuous transition
  • Borrower keeps their position throughout (unless price drops too far)

Why it’s novel:

  • Traditional CDPs: price hits threshold β†’ entire position liquidated β†’ penalty applied β†’ user loses collateral
  • LLAMMA: price drops β†’ collateral gradually converts β†’ price recovers β†’ collateral converts back
  • Reduces liquidation losses for borrowers (no penalty on soft liquidation)
  • Reduces bad debt risk for the protocol (continuous adjustment vs sudden cascade)

Trade-offs:

  • During soft liquidation, the AMM-converted position earns less than holding pure collateral (similar to impermanent loss in an LP position)
  • More complex than traditional liquidation β€” harder to reason about and audit
  • LLAMMA pools need liquidity to function properly

πŸ”— Connection: crvUSD’s LLAMMA is essentially an AMM (Module 2) repurposed as a liquidation mechanism (Module 4). It shows how DeFi primitives can be combined in unexpected ways.

πŸ’‘ Concept: FRAX: The Evolution from Algorithmic to Fully Backed

FRAX started as a β€œfractional-algorithmic” stablecoin β€” partially backed by collateral (USDC) and partially by its governance token (FXS). The collateral ratio would adjust algorithmically based on market conditions.

The evolution:

  1. V1 (2020): Fractional-algorithmic β€” e.g., 85% USDC + 15% FXS backing
  2. V2 (2022): Moved toward 100% collateral ratio after algorithmic stablecoins collapsed (Terra)
  3. V3 / frxETH (2023+): Pivoted to liquid staking (frxETH, sfrxETH) and became fully collateralized

The lesson: The algorithmic component was abandoned because it created the same reflexive risk as Terra β€” when confidence drops, the algorithmic portion amplifies the problem. FRAX’s evolution mirrors the industry’s consensus: full collateral backing is necessary.

πŸ”— Connection: frxETH and sfrxETH are liquid staking tokens covered in Part 3 Module 1. FRAX’s pivot illustrates how stablecoin protocols evolve toward safety.

Design Trade-off Matrix

PropertyDAI/USDSLUSDGHOcrvUSDUSDeUSDC
DecentralizationMediumHighMediumMediumLowLow
Capital efficiencyLow (150%+)Medium (110%)Low (Aave LTVs)Medium (soft liq.)~1:11:1
Peg stabilityStrong (PSM)Good (redemptions)ModerateModerateGoodVery strong
YieldDSR/SSRNoneDiscount for stkAAVENoneHigh (15%+)None
LiquidationDutch auctionStability PoolAave standardSoft (LLAMMA)N/AN/A
Failure modeBad debt, USDC dep.Bad debtSame as AaveLLAMMA ILFunding reversalRegulatory

πŸ’‘ Concept: The Fundamental Trilemma

Stablecoins face a trilemma between:

  1. Decentralization β€” no central point of failure or censorship
  2. Capital efficiency β€” not requiring significantly more collateral than the stablecoins minted
  3. Peg stability β€” maintaining a reliable $1 value

No design achieves all three. DAI sacrifices efficiency for decentralization and stability. USDC sacrifices decentralization for efficiency and stability. Algorithmic designs attempt efficiency and decentralization but sacrifice stability (and typically fail).

Understanding this trilemma is essential for evaluating any stablecoin design you encounter or build.

πŸ” Deep Dive: Peg Mechanism Comparison

How does each stablecoin actually maintain its $1 peg? The mechanisms are fundamentally different:

                        When price > $1                    When price < $1
                        (too much demand)                  (too much supply)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ DAI/USDS β”‚ PSM: swap USDC β†’ DAI at 1:1  β”‚ PSM: swap DAI β†’ USDC at 1:1     β”‚
β”‚          β”‚ ↑ supply β†’ price falls        β”‚ ↓ supply β†’ price rises          β”‚
β”‚          β”‚ Also: lower stability fee β†’   β”‚ Also: raise stability fee β†’     β”‚
β”‚          β”‚ more vaults open β†’ more DAI   β”‚ vaults close β†’ less DAI         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ LUSD     β”‚ Anyone can open vault at 110% β”‚ Redemption: burn 1 LUSD β†’       β”‚
β”‚          β”‚ cheap to mint β†’ more supply   β”‚ get $1 of ETH from riskiest     β”‚
β”‚          β”‚ (soft ceiling at $1.10)       β”‚ vault (hard floor at $1.00)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ GHO      β”‚ Governance lowers borrow rate β”‚ Governance raises borrow rate   β”‚
β”‚          β”‚ β†’ more minting β†’ more supply  β”‚ β†’ repayment β†’ less supply      β”‚
β”‚          β”‚ (slower response than PSM)    β”‚ (slower response than PSM)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ crvUSD   β”‚ Minting via LLAMMA pools     β”‚ PegKeeper contracts buy crvUSD  β”‚
β”‚          β”‚ + PegKeepers sell crvUSD      β”‚ from pools, reducing supply     β”‚
β”‚          β”‚ into Curve pools              β”‚ (automated, no governance)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ USDe     β”‚ Arbitrage: mint USDe at $1,  β”‚ Arbitrage: buy USDe < $1,       β”‚
β”‚          β”‚ sell at market for > $1       β”‚ redeem for $1 of backing        β”‚
β”‚          β”‚ (requires whitelisting)       β”‚ (requires whitelisting)         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ USDC     β”‚ Circle mints new USDC for $1 β”‚ Circle redeems USDC for $1 bank β”‚
β”‚          β”‚ bank deposit                 β”‚ transfer                        β”‚
β”‚          β”‚ (centralized, instant)       β”‚ (centralized, 1-3 business days)β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Speed of peg restoration:
  USDC β‰ˆ PSM (DAI) > Redemption (LUSD) > PegKeeper (crvUSD) > Arb (USDe) > Governance (GHO)
  ← faster                                                            slower β†’

The pattern: Faster peg restoration requires either centralization (USDC, PSM’s USDC dependency) or capital lock-up (Liquity’s 110% CR). Slower mechanisms preserve decentralization but risk prolonged depegs during stress.

🎯 Build Exercise: Stablecoin Design Trade-offs

Workspace: workspace/test/part2/module6/exercise4b-peg-dynamics/ β€” test-only exercise: PegDynamics.t.sol (tests PSM peg restoration, reserve depletion, stability fee dynamics, and the fee/PSM feedback loop β€” requires SimplePSM and SimpleJug to be implemented)

Exercise 1: Liquity analysis. Read Liquity’s TroveManager.sol and StabilityPool.sol. Compare the liquidation mechanism (Stability Pool absorption) to MakerDAO’s Dutch auctions. Which is simpler? Which handles edge cases better? Write a comparison document.

Exercise 2: Peg stability simulation. Using your SimpleCDP from the Build exercise, simulate peg pressure scenarios:

  • What happens when collateral prices crash 30%? Model the liquidation volume and its impact.
  • What happens when demand for the stablecoin exceeds Vault creation? The price goes above $1. Show how the PSM resolves this.
  • What happens if the PSM’s USDC reserves are depleted? The stablecoin trades above $1 with no easy correction.

Exercise 3: Stability fee as monetary policy. Using your Jug implementation, model how changing the stability fee affects Vault behavior:

  • High fee β†’ users repay and close Vaults β†’ stablecoin supply decreases β†’ price pressure upward
  • Low fee β†’ users open Vaults β†’ stablecoin supply increases β†’ price pressure downward
  • The DSR works in reverse: high DSR β†’ more DAI locked β†’ supply decreases β†’ price up

Map out the feedback loops. This is how decentralized monetary policy works.

πŸ’Ό Job Market Context β€” Module-Level Interview Prep

What DeFi teams expect you to know:

  1. β€œExplain the stablecoin trilemma and where DAI sits”

    • Good answer: DAI trades off capital efficiency for decentralization and stability
    • Great answer: DAI started decentralized (ETH-only collateral) but the PSM made it USDC-dependent for better stability. The Sky rebrand pushed further toward centralization with USDS’s freeze function. Liquity V1 sits at the opposite extreme β€” fully decentralized and immutable, but less capital-efficient and narrower collateral. No design achieves all three.
  2. β€œHow does crvUSD’s LLAMMA differ from traditional liquidation?”

    • Good answer: It gradually converts collateral instead of a sudden liquidation event
    • Great answer: LLAMMA is essentially an AMM where your collateral IS the liquidity. As price drops, the AMM sells your collateral for crvUSD automatically. If price recovers, it buys back. This eliminates the discrete liquidation penalty but introduces AMM-like impermanent loss during soft liquidation. It’s a fundamentally different paradigm β€” continuous adjustment vs threshold-triggered liquidation.
  3. β€œWhat’s the main risk with Ethena’s USDe?”

    • Good answer: Funding rates can go negative
    • Great answer: Three correlated risks: (1) prolonged negative funding drains the insurance fund and erodes backing, (2) centralized exchange counterparty risk β€” positions are on Binance/Bybit/Deribit via custodians, (3) during a black swan, all three risks compound simultaneously (funding reversal + exchange stress + basis blowout). The model works great in bull markets with positive funding but hasn’t been tested through a severe extended downturn with the current AUM.

Interview Red Flags:

  • 🚩 Thinking algorithmic stablecoins without external collateral can work (Terra killed this thesis)
  • 🚩 Not knowing the difference between a CDP and a lending protocol
  • 🚩 Inability to explain how stability fees act as monetary policy
  • 🚩 Not recognizing that MakerDAO’s terse naming convention exists (demonstrates you haven’t read the code)

Pro tip: The stablecoin landscape is one of the most interview-relevant DeFi topics because it touches everything β€” oracles, liquidation, governance, monetary policy, risk management. Being able to compare MakerDAO vs Liquity vs Ethena design trade-offs demonstrates systems-level thinking that teams value highly.


πŸ“‹ Summary: Stablecoin Landscape

βœ“ Covered:

  • Stablecoin taxonomy: fiat-backed, overcollateralized, algorithmic, delta-neutral
  • MakerDAO vs Liquity: governance vs immutability, Dutch auction vs Stability Pool
  • GHO: stablecoin built on existing lending infrastructure (Aave V3)
  • crvUSD: novel soft-liquidation via LLAMMA (AMM-based continuous adjustment)
  • FRAX evolution: fractional-algorithmic β†’ fully collateralized (lesson from Terra)
  • Ethena USDe: delta-neutral hedging with funding rate revenue and its risks
  • The fundamental trilemma: decentralization vs capital efficiency vs stability
  • Terra collapse as the definitive failure case for uncollateralized algorithmic designs

Internalized patterns: CDPs mint money (create new stablecoins, closer to central banking than commercial banking). The Vat is the source of truth (frob() and normalized debt art x rate). Liquidation design is existential (Black Thursday proved auction mechanics can fail; Dutch auction vs Stability Pool vs LLAMMA). Peg stability requires trade-offs (PSM = stability + centralization, redemptions = decentralization + less capital-efficiency). Algorithmic stablecoins without external collateral fail (Terra $40B collapse). rpow() and rate accumulators are the mathematical backbone (per-second compounding via exponentiation by squaring). Stablecoins are the ultimate integration test (oracles, lending, liquidation, governance, AMMs). The landscape is diversifying (GHO, crvUSD, Ethena USDe each recombine DeFi primitives differently).

Next: Module 7 β€” ERC-4626 tokenized vaults, yield aggregation, inflation attacks


⚠️ Common Mistakes

Mistake 1: Confusing normalized debt (art) with actual debt (art Γ— rate)

// ❌ WRONG β€” reading art directly as the debt amount
(uint256 ink, uint256 art) = vat.urns(ilk, user);
uint256 debtOwed = art;  // This is NORMALIZED debt, not actual

// βœ… CORRECT β€” multiply by the rate accumulator
(uint256 ink, uint256 art) = vat.urns(ilk, user);
(, uint256 rate,,,) = vat.ilks(ilk);
uint256 debtOwed = art * rate / RAY;  // Actual debt in WAD

After years of accumulated stability fees, rate might be 1.15e27 (15% total fees). Reading art directly would understate the debt by 15%. This same mistake applies to Aave’s scaledBalance vs actual balance.

Mistake 2: Forgetting to call drip() before reading or modifying debt

// ❌ WRONG β€” rate is stale (hasn't been updated since last drip)
(, uint256 rate,,,) = vat.ilks(ilk);
uint256 debtOwed = art * rate / RAY;  // Could be hours/days stale

// βœ… CORRECT β€” drip first to update the rate accumulator
jug.drip(ilk);  // Updates rate to current timestamp
(, uint256 rate,,,) = vat.ilks(ilk);
uint256 debtOwed = art * rate / RAY;  // Accurate to this block

If nobody has called drip() for a week, the rate is a week stale. Any debt calculation, vault safety check, or liquidation trigger that reads the stale rate will be wrong. In practice, keepers and frontends call drip() before state-changing operations. Your contracts should too.

Mistake 3: PSM decimal mismatch (USDC is 6 decimals, DAI is 18)

// ❌ WRONG β€” treating USDC and DAI amounts as the same scale
uint256 usdcAmount = 1000e18;  // 10^21 base units Γ· 10^6 decimals = 10^15 USDC ($1 quadrillion!)
psm.sellGem(address(this), usdcAmount);

// βœ… CORRECT β€” USDC uses 6 decimals
uint256 usdcAmount = 1000e6;   // 1000 USDC ($1,000)
psm.sellGem(address(this), usdcAmount);
// The PSM internally handles the 6β†’18 decimal conversion via GemJoin

The PSM’s GemJoin adapter handles the decimal conversion (gem_amount * 10^(18-6)), but you must pass the USDC amount in USDC’s native 6-decimal scale. Passing 18-decimal amounts will either overflow or swap vastly more than intended.

Mistake 4: Not checking the dust minimum when partially repaying

// ❌ WRONG β€” partial repay leaves vault below dust threshold
// Vault has 10,000 DAI debt, dust = 5,000 DAI
vat.frob(ilk, address(this), address(this), address(this), 0, -int256(8000e18));
// Tries to reduce debt to 2,000 DAI β†’ REVERTS (below dust of 5,000)

// βœ… CORRECT β€” either repay fully (dart = -art) or keep above dust
// Option A: Full repay
vat.frob(ilk, ..., 0, -int256(art));  // Close to 0 debt
// Option B: Stay above dust
vat.frob(ilk, ..., 0, -int256(5000e18));  // Leaves 5,000 DAI β‰₯ dust

The dust parameter prevents tiny vaults whose gas costs for liquidation would exceed the recovered value. When a vault has debt, it must be either 0 or β‰₯ dust. This catches developers who try to partially repay without checking.


← Backward References (Part 1 + Modules 1–5)

SourceConceptHow It Connects
Part 1 Module 1mulDiv / fixed-point mathWAD/RAY/RAD arithmetic throughout the Vat; rmul/rpow for stability fee compounding
Part 1 Module 1Custom errorsProduction CDP contracts use custom errors for vault safety violations, ceiling breaches
Part 1 Module 2Transient storageModern CDP implementations can use TSTORE for reentrancy guards during liquidation callbacks
Part 1 Module 5Fork testing / vm.mockCallEssential for testing against live MakerDAO state and simulating oracle price drops for liquidation
Part 1 Module 5Invariant testingProperty-based testing for CDP invariants: total debt ≀ total DAI, all vaults safe, rate monotonicity
Part 1 Module 6Proxy patternsMakerDAO’s authorization system (wards/can) and join adapter pattern for upgradeable periphery
Module 1SafeERC20 / token decimalsJoin adapters bridge external ERC-20 tokens to Vat’s internal accounting; decimal handling critical for multi-collateral
Module 1Fee-on-transfer awarenessCollateral join adapters must handle non-standard token behavior; PSM must handle USDC’s blacklist
Module 2AMM / Curve StableSwapPSM uses 1:1 swap; crvUSD’s LLAMMA repurposes AMM as liquidation mechanism; Curve pools for peg monitoring
Module 3Oracle Security Module (OSM)MakerDAO delays oracle prices by 1 hour via OSM β€” gives governance reaction time before liquidations
Module 3Chainlink / staleness checksCollateral pricing for vault safety checks; oracle failure triggers emergency shutdown
Module 4Index-based accountingNormalized debt (art Γ— rate) is the same pattern as Aave’s scaledBalance Γ— liquidityIndex
Module 4Liquidation mechanicsDutch auction (Dog/Clipper) parallels Aave’s direct liquidation; Stability Pool parallels Compound’s absorb
Module 5Flash loans / flash mintDutch auctions designed for flash loan compatibility; DssFlash mints unlimited DAI for flash borrowing

β†’ Forward References (Modules 7–9 + Part 3)

TargetConceptHow Stablecoin/CDP Knowledge Applies
Module 7 (Yield/Vaults)sUSDS as ERC-4626Sky Savings Rate packaged as standard vault interface β€” stablecoin meets tokenized vault
Module 7 (Yield/Vaults)DSR as yield sourceDAI Savings Rate and sUSDS as yield-bearing stablecoin deposits for vault strategies
Module 8 (Security)CDP invariant testingInvariant testing SimpleCDP: total debt ≀ ceiling, all active vaults safe, rate accumulator monotonic
Module 8 (Security)Peg stability threat modelModeling peg attacks: PSM drain, oracle manipulation, governance parameter manipulation
Module 9 (Capstone)Multi-collateral stablecoinBuilding a decentralized stablecoin protocol β€” CDP engine, Dutch auction liquidation, flash mint, vault share collateral
Part 3 Module 1 (Liquid Staking)LSTs as collateralwstETH, rETH as CDP collateral types β€” requires exchange rate oracle chaining
Part 3 Module 2 (Perpetuals)Funding rate mechanicsEthena’s USDe uses perpetual funding rates as stablecoin backing β€” studied in depth
Part 3 Module 8 (Governance)Monetary policy governanceGovernor for stability fee, DSR, debt ceiling parameter updates; governance attack surface

πŸ“– Production Study Order

Study these codebases in order β€” each builds on the previous one’s patterns:

#RepositoryWhy Study ThisKey Files
1MakerDAO dss (Vat)The foundational CDP engine β€” normalized debt, rate accumulator, frob() as the atomic vault operationsrc/vat.sol
2MakerDAO JugStability fee accumulator β€” per-second compounding via drip(), same index pattern as lending protocolssrc/jug.sol
3MakerDAO Dog + ClipperLiquidation 2.0 β€” Dutch auction mechanics, circuit breakers, keeper incentives (post-Black Thursday redesign)src/dog.sol, src/clip.sol, src/abaci.sol
4MakerDAO PSMPeg Stability Module β€” 1:1 stablecoin swaps, tin/tout fee mechanism, centralization trade-offsrc/psm.sol
5Liquity V1Alternative CDP: no governance, 110% CR, Stability Pool instant liquidation, redemption mechanismcontracts/TroveManager.sol, contracts/StabilityPool.sol, contracts/BorrowerOperations.sol
6crvUSD LLAMMANovel soft-liquidation via AMM β€” continuous collateral conversion, PegKeeper for peg maintenancecontracts/AMM.sol, contracts/Controller.sol

Reading strategy: Start with the Vat β€” memorize the glossary (ilk, urn, ink, art, gem, dai, sin) and read frob() line by line. Then Jug for the fee accumulator. Dog + Clipper show the Dutch auction (trace bark() β†’ kick() β†’ take()). PSM is short and shows the peg mechanism. Liquity shows a radically different CDP design (no governance). crvUSD shows the frontier: AMM-based soft liquidation.


πŸ“š Resources

MakerDAO/Sky:

Liquity:

GHO:

crvUSD:

Ethena:

Stablecoin analysis:

  • CDP classical design
  • Terra post-mortem: Search β€œTerra LUNA collapse analysis” for numerous detailed breakdowns

Black Thursday:


Navigation: ← Module 5: Flash Loans | Module 7: Vaults & Yield β†’