Decentralized Stablecoin

A complete interactive guide to understanding how a DeFi stablecoin works - from concepts to code

Exogenous Algorithmic USD Pegged

Scroll down to begin your journey โ†“

๐Ÿช™ What is a Stablecoin?

Let's start from the very basics

The Piggy Bank Analogy

Imagine you have a special piggy bank. For every $2 worth of toys you put in, you get a special golden coin worth $1.

๐Ÿ“ฆ You put in: $2 worth of toys (WETH/WBTC)

๐Ÿช™ You get: $1 golden coin (DSC)

Why $2 for $1? Because even if your toys lose some value, there's still enough to back your golden coin!

The Three Properties of Our Stablecoin

Property What It Means Real World Example
๐ŸŒ Exogenous Backed by EXTERNAL assets (WETH, WBTC) Like a gold-backed currency - the gold exists outside the money system
๐Ÿค– Algorithmic Rules-based, no human decisions Like a vending machine - put in money, get product, no cashier needed
๐Ÿ’ต USD Pegged 1 DSC = $1 always Like how arcade tokens are always worth the same in that arcade

Why 200% Collateralization?

๐Ÿ‘ค
You deposit
$2000
worth of ETH
โ†’ Protocol holds it
๐Ÿฆ
DSCEngine
$2000
collateral locked
โ†’ You can mint
๐Ÿช™
You receive
$1000
DSC tokens

Why not just 100%?

Imagine ETH price drops 30%. If you only had 100% backing:

โŒ Your $1000 collateral is now worth $700 โ†’ Can't back $1000 DSC!

With 200% backing:

โœ… Your $2000 collateral is now worth $1400 โ†’ Still backs $1000 DSC!

โค๏ธ Understanding Health Factor

The most important number in DeFi lending

What is Health Factor?

Health Factor Formula:

(Collateral Value ร— 50%)
Total DSC Minted

The Safety Score

Think of Health Factor like a safety score for your loan:

  • Above 1.0 = You're safe! ๐Ÿ˜Š
  • Exactly 1.0 = Danger zone! ๐Ÿ˜ฐ
  • Below 1.0 = You can be liquidated! ๐Ÿ˜ฑ

Interactive Health Bar (adjust values below)

1.5

๐Ÿงฎ Interactive Health Factor Calculator

Your Health Factor:

2.0

โœ… Safe - You cannot be liquidated

๐Ÿ“ Step-by-Step Calculation:

1 Collateral Value = 1 ETH ร— $2000 = $2000
2 Adjusted Collateral = $2000 ร— 50% = $1000
3 Health Factor = $1000 รท $500 = 2.0

โšก How Liquidation Works

The Bounty Hunter Analogy

When your safety score drops below 1.0, you become a "wanted" person. Anyone can be a bounty hunter!

๐Ÿค  Bounty Hunter (Liquidator): "I'll pay off some of your debt!"

๐Ÿ’ฐ Reward: They get your collateral + 10% bonus!

This keeps the whole system healthy because bad positions get cleaned up.

1

Bob's Position Goes Underwater

Bob deposited 1 ETH at $2000, minted $1000 DSC. ETH crashes to $1500.

Health Factor: ($1500 ร— 50%) รท $1000 = 0.75 โŒ

2

Alice Sees an Opportunity

Alice has $500 DSC and wants to liquidate Bob's position.

3

The Math

Alice pays: $500 DSC

Alice receives: $500 worth of ETH + 10% bonus = $550 worth of ETH

Alice profit: $50! ๐ŸŽ‰

โšก Liquidation Profit Calculator

You pay: $500 DSC

You receive: 0.367 ETH ($550)

+$50

That's your 10% liquidation bonus! ๐ŸŽ‰

๐Ÿ—๏ธ Protocol Architecture

How all the pieces fit together

System Overview

๐Ÿ‘ค
User
Deposits collateral Mints/Burns DSC
โ†”๏ธ
โš™๏ธ
DSCEngine
Core Logic ~700 lines
โ†”๏ธ
๐Ÿช™
DSC Token
ERC20 ~60 lines
๐Ÿ”—
Chainlink ETH/USD
๐Ÿ”—
Chainlink BTC/USD
๐Ÿ’Ž
WETH Collateral
๐ŸŸ 
WBTC Collateral

