7.1 Decentralized Lending: Aave and Compound
The $20 Billion Revolution in Credit
On March 12, 2020—"Black Thursday"—cryptocurrency markets crashed 50% in a single day. ETH plummeted from $195 to $90 in hours. The stress test was catastrophic:
Traditional finance response:
- Banks closed lending desks
- Credit froze completely
- Counterparty risk paralyzed markets
- Manual intervention everywhere
- Recovery: days to weeks
DeFi lending response:
- Protocols operated autonomously 24/7
- $88 million in liquidations executed automatically
- No human intervention required
- Full transparency of all positions
- Recovery: hours
But there was a catch: Ethereum network congestion caused $8 million in undercollateralized liquidations when oracle prices couldn't update fast enough. Some liquidators paid $500+ in gas for a single transaction. Zero-bid liquidations allowed attackers to seize $8M in collateral for free.
This stress test revealed both the promise and peril of decentralized lending:
The promise:
- $20B+ total value locked (Aave + Compound alone, 2024)
- No credit checks, no KYC, no discrimination
- Permissionless access to capital for anyone with collateral
- Complete transparency of all positions and risks
- Composability enables capital efficiency impossible in TradFi
The peril:
- Overcollateralization required (150-300% typical)
- Liquidation cascades can spiral out of control
- Oracle dependency creates single point of failure
- Flash loan attacks exploit atomicity
- Network congestion can cause insolvencies
Yet despite these risks, decentralized lending has become one of DeFi's biggest success stories:
Market size (2024):
Aave: $10B+ TVL, $6B borrowed
Compound: $3B TVL, $1.8B borrowed
MakerDAO: $5B TVL (DAI supply)
Others: $2B+ TVL
Total: $20B+ in active lending
Annual interest paid: $800M+
Growth trajectory:
2018: $50M TVL
2019: $500M TVL
2020: $2B TVL
2021: $50B TVL (peak)
2022: $15B TVL (bear market)
2024: $20B+ TVL (recovery)
This lesson explores how decentralized lending works—the mathematics, mechanics, and risks of trustless credit markets where smart contracts replace loan officers and algorithms replace credit scores.
How Decentralized Lending Works
The Core Problem: Trustless Credit
Traditional lending requires trust:
Bank → Lend $100k → Borrower
↓
Borrower defaults?
↓
Bank sues, seizes assets
↓
Legal system enforces
Requirements:
- Identity verification (KYC)
- Credit history checking
- Legal agreements
- Court system for enforcement
- Physical asset seizure capability
On blockchain, none of this exists:
- Pseudonymous addresses (no identity)
- No credit scores
- Smart contracts can't sue
- No legal enforcement
- Can't seize off-chain assets
The solution: Overcollateralization
Borrower → Deposit $150k ETH → Protocol
↓
Borrow $100k USDC
↓
If collateral value < threshold
↓
Automatic liquidation
Key insight: Replace legal enforcement with economic incentives and automatic execution.
Overcollateralization: The Mathematics
Basic collateral ratio:
Collateral Ratio = Collateral Value / Debt Value
Example:
Collateral: 1 ETH at $2,000 = $2,000
Debt: 1,000 USDC = $1,000
Collateral Ratio: 200%
Typical ratios by asset:
Asset Min Collateral Ratio Liquidation Threshold
────────────────────────────────────────────────────────────
ETH 133% 80% LTV
WBTC 133% 75% LTV
USDC 110% 90% LTV (stablecoin)
DAI 110% 90% LTV (stablecoin)
LINK 175% 70% LTV
UNI 200% 65% LTV
Random altcoin 300%+ 50% LTV
LTV (Loan-to-Value):
LTV = Debt / Collateral = 1 / Collateral_Ratio
80% LTV = 125% collateral ratio
75% LTV = 133% collateral ratio
50% LTV = 200% collateral ratio
Why different ratios?
Risk factors:
- Volatility: Higher volatility → higher collateral requirement
- Liquidity: Lower liquidity → higher requirement (harder to liquidate)
- Oracle reliability: Less reliable oracle → higher requirement
- Market cap: Smaller market cap → higher requirement
Example calculation:
User wants to borrow maximum USDC against ETH
ETH price: $2,000
ETH collateral: 10 ETH
Liquidation threshold: 80% LTV
Maximum borrow:
Max Debt = Collateral Value × LTV
= (10 ETH × $2,000) × 0.80
= $20,000 × 0.80
= $16,000 USDC
At borrowing:
Collateral: $20,000
Debt: $16,000
Ratio: 125% (1/0.80)
Health Factor: 1.25
Health Factor: The Key Metric
Most protocols use a health factor to represent position safety:
Health Factor = (Collateral Value × Liquidation Threshold) / Debt Value
Interpretation:
HF > 1.0: Safe position
HF = 1.0: At liquidation threshold
HF < 1.0: Liquidatable
Example:
Collateral: 10 ETH at $2,000 = $20,000
Liquidation threshold: 80%
Debt: $12,000 USDC
HF = ($20,000 × 0.80) / $12,000
= $16,000 / $12,000
= 1.33
Safe: Can borrow up to $4,000 more before HF = 1.0
What happens as price moves:
ETH price drops to $1,800:
Collateral: 10 ETH × $1,800 = $18,000
Debt: $12,000 (unchanged)
HF = ($18,000 × 0.80) / $12,000
= $14,400 / $12,000
= 1.20
Still safe, but less margin
ETH price drops to $1,500:
Collateral: 10 ETH × $1,500 = $15,000
Debt: $12,000
HF = ($15,000 × 0.80) / $12,000
= $12,000 / $12,000
= 1.00
Liquidatable! Position can be liquidated.
Pool-Based Lending Architecture
Two main models:
1. Pooled liquidity (Compound, Aave)
Many lenders → Pool → Many borrowers
2. Peer-to-peer (pre-DeFi, less common now)
Individual lender → Match → Individual borrower
We'll focus on pooled model as it's dominant.
Pooled lending mechanics:
LENDING POOL
Lenders: Borrowers:
User A deposits 100 ETH ─────→ ←───── User X borrows 50 ETH
User B deposits 50 ETH ─────→ ←───── User Y borrows 30 ETH
User C deposits 200 ETH ─────→
Pool State:
Total Supply: 350 ETH
Total Borrowed: 80 ETH
Available: 270 ETH
Utilization: 22.9%
Key properties:
- Instant liquidity: Lenders can withdraw anytime (if liquidity available)
- Flexible borrowing: Borrow any amount up to available liquidity
- Dynamic rates: Interest rates adjust based on utilization
- Fungible positions: All lenders earn same rate
Interest Rate Models
Interest rates are algorithmic, not set by humans.
Utilization rate:
U = Borrowed / (Supplied)
Example:
Supplied: 1,000 ETH
Borrowed: 300 ETH
U = 300/1,000 = 0.30 = 30%
Basic rate model (Compound V2):
If U ≤ U_optimal:
Borrow Rate = Base Rate + (U / U_optimal) × Rate_1
If U > U_optimal:
Excess = (U - U_optimal) / (1 - U_optimal)
Borrow Rate = Base Rate + Rate_1 + Excess × Rate_2
Where:
U_optimal = target utilization (e.g., 80%)
Base Rate = minimum rate (e.g., 0%)
Rate_1 = slope up to optimal (e.g., 4%)
Rate_2 = slope above optimal (e.g., 75%)
Graphically:
Borrow Rate (%)
100│ ╱
│ ╱
80│ ╱
│ ╱
60│ ╱
│ ╱
40│ ╱
│ ╱──────────────── Rate_2 slope
20│ ╱
│╱ Rate_1 slope
0├────────┬────────────────────> Utilization (%)
0 80 100
U_optimal
Example calculation:
Parameters (ETH market):
Base Rate: 0%
Rate_1: 4%
Rate_2: 75%
U_optimal: 80%
Scenario 1: U = 40%
U < U_optimal, so:
Borrow Rate = 0% + (40% / 80%) × 4%
= 0% + 0.5 × 4%
= 2%
Scenario 2: U = 90%
U > U_optimal, so:
Excess = (90% - 80%) / (100% - 80%)
= 10% / 20%
= 0.5
Borrow Rate = 0% + 4% + 0.5 × 75%
= 0% + 4% + 37.5%
= 41.5%
High utilization → High rate → Incentivizes repayment/deposits
Why this curve shape?
Low utilization (< 80%):
- Plenty of liquidity available
- Gentle rate increase
- Encourage borrowing
High utilization (> 80%):
- Liquidity getting scarce
- Steep rate increase
- Discourage borrowing, encourage repayment
- Protect lenders' ability to withdraw
Supply (lending) rate:
Supply Rate = Borrow Rate × Utilization × (1 - Reserve Factor)
Where Reserve Factor = protocol fee (e.g., 10%)
Example:
Borrow Rate: 10%
Utilization: 60%
Reserve Factor: 10%
Supply Rate = 10% × 60% × (1 - 10%)
= 10% × 60% × 90%
= 5.4%
Interpretation:
Borrowers pay 10%
Lenders earn 5.4%
Protocol keeps 0.6% (10% of 6%)
Compound's cToken Mechanism
How Compound represents deposits:
Instead of tracking balances, Compound issues cTokens:
User deposits 100 ETH → Receives 500 cETH
cToken exchange rate: 1 cETH = 0.2 ETH initially
Exchange rate appreciates over time:
Time 0: 1 cETH = 0.2 ETH
Time 1: Interest accrues, 1 cETH = 0.21 ETH
Time 2: More interest, 1 cETH = 0.22 ETH
User's 500 cETH now worth:
500 × 0.22 = 110 ETH
Profit: 10 ETH interest earned
Exchange rate formula:
exchange_rate = (total_cash + total_borrows - total_reserves) / total_supply_cTokens
Where:
total_cash = ETH in contract
total_borrows = ETH borrowed out
total_reserves = protocol reserves
total_supply_cTokens = total cETH issued
Example:
Initial state:
100 ETH deposited
0 ETH borrowed
500 cETH issued
exchange_rate = 100 / 500 = 0.2
After 1 year:
100 ETH cash
50 ETH borrowed (earning 10% = 5 ETH interest)
0 reserves
505 cETH issued (500 original + 5 new from borrower interest)
total_value = 100 + 50 = 150 ETH
exchange_rate = 150 / 500 = 0.3
Each cETH now worth 0.3 ETH (was 0.2)
50% return to lenders!
Why this design?
Benefits:
- Gas efficient (don't update every balance on every interest accrual)
- Composable (cTokens are ERC-20, can be used elsewhere)
- Simple accounting (exchange rate tracks aggregate interest)
Aave's aToken Mechanism
Different approach: Balance rebasing
User deposits 100 ETH → Receives 100 aETH
aToken balance increases over time:
Time 0: 100 aETH
Time 1: 100.5 aETH (interest accrued)
Time 2: 101.0 aETH (more interest)
Always redeemable 1:1 for underlying
Interest accrual:
Continuously compounding interest:
balance(t) = balance(0) × e^(rate × t)
Implemented as:
balanceOf(user) = principal × liquidity_index / initial_index
Where liquidity_index grows with interest
Comparison:
Compound cToken:
- Exchange rate increases
- Balance stays constant
- Need to track exchange rate
Aave aToken:
- Balance increases
- Exchange rate always 1:1
- Need to track liquidity index
Both achieve same economic result!
Borrowing Mechanics
To borrow on Compound/Aave:
// 1. Deposit collateral
function supply(address asset, uint256 amount) external {
// Transfer tokens to protocol
asset.transferFrom(msg.sender, address(this), amount);
// Issue cTokens/aTokens
_mint(msg.sender, amount / exchangeRate);
// Enable as collateral
_enableCollateral(msg.sender, asset);
}
// 2. Borrow against collateral
function borrow(address asset, uint256 amount) external {
// Check collateral sufficiency
require(_healthFactor(msg.sender) > 1.0, "Insufficient collateral");
// Check liquidity
require(available[asset] >= amount, "Not enough liquidity");
// Record debt
debt[msg.sender][asset] += amount;
// Transfer borrowed tokens
asset.transfer(msg.sender, amount);
// Update interest rate
_updateInterestRate(asset);
}
Health factor calculation:
function _healthFactor(address user) internal view returns (uint256) {
uint256 totalCollateralETH = 0;
uint256 totalDebtETH = 0;
uint256 liquidationThreshold = 0;
// Sum all collateral assets
for (address asset : userAssets[user]) {
uint256 balance = balanceOf[user][asset];
uint256 price = oracle.getPrice(asset);
uint256 ltv = ltvs[asset];
totalCollateralETH += balance × price;
liquidationThreshold += balance × price × ltv;
}
// Sum all debt assets
for (address asset : userDebts[user]) {
uint256 debt = debtOf[user][asset];
uint256 price = oracle.getPrice(asset);
totalDebtETH += debt × price;
}
if (totalDebtETH == 0) return type(uint256).max;
return (liquidationThreshold / totalDebtETH);
}
Multi-Collateral and Cross-Borrowing
Users can deposit multiple assets:
User deposits:
- 10 ETH at $2,000 = $20,000
- 1 WBTC at $40,000 = $40,000
- 1,000 LINK at $15 = $15,000
Total collateral value: $75,000
But different liquidation thresholds:
- ETH: 80% LTV → $16,000 borrowing power
- WBTC: 75% LTV → $30,000 borrowing power
- LINK: 70% LTV → $10,500 borrowing power
Total borrowing power: $56,500
Can borrow any combination:
- $56,500 USDC, or
- $28,000 USDC + $14,000 DAI, or
- Mix of any assets
Weighted average LTV:
Borrowing Power = Σ(collateral_i × price_i × ltv_i)
Example above:
= ($20,000 × 0.80) + ($40,000 × 0.75) + ($15,000 × 0.70)
= $16,000 + $30,000 + $10,500
= $56,500
Isolated vs Cross-Margined Risk
Cross-margined (Compound, Aave V2):
All collateral backs all debt
One underwater position can liquidate entire account
User:
Collateral: 10 ETH, 1 WBTC, 1000 LINK
Debt: $50k USDC
If ETH crashes and total HF < 1.0:
Liquidator can seize ANY collateral to repay debt
Isolated (Aave V3):
Collateral and debt are isolated per position
Each position has independent health factor
User Position 1:
Collateral: 10 ETH
Debt: $15k USDC
HF: 1.5
User Position 2:
Collateral: 1 WBTC
Debt: $20k DAI
HF: 2.0
If Position 1 goes underwater:
Only Position 1 gets liquidated
Position 2 is safe
Trade-offs:
Cross-margined:
+ More capital efficient (same collateral backs more debt)
+ Simpler UX
- Riskier (cascade risk)
- Less control
Isolated:
+ Safer (compartmentalized risk)
+ More control
- Less capital efficient
- More complex UX
Liquidations: The Safety Mechanism
Why Liquidations Exist
The solvency problem:
Without liquidations:
Time 0:
Collateral: 10 ETH at $2,000 = $20,000
Debt: $15,000 USDC
Health Factor: 1.33 ✓
Time 1: ETH drops to $1,000
Collateral: 10 ETH at $1,000 = $10,000
Debt: $15,000 USDC
Health Factor: 0.67 ✗
User has no incentive to repay (debt > collateral)
Protocol has bad debt: -$5,000
Lenders can't withdraw (insufficient funds)
Protocol becomes insolvent!
Solution: Liquidate before insolvency
Liquidation threshold: 80% LTV (HF = 1.0)
When HF ≤ 1.0:
Anyone can liquidate position
Seize collateral
Repay debt
Earn liquidation bonus
This ensures:
- Protocol stays solvent
- Lenders can withdraw
- System maintains 1:1 backing
Liquidation Mechanics
Basic liquidation flow:
1. Liquidator identifies underwater position
(Health Factor < 1.0)
2. Liquidator calls liquidate() function:
- Repays portion of borrower's debt
- Receives collateral + bonus
3. Borrower's position:
- Debt reduced
- Collateral reduced
- Health factor improved
4. If still underwater after partial liquidation:
- Can be liquidated again
- Continues until HF > 1.0 or fully liquidated
Compound liquidation interface:
function liquidateBorrow(
address borrower,
uint256 repayAmount,
address collateralAsset
) external returns (uint256) {
require(
_healthFactor(borrower) < 1.0,
"Cannot liquidate healthy position"
);
// Max liquidation amount (50% of debt)
uint256 maxClose = borrowedAmount[borrower] / 2;
uint256 actualRepay = min(repayAmount, maxClose);
// Calculate collateral to seize
uint256 collateralPrice = oracle.getPrice(collateralAsset);
uint256 borrowPrice = oracle.getPrice(borrowAsset);
uint256 liquidationIncentive = 1.08; // 8% bonus
uint256 collateralSeized =
actualRepay × borrowPrice × liquidationIncentive / collateralPrice;
// Transfer repayment from liquidator
borrowAsset.transferFrom(msg.sender, address(this), actualRepay);
// Transfer collateral to liquidator
collateralAsset.transfer(msg.sender, collateralSeized);
// Update borrower's position
borrowedAmount[borrower] -= actualRepay;
collateralAmount[borrower] -= collateralSeized;
return collateralSeized;
}
Numerical example:
Borrower position:
Collateral: 10 ETH at $1,500 = $15,000
Debt: $13,000 USDC
Liquidation threshold: 80%
Health Factor: ($15,000 × 0.80) / $13,000 = 0.923 < 1.0 ✗
Liquidation:
Max liquidation: 50% of debt = $6,500
Liquidator repays: $6,500 USDC
Collateral seized:
= $6,500 × 1.08 / $1,500
= $7,020 / $1,500
= 4.68 ETH
Liquidator profit:
Collateral received: 4.68 ETH = $7,020
Debt repaid: $6,500
Profit: $520 (8% liquidation bonus)
After liquidation:
Collateral: 5.32 ETH at $1,500 = $7,980
Debt: $6,500
HF: ($7,980 × 0.80) / $6,500 = 0.982
Still underwater! Can be liquidated again.
Liquidation Parameters
Key parameters:
1. Close Factor:
Maximum % of debt that can be repaid in one liquidation
Compound: 50%
Aave: Up to 50% (depends on HF)
Why not 100%?
- Gives borrower chance to save position
- Prevents unnecessarily large liquidations
- Distributes liquidation opportunities
Exception: If HF very low (<0.95), some protocols allow 100%
2. Liquidation Bonus/Incentive:
Extra collateral liquidator receives
Typical range: 5-15%
ETH: 5-8% (liquid, low risk)
WBTC: 6-10%
Altcoins: 10-15% (higher risk)
Trade-off:
Higher bonus → More liquidator incentive → Safer protocol
Higher bonus → Worse for borrowers → Higher borrowing costs
3. Liquidation Threshold:
LTV at which liquidation becomes possible
Conservative assets (ETH, WBTC): 75-80%
Stablecoins: 85-90%
Risky assets: 60-70%
Must be lower than max LTV:
Max LTV: 75%
Liquidation threshold: 80%
(User can borrow up to 75%, liquidated at 80%)
Liquidation Bots and MEV
Professional liquidators:
class LiquidationBot:
def __init__(self):
self.protocols = [Compound, Aave, MakerDAO]
self.flash_loan_providers = [Aave, dYdX, Uniswap]
def monitor_positions(self):
while True:
for protocol in self.protocols:
# Get all positions
positions = protocol.get_all_positions()
# Check health factors
for position in positions:
if position.health_factor < 1.0:
self.attempt_liquidation(position)
def attempt_liquidation(self, position):
# Calculate profit
debt = position.debt_value
collateral = position.collateral_value
bonus = protocol.liquidation_bonus
profit = debt × bonus - gas_cost
if profit > MIN_PROFIT:
# Get flash loan if needed
if self.balance < debt:
flash_loan_amount = debt - self.balance
self.execute_flash_loan_liquidation(
position,
flash_loan_amount
)
else:
self.execute_liquidation(position)
def execute_flash_loan_liquidation(self, position, amount):
# 1. Flash loan
# 2. Liquidate position
# 3. Sell seized collateral
# 4. Repay flash loan
# 5. Keep profit
tx = build_atomic_transaction([
FlashLoan(amount),
Liquidate(position),
SwapCollateral(collateral_asset, debt_asset),
RepayFlashLoan(amount + fee),
])
submit_transaction(tx)
Competition dynamics:
Pre-Black Thursday (2020):
- Few sophisticated liquidators
- Generous profits (15-30%)
- Slow execution
Post-Black Thursday:
- Hundreds of bots competing
- Tight profits (0.5-2%)
- MEV strategies
- Flash loan liquidations
Modern (2023-2024):
- Integrated with Flashbots
- Private transaction submission
- Millisecond latency
- Professional operators only
Liquidation as MEV:
Annual liquidation volume: ~$5B
Average liquidation bonus: 5%
Total liquidator profit: ~$250M/year
Distribution:
- Top 10 liquidators: ~60% of profits
- Next 100 liquidators: ~30%
- Long tail: ~10%
MEV captured: Significant
Often combined with:
- Arbitrage (sell collateral at best price)
- Backrunning (liquidate after price oracle update)
- Frontrunning (liquidate before others)
The Black Thursday Crisis (March 12-13, 2020)
Timeline of disaster:
March 12, 2020
00:00 UTC: ETH at $195
06:00 UTC: COVID fears escalate, markets crash
12:00 UTC: ETH at $150 (-23%)
18:00 UTC: ETH at $110 (-43%)
24:00 UTC: ETH at $90 (-54%)
Impact on MakerDAO:
- $88M in liquidations triggered
- Ethereum network congested (gas prices 100+ gwei)
- Oracle price feeds delayed
- Some liquidators paid $500+ gas per tx
Zero-bid auction exploit:
Normal auction:
Liquidator A bids: $950 for $1,000 collateral
Liquidator B bids: $970
Liquidator C bids: $980
Winner pays $980, protocol gets $980
Black Thursday:
Network congestion → Only one liquidator's tx confirmed
Liquidator bids: $0 for $1,000 collateral
No competing bids (all stuck in mempool)
Liquidator wins with $0 bid!
Result:
- $8M in collateral seized for free
- $5.3M bad debt created
- MakerDAO system deeply underwater
Lessons learned:
1. Oracle latency matters
→ Need faster price updates during volatility
2. Network congestion risk
→ Need better liquidation mechanisms
→ Consider L2s or faster chains
3. Auction design matters
→ Minimum bid requirements added
→ Time delays between bids
4. Need circuit breakers
→ Pause mechanism during extreme events
→ Gradual liquidations instead of all-at-once
MakerDAO's response:
1. System Stabilizer Module
- Mints MKR tokens to cover bad debt
- Dilutes MKR holders
- $5.3M covered by minting MKR
2. Auction redesign
- Minimum bid requirements
- Longer auction periods
- Better incentive structure
3. Oracle improvements
- Faster updates
- More sources
- Better aggregation
4. Emergency shutdown tested
- Can pause system if needed
- Gradual instead of instant liquidations
Continued in next section...
Would you like me to continue with:
- Interest rate models and equilibrium
- Risk management and protocol design
- Flash loan attacks on lending
- Comparison of major protocols (Compound, Aave, Maker)
- Future directions