DSCEngine.sol - The Brain

Handles all collateral management, minting, burning, and liquidations

Key State Variables

// Who deposited what mapping(address user => mapping(address token => uint256)) private s_collateralDeposited; // How much DSC each user minted (their debt) mapping(address user => uint256) private s_DSCMinted; // Token โ†’ Chainlink Price Feed mapping(address token => address priceFeed) private s_priceFeeds;

Key Constants

50%
Liquidation Threshold
10%
Liquidation Bonus
1e18
Min Health Factor
1e18
Precision

Main Functions

depositCollateral()

Deposit WETH or WBTC as collateral

function depositCollateral(address token, uint256 amount) public { s_collateralDeposited[msg.sender][token] += amount; // Transfer tokens from user to this contract IERC20(token).transferFrom(msg.sender, address(this), amount); }

mintDSC()

Mint DSC against your collateral

function mintDSC(uint256 amount) public { s_DSCMinted[msg.sender] += amount; _revertIfHealthFactorIsBroken(msg.sender); // Critical check! i_dsc.mint(msg.sender, amount); }

liquidate()

Liquidate unhealthy positions and earn 10% bonus

function liquidate(address collateral, address user, uint256 debtToCover) public { // 1. Check user is actually liquidatable require(_healthFactor(user) < MIN_HEALTH_FACTOR); // 2. Calculate collateral to take (debt + 10% bonus) uint256 tokenAmount = getTokenAmountFromUsd(collateral, debtToCover); uint256 bonus = tokenAmount * 10 / 100; // 3. Take collateral, burn DSC _redeemCollateral(user, msg.sender, collateral, tokenAmount + bonus); _burnDSC(debtToCover, user, msg.sender); }

DecentralizedStableCoin.sol - The Token

Simple ERC20 token that only DSCEngine can mint/burn

Why is it so simple?

The token itself doesn't need to be smart. It's just a "receipt" for your position.

All the intelligence is in DSCEngine - the token just says "DSCEngine is my boss".

contract DecentralizedStableCoin is ERC20Burnable, Ownable { // Only the owner (DSCEngine) can mint new tokens function mint(address to, uint256 amount) external onlyOwner returns (bool) { _mint(to, amount); return true; } // Only the owner (DSCEngine) can burn tokens function burn(uint256 amount) public override onlyOwner { super.burn(amount); } }

OracleLib.sol - The Price Guardian

Protects against stale/manipulated price data

The Newspaper Analogy

Imagine you only trust today's newspaper for stock prices. If someone gives you a 3-day old newspaper and says "buy!", you'd refuse.

OracleLib does the same - it checks if the price data is "fresh" (less than 3 hours old).

Why is this critical?

!

Stale Price Attack

Real ETH: $1000 (crashed!)

Stale Oracle: $2000 (old data)

Attacker deposits 1 ETH โ†’ Protocol thinks $2000 โ†’ Mints $1000 DSC

Protocol is now UNDERCOLLATERALIZED! ๐Ÿ’€

function staleCheckLatestRoundData(AggregatorV3Interface feed) public view { (,, , uint256 updatedAt,) = feed.latestRoundData(); uint256 secondsSince = block.timestamp - updatedAt; // If price is older than 3 hours, REVERT! if (secondsSince > 3 hours) { revert OracleLib__StalePrice(); } }

๐Ÿงช Testing Strategy

How we ensure the protocol is secure

Testing Pyramid

Invariant
Fuzz Tests
Unit Tests (29 tests)

Invariant Testing - The Ultimate Test

The "No Matter What" Rule

An invariant is something that must ALWAYS be true, no matter what users do.

Our invariant: "Total collateral value โ‰ฅ Total DSC minted"

We let the fuzzer do THOUSANDS of random actions, then check if this rule still holds!

function invariant_protocolMustHaveMoreValueThanTotalSupply() public { uint256 totalSupply = dsc.totalSupply(); uint256 wethValue = dscEngine.getUSDValue(weth, weth.balanceOf(address(dscEngine))); uint256 wbtcValue = dscEngine.getUSDValue(wbtc, wbtc.balanceOf(address(dscEngine))); // THE INVARIANT: Protocol must always be solvent! assert(wethValue + wbtcValue >= totalSupply); }
1000
Fuzz Runs
128,000
Total Calls
0
Reverts
31
Tests Passed

Handler Pattern - Making Fuzz Tests Smart

The Helpful Assistant

Without a Handler, the fuzzer calls random functions with random inputs - most calls fail!

The Handler is like a helpful assistant that:

  • Mints tokens before depositing
  • Approves spending before transfers
  • Bounds values to valid ranges

Without Handler vs With Handler

โŒ Without Handler

Fuzzer: "deposit 1000 WETH!"

Contract: "You don't have any WETH..."

REVERT!

โœ… With Handler

Handler: "Let me mint WETH first..."

Handler: "And approve spending..."

Handler: "Now deposit!"

SUCCESS!

๐Ÿ”ข Understanding Decimal Precision

The math behind Solidity's number system

Why 1e18? The Precision Problem

The Decimal Dilemma

Solidity doesn't have decimals! You can't write 1.5 ETH.

Solution: Use REALLY big numbers and pretend the last 18 digits are decimals.

1 ETH = 1,000,000,000,000,000,000 wei (that's 1 followed by 18 zeros = 1e18)

1e18
ETH Precision
1e8
Chainlink Price
1e10
Additional Precision
1e18
DSC Token

The Precision Ladder

1 Chainlink returns: 200000000000 (ETH = $2000, 8 decimals)
2 Multiply by 1e10: 2000000000000000000000 (now 18 decimals)
3 User has 1 ETH = 1000000000000000000 (1e18 wei)
4 USD Value = (price ร— amount) / 1e18 = $2000
// PRECISION CONSTANTS uint256 private constant PRECISION = 1e18; // 18 decimals for math uint256 private constant ADDITIONAL_FEED_PRECISION = 1e10; // 8โ†’18 decimals uint256 private constant FEED_PRECISION = 1e8; // Chainlink uses 8 // Converting ETH amount to USD value function _getUsdValue(address token, uint256 amount) private view returns (uint256) { (, int256 price,,,) = priceFeed.latestRoundData(); // price = 200000000000 ($2000 with 8 decimals) // amount = 1000000000000000000 (1 ETH with 18 decimals) return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION; // ((200000000000 * 1e10) * 1e18) / 1e18 // = 2000e18 (which represents $2000) }

Reverse Conversion: USD to Token Amount

Getting ETH from USD

If I want $500 worth of ETH and ETH is $2000, how much ETH do I get?

Simple: $500 รท $2000 = 0.25 ETH

In code: (500e18 ร— 1e18) รท (2000e8 ร— 1e10) = 0.25e18 = 250000000000000000 wei

// USD to Token conversion (used in liquidation) function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) { (, int256 price,,,) = priceFeed.latestRoundData(); // usdAmountInWei = 500e18 ($500) // price = 200000000000 ($2000) return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION); // (500e18 * 1e18) / (200000000000 * 1e10) // = 500e36 / 2000e18 // = 0.25e18 = 250000000000000000 wei = 0.25 ETH }

๐Ÿšถ Complete User Journey

Follow the entire flow from start to finish

Step 1: Deposit Collateral

๐Ÿ‘ค
Alice
Has 2 WETH
โ†’ approve()
๐Ÿ’Ž
WETH
ERC20
โ†’ depositCollateral()
โš™๏ธ
DSCEngine
Holds collateral
1a

User approves DSCEngine to spend their WETH

weth.approve(address(dscEngine), 2e18); // 2 WETH
1b

User calls depositCollateral

dscEngine.depositCollateral(address(weth), 2e18);
1c

State Changes

s_collateralDeposited[alice][weth]: 0 โ†’ 2e18

weth.balanceOf(alice): 2e18 โ†’ 0

weth.balanceOf(dscEngine): 0 โ†’ 2e18

Step 2: Mint DSC (Borrow)

๐Ÿ‘ค
Alice
Has collateral
โ†’ mintDSC()
โš™๏ธ
DSCEngine
Checks health
โ†’ mint()
๐Ÿช™
DSC Token
Creates tokens
2a

User wants to mint DSC

dscEngine.mintDSC(1500e18); // Mint $1500 DSC
2b

Engine calculates health factor

1 Collateral: 2 ETH ร— $2000 = $4000
2 Adjusted: $4000 ร— 50% = $2000
3 Health: $2000 รท $1500 = 1.33 โœ“
2c

State Changes

s_DSCMinted[alice]: 0 โ†’ 1500e18

dsc.balanceOf(alice): 0 โ†’ 1500e18

dsc.totalSupply(): 0 โ†’ 1500e18

Step 3: Use DSC (In Real World)

What can Alice do with DSC?

  • ๐Ÿ’ฑ Trade on DEXs (Uniswap, Curve)
  • ๐Ÿฆ Lend on other protocols for yield
  • ๐Ÿ’ฐ Use as stable payment
  • ๐Ÿ”„ Provide liquidity in LP pools

The DSC is REAL money on the blockchain!

Step 4: Burn DSC & Withdraw (Exit Position)

๐Ÿ‘ค
Alice
Wants ETH back
โ†’ burnDSC()
๐Ÿ”ฅ
Burn DSC
Reduce debt
โ†’ redeemCollateral()
๐Ÿ’Ž
Get WETH
Back to Alice
4a

Alice approves DSCEngine to burn her DSC

dsc.approve(address(dscEngine), 1500e18);
4b

Burn DSC to reduce debt

dscEngine.burnDSC(1500e18); // s_DSCMinted[alice]: 1500e18 โ†’ 0 // DSC tokens are destroyed forever
4c

Redeem collateral

dscEngine.redeemCollateral(address(weth), 2e18); // Alice gets her 2 WETH back!

The Combo Function

There's also redeemCollateralForDSC() that does burn + redeem in one transaction!

dscEngine.redeemCollateralForDSC(weth, 2e18, 1500e18); // Burns 1500 DSC AND withdraws 2 WETH - saves gas!

๐Ÿ’€ Liquidation Deep Dive

Complete flow of a liquidation event

Scenario: ETH Crashes, Bob Gets Liquidated

0

Initial State (Bob is Healthy)

Bob deposited: 1 ETH @ $2000

Bob minted: $800 DSC

Health Factor: ($2000 ร— 50%) รท $800 = 1.25 โœ“

1

ETH Price Crashes! ๐Ÿ“‰

ETH drops from $2000 โ†’ $1400

Bob's collateral value: 1 ETH ร— $1400 = $1400

New Health Factor: ($1400 ร— 50%) รท $800 = 0.875 โœ—

โš ๏ธ Bob is now LIQUIDATABLE!

2

Alice Spots the Opportunity ๐Ÿ”

Alice has $400 DSC and wants to liquidate Bob

// Alice checks Bob's health factor uint256 healthFactor = dscEngine.getHealthFactor(bob); // Returns 0.875e18 - BELOW 1e18, can be liquidated!
3

The Liquidation Math

1 Alice pays: $400 DSC (debtToCover)
2 Base ETH: $400 รท $1400 = 0.2857 ETH
3 Bonus (10%): 0.2857 ร— 0.10 = 0.0286 ETH
4 Total: 0.2857 + 0.0286 = 0.3143 ETH
5 Value: 0.3143 ร— $1400 = $440
โœ“ Alice profit: $440 - $400 = $40! ๐ŸŽ‰
4

Execute Liquidation

// Alice approves DSCEngine to take her DSC dsc.approve(address(dscEngine), 400e18); // Alice liquidates Bob dscEngine.liquidate( address(weth), // collateral to take bob, // user to liquidate 400e18 // debt to cover );
5

Final State Changes

Variable Before After
Bob's Collateral 1 ETH 0.6857 ETH
Bob's Debt $800 DSC $400 DSC
Bob's Health Factor 0.875 1.2 โœ“
Alice's DSC $400 $0
Alice's ETH 0 0.3143 ETH ($440)

๐Ÿšจ Error Handling

Understanding all custom errors

DSCEngine Custom Errors

Error When It Happens Fix
NeedsMoreThanZero Trying to deposit/mint/burn 0 amount Pass amount > 0
TokenNotAllowed Using unsupported collateral token Only use WETH or WBTC
TransferFailed ERC20 transfer didn't work Check token approval/balance
BreaksHealthFactor Action would make health factor < 1 Deposit more or mint less
MintFailed DSC token mint returned false Check DSCEngine is owner
HealthFactorOk Trying to liquidate healthy user Can only liquidate HF < 1
HealthFactorNotImproved Liquidation didn't help the user Cover more debt
// All custom errors defined at the top of DSCEngine error DSCEngine__TokenAddressesAndPriceFeedAddressesAmountsDontMatch(); error DSCEngine__NeedsMoreThanZero(); error DSCEngine__TokenNotAllowed(address token); error DSCEngine__TransferFailed(); error DSCEngine__BreaksHealthFactor(uint256 healthFactorValue); error DSCEngine__MintFailed(); error DSCEngine__HealthFactorOk(); error DSCEngine__HealthFactorNotImproved();

OracleLib Error

The Single Error That Freezes Everything

OracleLib__StalePrice()

This error is thrown when:

  • updatedAt == 0 - Price was never set
  • answeredInRound < roundId - Oracle is behind
  • block.timestamp - updatedAt > 3 hours - Price too old

When this happens, the ENTIRE protocol freezes. No deposits, mints, or redeems. Safety first!

โš–๏ธ Stablecoin Comparison

How DSC compares to other stablecoins

Feature DSC (Ours) DAI USDT USDC
Type Crypto-backed Crypto-backed Fiat-backed Fiat-backed
Collateral WETH, WBTC ETH, WBTC, etc. USD in bank USD in bank
Decentralized โœ… Fully โœ… Mostly โŒ No โŒ No
Can be frozen โŒ No โŒ No โœ… Yes โœ… Yes
Overcollateralized โœ… 200% โœ… 150%+ 100% (claimed) 100% (audited)
Trust Required Code only Code + DAO Tether company Circle company
Transparency 100% on-chain 100% on-chain Quarterly reports Monthly attestations

Why Choose DSC?

Pros: Fully trustless, censorship-resistant, transparent, overcollateralized

Cons: Capital inefficient (need $2 to get $1), volatile collateral risk

It's a trade-off: Do you trust code or companies?

๐Ÿš€ Deployment Flow

How the contracts get deployed

Deployment Order Matters!

1

Deploy DecentralizedStableCoin

DecentralizedStableCoin dsc = new DecentralizedStableCoin(); // At this point, deployer is the owner
2

Deploy DSCEngine with DSC address

address[] memory tokens = [wethAddress, wbtcAddress]; address[] memory priceFeeds = [ethUsdFeed, btcUsdFeed]; DSCEngine engine = new DSCEngine(tokens, priceFeeds, address(dsc));
3

Transfer DSC ownership to Engine

dsc.transferOwnership(address(engine)); // Now ONLY DSCEngine can mint/burn DSC!

After Deployment

๐Ÿ”—
ETH/USD Feed
โ†’
โš™๏ธ
DSCEngine
OWNER of DSC
โ†’
๐Ÿช™
DSC Token
Owned by Engine

๐Ÿ“– Complete Function Reference

Every function in DSCEngine explained

External Functions (User Calls These)

depositCollateralAndMintDSC()

Combo: Deposit + Mint in one transaction

Gas Efficient: Saves ~21000 gas vs two separate calls

function depositCollateralAndMintDSC( address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDscToMint ) external { depositCollateral(tokenCollateralAddress, amountCollateral); mintDSC(amountDscToMint); }

redeemCollateralForDSC()

Combo: Burn DSC + Withdraw collateral in one transaction

function redeemCollateralForDSC( address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDscToBurn ) external { _burnDSC(amountDscToBurn, msg.sender, msg.sender); _redeemCollateral(tokenCollateralAddress, amountCollateral, msg.sender, msg.sender); _revertIfHealthFactorIsBroken(msg.sender); // Check AFTER both operations }

View Functions (Read-Only)

getAccountInformation()
Get debt + collateral value
getHealthFactor()
Get user's health factor
getUsdValue()
Convert token โ†’ USD
getTokenAmountFromUsd()
Convert USD โ†’ token
getCollateralTokens()
Get allowed tokens list
getCollateralBalanceOfUser()
Get user's collateral amount

Why are these important?

Frontend apps call these to show users their positions. Liquidation bots call these to find liquidatable users!

๐Ÿ”„ User Flow Diagrams

Visual step-by-step flows for every user action

Flow 1: Opening a Position (Deposit + Mint)

๐Ÿ‘ค
User
Has WETH/WBTC
โ†’ approve()
โœ…
Approve
Allow DSCEngine to spend tokens
โ†’ depositCollateralAndMintDSC()
โš™๏ธ
DSCEngine
Validates & processes
โ†’ transferFrom()
๐Ÿ’Ž
WETH/WBTC
Tokens transferred
โš™๏ธ
DSCEngine
Updates state
โ†’ mint()
๐Ÿช™
DSC Token
DSC minted to user
โ†’ Balance Updated
๐ŸŽ‰
Position Open!
User has DSC + collateral locked

๐Ÿ“Š State Changes

s_collateralDeposited[user]
0 โ†’ 2e18
s_DSCMinted[user]
0 โ†’ 1000e18
User DSC Balance
0 โ†’ 1000e18
Health Factor
โˆž โ†’ 2.0 โœ“

Flow 2: Adding Collateral Only

๐Ÿ‘ค
User
Wants to add collateral
โ†’ approve()
โœ…
Approve
Allow spending
โ†’ depositCollateral()
โš™๏ธ
DSCEngine
Updates mapping
โ†’ transferFrom()
๐Ÿ“ˆ
Health โ†‘
HF improves!

Why Add Collateral Without Minting?

Users might want to:

  • ๐Ÿ›ก๏ธ Improve health factor - Make position safer
  • ๐Ÿ“Š Prepare for minting - Deposit now, mint later
  • ๐Ÿ’ฐ Lower liquidation risk - Buffer against price drops

Flow 3: Minting More DSC

๐Ÿ‘ค
User
Has collateral deposited
โ†’ mintDSC()
โš™๏ธ
DSCEngine
Check health factor
โ†’ _healthFactor()
โš–๏ธ
Check
HF >= 1.0 after mint?
โ†“ Yes
๐Ÿช™
Mint Success
DSC minted!
โ†“ No
โŒ
Revert
BreaksHealthFactor

Flow 4: Closing Position (Burn + Redeem)

๐Ÿ‘ค
User
Wants ETH back
โ†’ approve(DSC)
โœ…
Approve DSC
Allow burn
โ†’ redeemCollateralForDSC()
๐Ÿ”ฅ
Burn DSC
Debt reduced
โ†’ _redeemCollateral()
๐Ÿ’Ž
Get Collateral
WETH returned!

๐Ÿ“Š State Changes (Full Exit)

s_collateralDeposited[user]
2e18 โ†’ 0
s_DSCMinted[user]
1000e18 โ†’ 0
DSC Total Supply
1000e18 โ†’ 0
Position Status
Active โ†’ Closed โœ“

Flow 5: Liquidation Flow

๐Ÿ˜ฐ
User A
HF < 1.0 (Liquidatable)
โ‡„ Monitored by
๐Ÿค 
Liquidator
Bot or user
โ†’ liquidate()
โš™๏ธ
DSCEngine
Verify & execute
๐Ÿ’ฐ
Pays DSC
Covers debt
โ†’ +10% bonus
๐ŸŽ
Receives
Collateral + bonus
โ†’ debt reduced
๐Ÿ˜Š
User A
HF improved!

๐Ÿ’ฐ Liquidation Math Visualization

Debt Covered
$500 DSC
โ†’
Base Collateral
0.333 ETH
+
10% Bonus
0.033 ETH
=
Total Received
0.366 ETH
๐Ÿ’ต Profit: $50 (10% of $500)

๐Ÿ—๏ธ Code Architecture Deep Dive

Understanding the smart contract structure and design patterns

Contract Inheritance Diagram

๐Ÿ“ฆ
OpenZeppelin
External Library
โ†“
๐Ÿ”ท
ERC20Burnable
Base Contract
๐Ÿ”ท
Ownable
Base Contract
๐Ÿ”ท
ReentrancyGuard
Base Contract
โ†“
๐Ÿช™
DecentralizedStableCoin
DSC Token (~60 lines)
โš™๏ธ
DSCEngine
Core Engine (~700 lines)

Why This Architecture?

Separation of Concerns: The token (DSC) is simple - it just moves value. The engine (DSCEngine) is smart - it handles all the complex logic.

This is like a bank: The vault (DSC) holds the money, but the bank manager (DSCEngine) decides who can deposit/withdraw.

DSCEngine State Variables Map

๐Ÿ”’ Immutable (Set Once)

i_dsc DSC token contract address

๐Ÿ“Š Mappings (User Data)

s_collateralDeposited user โ†’ token โ†’ amount
s_DSCMinted user โ†’ debt amount
s_priceFeeds token โ†’ price feed

๐Ÿ“‹ Arrays (Configuration)

s_collateralTokens List of allowed tokens [WETH, WBTC]

Function Categories

๐Ÿ“ฅ Deposit Functions
depositCollateral() Deposit single token
depositCollateralAndMintDSC() Deposit + mint combo
๐Ÿช™ Mint Functions
mintDSC() Mint against collateral
๐Ÿ“ค Redeem Functions
redeemCollateral() Withdraw collateral
redeemCollateralForDSC() Burn + withdraw combo
๐Ÿ”ฅ Burn Functions
burnDSC() Reduce debt
โšก Liquidation
liquidate() Liquidate unhealthy user
๐Ÿ‘๏ธ View Functions
getAccountInformation() Debt + collateral
getHealthFactor() User's health score
getUSDValue() Token โ†’ USD
getTokenAmountFromUsd() USD โ†’ token

Security Patterns Used

๐Ÿ›ก๏ธ

ReentrancyGuard

Prevents reentrancy attacks on external calls

nonReentrant modifier
โœ…

Checks-Effects-Interactions

State changes before external calls

Update mapping โ†’ Transfer
๐Ÿ”’

Access Control

Only DSCEngine can mint/burn DSC

onlyOwner modifier
๐Ÿ“Š

Health Factor Checks

All operations validate position health

_revertIfHealthFactorIsBroken()
โฑ๏ธ

Oracle Staleness

Rejects stale price data (>3 hours)

OracleLib.check()
๐Ÿšซ

Input Validation

Zero address, zero amount checks

moreThanZero modifier

Modifier Chain Visualization

depositCollateral()
โ†’
โœ“ moreThanZero
โ†’
โœ“ isZeroAddress
โ†’
โœ“ isAllowedToken
โ†’
โœ“ nonReentrant
โ†’
Execute
// Each modifier acts as a gatekeeper modifier moreThanZero(uint256 _amount) { if (_amount <= 0) { revert DSCEngine__AmountMustBeMoreThanZero(); } _; // Continue to next modifier or function body }

๐Ÿง  Core Concepts Explained

Deep dives into the fundamental mechanisms

Overcollateralization Visualized

100% Collateralization (DANGEROUS)

$1000 ETH
$1000 DSC
โŒ ETH drops 10% โ†’ Position underwater!

150% Collateralization (DAI Standard)

$1500 ETH
$1000 DSC
โš ๏ธ ETH can drop 33% before liquidation

200% Collateralization (DSC Standard)

$2000 ETH
$1000 DSC
โœ… ETH can drop 50% before liquidation

The Safety Buffer

Think of overcollateralization like a down payment on a house:

  • 0% down (100% collateral): If house value drops, bank loses money
  • 20% down (125% collateral): Small buffer for price drops
  • 50% down (200% collateral): Huge buffer - very safe for lender

DSC requires 200% because crypto is volatile!

Health Factor Zones

๐ŸŸข Safe Zone HF โ‰ฅ 1.5

Your position is very healthy

  • โœ… Can mint more DSC
  • โœ… Can withdraw collateral
  • โœ… Safe from liquidation
๐ŸŸก Caution Zone 1.0 โ‰ค HF < 1.5

Your position is at risk

  • โš ๏ธ Cannot mint more DSC
  • โš ๏ธ Cannot withdraw collateral
  • โœ… Still safe from liquidation
๐Ÿ”ด Liquidation Zone HF < 1.0

Your position can be liquidated!

  • โŒ Cannot mint or withdraw
  • โŒ Anyone can liquidate you
  • ๐Ÿ’ก Add collateral or burn DSC to escape
Danger
0 - 1.0
Caution
1.0 - 1.5
Safe
1.5+
๐Ÿ‘ค You are here (HF: 1.5)

Liquidation Incentive Mechanics

๐Ÿ‘€

1. Spot Opportunity

Liquidator finds user with HF < 1.0

โ†’
๐Ÿ’ฐ

2. Pay Debt

Pay $500 DSC to cover user's debt

โ†’
๐ŸŽ

3. Get Bonus

Receive $550 worth of collateral

โ†’
๐Ÿ“ˆ

4. Profit!

Instant $50 profit (10%)

Liquidation Profit Table

Debt Covered 10% Bonus Your Profit
$100 DSC $10 $10
$500 DSC $50 $50
$1,000 DSC $100 $100
$5,000 DSC $500 $500

Price Oracle Flow

โ›“๏ธ
Ethereum Network
โ†“
๐Ÿ”—
Chainlink Nodes
Multiple independent nodes
โ†“
๐Ÿ“Š
Aggregator Contract
Aggregates prices from nodes
โ†“
โœ…
OracleLib Check
Fresh? (< 3 hours)
โ†“
โš™๏ธ
DSCEngine
Uses price for calculations

Why Chainlink?

Chainlink is a decentralized oracle network:

  • ๐Ÿ”ข Multiple data sources - Not reliant on one exchange
  • ๐ŸŒ Decentralized nodes - No single point of failure
  • ๐Ÿ”’ On-chain verification - Prices are cryptographically signed
  • โฑ๏ธ Regular updates - Prices refresh every ~1 hour for ETH/USD

Token Economics

๐Ÿ–จ๏ธ

Minting

DSC is created when users deposit collateral

Collateral ร— 50% = Max Mint
โ†”๏ธ
๐Ÿ”„

Circulation

DSC trades at $1 in the market

Total Supply = ฮฃ All Minted
โ†”๏ธ
๐Ÿ”ฅ

Burning

DSC is destroyed when debt is repaid

Burn DSC โ†’ Unlock Collateral

Supply Dynamics

๐Ÿ“ˆ Supply Expansion

When ETH price rises, users can mint more DSC

More collateral value โ†’ More DSC supply

๐Ÿ“‰ Supply Contraction

When ETH price drops, users burn DSC to avoid liquidation

Less collateral value โ†’ Less DSC supply

โš–๏ธ Equilibrium

DSC maintains $1 peg through overcollateralization

Always backed by more collateral than DSC issued

๐Ÿ“‹ Quick Reference

Everything at a glance

Key Formulas

Formula Calculation Example
Health Factor (Collateral ร— 50%) รท Debt ($2000 ร— 50%) รท $500 = 2.0
Max Mintable DSC Collateral Value ร— 50% $2000 ร— 50% = $1000 DSC
Liquidation Bonus Debt Covered ร— 10% $500 ร— 10% = $50
USD to Token USD Amount รท Token Price $500 รท $1500 = 0.333 ETH

Contract Addresses (Testnet)

Deploy on Sepolia or Anvil local network

// Run deployment forge script script/DeployDSCEngine.s.sol --rpc-url $RPC_URL --broadcast // Run tests forge test -vvv // Run invariant tests forge test --mt invariant -vvv