DeFi Protocols

Decentralized finance - exchanges, lending, and financial primitives


DeFi Protocols

Decentralized Finance (DeFi) is a category of applications that recreate traditional financial services using smart contracts. DeFi protocols are permissionless, composable, and operate 24/7 without intermediaries.

This chapter covers the major DeFi primitives and how they work at the smart contract level.

What Makes DeFi Different

Traditional finance relies on trusted intermediaries (banks, brokers, exchanges). DeFi replaces these with smart contracts:

TraditionalDeFi
Bank accountsSelf-custody wallets
Stock exchangesAMM DEXes
Loans from banksLending protocols
Market makersLiquidity providers
Clearing housesSmart contracts
Business hours24/7/365
KYC requiredPermissionless

Decentralized Exchanges (DEXes)

Order Book vs AMM

Order Book DEXes (like traditional exchanges):

  • Buyers and sellers post orders
  • Orders match when prices cross
  • Examples: dYdX, Serum

Automated Market Makers (AMMs):

  • Use mathematical formulas for pricing
  • Liquidity providers deposit token pairs
  • Examples: Uniswap, Sushiswap, Curve

How AMMs Work

The constant product formula (Uniswap V2):

x * y = k

Where:

  • x = reserve of token A
  • y = reserve of token B
  • k = constant (maintained after trades)
// Simplified swap calculation
function getAmountOut(
    uint256 amountIn,
    uint256 reserveIn,
    uint256 reserveOut
) public pure returns (uint256) {
    uint256 amountInWithFee = amountIn * 997; // 0.3% fee
    uint256 numerator = amountInWithFee * reserveOut;
    uint256 denominator = (reserveIn * 1000) + amountInWithFee;
    return numerator / denominator;
}

Liquidity Provision

Liquidity providers (LPs) deposit equal value of two tokens:

function addLiquidity(
    address tokenA,
    address tokenB,
    uint256 amountA,
    uint256 amountB
) external returns (uint256 liquidity) {
    // Transfer tokens to pool
    IERC20(tokenA).transferFrom(msg.sender, address(this), amountA);
    IERC20(tokenB).transferFrom(msg.sender, address(this), amountB);
 
    // Mint LP tokens
    liquidity = sqrt(amountA * amountB);
    _mint(msg.sender, liquidity);
}

LPs earn trading fees but face impermanent loss when prices change.

Impermanent Loss

When you provide liquidity and prices change, you would have been better off just holding:

Price ChangeImpermanent Loss
1.25x0.6%
1.5x2.0%
2x5.7%
3x13.4%
5x25.5%

Concentrated Liquidity (Uniswap V3)

LPs can concentrate liquidity in price ranges for higher capital efficiency:

// V2: Liquidity spread across 0 to ∞
// V3: Liquidity only in [priceLower, priceUpper]
 
function mint(
    address recipient,
    int24 tickLower,
    int24 tickUpper,
    uint128 amount
) external returns (uint256 amount0, uint256 amount1);

Lending Protocols

How Lending Works

Users deposit assets to earn interest. Borrowers provide collateral and pay interest:

┌─────────────┐      deposits      ┌─────────────────┐
│   Lenders   │ ─────────────────▶ │  Lending Pool   │
│             │ ◀───────────────── │  (Smart Contract)│
└─────────────┘      interest      │                 │
                                   │  Collateral: $150│
┌─────────────┐      collateral    │  Borrowed: $100 │
│  Borrowers  │ ─────────────────▶ │                 │
│             │ ◀───────────────── │                 │
└─────────────┘     borrowed       └─────────────────┘

Interest Rate Models

Rates adjust based on utilization:

// Simplified interest rate model
function getBorrowRate(uint256 cash, uint256 borrows) public pure returns (uint256) {
    uint256 utilization = borrows * 1e18 / (cash + borrows);
 
    // Low utilization: low rates
    // High utilization: high rates (incentivize deposits)
    if (utilization <= 0.8e18) {
        return utilization * 5 / 100; // 0-4% base rate
    } else {
        // Steep increase above 80% utilization
        return 0.04e18 + (utilization - 0.8e18) * 75 / 100;
    }
}

Collateralization

Most DeFi lending is over-collateralized:

// Example: 150% collateral ratio required
function borrow(uint256 amount) external {
    uint256 collateralValue = getCollateralValue(msg.sender);
    uint256 borrowValue = getBorrowValue(msg.sender) + amount;
 
    // Collateral must be > 150% of borrows
    require(collateralValue >= borrowValue * 150 / 100, "Undercollateralized");
 
    // Process borrow
    borrows[msg.sender] += amount;
    IERC20(borrowToken).transfer(msg.sender, amount);
}

Liquidations

When collateral falls below threshold, liquidators can repay debt and claim collateral at a discount:

function liquidate(address borrower, uint256 repayAmount) external {
    require(isLiquidatable(borrower), "Not liquidatable");
 
    // Liquidator repays part of debt
    IERC20(borrowToken).transferFrom(msg.sender, address(this), repayAmount);
 
    // Liquidator receives collateral at discount (e.g., 5%)
    uint256 collateralAmount = repayAmount * 105 / 100 / collateralPrice;
    IERC20(collateralToken).transfer(msg.sender, collateralAmount);
 
    borrows[borrower] -= repayAmount;
}

Stablecoins

Types of Stablecoins

Fiat-backed (USDC, USDT):

  • 1:1 backed by USD in bank accounts
  • Centralized, can freeze accounts

Crypto-backed (DAI, LUSD):

  • Over-collateralized by crypto
  • Decentralized, censorship-resistant

Algorithmic (FRAX, RAI):

  • Use algorithms to maintain peg
  • Various stability mechanisms

MakerDAO and DAI

DAI is created by depositing collateral into "Vaults":

// Simplified Vault (CDP) logic
function openVault(uint256 collateralAmount, uint256 daiAmount) external {
    // Deposit collateral
    collateral[msg.sender] += collateralAmount;
    IERC20(collateralToken).transferFrom(msg.sender, address(this), collateralAmount);
 
    // Mint DAI (must maintain collateral ratio)
    uint256 collateralValue = collateralAmount * getCollateralPrice();
    require(collateralValue >= daiAmount * 150 / 100, "Below min ratio");
 
    debt[msg.sender] += daiAmount;
    daiToken.mint(msg.sender, daiAmount);
}

Flash Loans

Flash loans allow borrowing without collateral — as long as you repay within the same transaction:

interface IFlashLoanReceiver {
    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    ) external returns (bool);
}
 
function flashLoan(
    address receiver,
    address asset,
    uint256 amount,
    bytes calldata params
) external {
    uint256 balanceBefore = IERC20(asset).balanceOf(address(this));
 
    // Send funds to receiver
    IERC20(asset).transfer(receiver, amount);
 
    // Receiver executes arbitrary logic
    IFlashLoanReceiver(receiver).executeOperation(
        asset, amount, 0, msg.sender, params
    );
 
    // Must repay (with premium)
    require(
        IERC20(asset).balanceOf(address(this)) >= balanceBefore,
        "Flash loan not repaid"
    );
}

Flash Loan Use Cases

  • Arbitrage — Profit from price differences across DEXes
  • Collateral swaps — Change vault collateral without repaying
  • Liquidations — Fund large liquidations without capital
  • Self-liquidation — Close underwater positions efficiently

Yield Aggregators

Yield aggregators automatically compound rewards:

function harvest() external {
    // Claim rewards
    uint256 rewards = farmingContract.pendingRewards(address(this));
    farmingContract.harvest();
 
    // Swap rewards to base token
    uint256 baseAmount = dex.swap(rewardToken, baseToken, rewards);
 
    // Reinvest
    farmingContract.deposit(baseAmount);
}

Examples: Yearn Finance, Beefy Finance, Convex

DeFi on Ethereum Classic

While most DeFi activity is on Ethereum mainnet, DeFi can exist on any EVM chain:

Building DeFi on ETC? Consider: - Lower transaction costs than ETH mainnet - Smaller liquidity pools (higher slippage) - Need to bootstrap liquidity - Same smart contract code works on both chains - Cross-chain bridges for liquidity

Security Considerations

Oracle Manipulation

Flash loans can manipulate on-chain price oracles:

// VULNERABLE - uses spot price
function getPrice() public view returns (uint256) {
    (uint112 reserve0, uint112 reserve1, ) = pair.getReserves();
    return reserve0 / reserve1; // Can be manipulated!
}
 
// SAFE - use Chainlink or TWAP
function getPrice() public view returns (uint256) {
    (, int256 price, , , ) = chainlink.latestRoundData();
    return uint256(price);
}

Reentrancy in DeFi

DeFi protocols are prime targets for reentrancy:

// VULNERABLE
function withdraw(uint256 shares) external {
    uint256 amount = sharesToAssets(shares);
    (bool success, ) = msg.sender.call{value: amount}(""); // Reentrancy!
    balances[msg.sender] -= shares;
}
 
// SAFE
function withdraw(uint256 shares) external nonReentrant {
    uint256 amount = sharesToAssets(shares);
    balances[msg.sender] -= shares; // Effects before interactions
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
}

Economic Attacks

DeFi introduces new attack vectors:

  • Flash loan attacks
  • Governance attacks
  • MEV extraction
  • Sandwich attacks

Composability

DeFi's "money legos" allow protocols to build on each other:

// Example: Leveraged yield farming
function leveragedFarm() external {
    // 1. Deposit collateral to Aave
    aave.deposit(collateral, amount);
 
    // 2. Borrow stablecoins
    aave.borrow(usdc, amount * 60 / 100);
 
    // 3. Swap to LP token
    uint256 lpAmount = curve.addLiquidity([usdc, 0, 0]);
 
    // 4. Stake LP for rewards
    convex.deposit(lpAmount);
 
    // Result: Earning yield on borrowed funds
}

Conclusions

DeFi recreates financial services without intermediaries:

  • DEXes — Trade without order books using AMMs
  • Lending — Borrow and lend without banks
  • Stablecoins — Stable value without central issuers
  • Flash loans — Uncollateralized loans in single transactions
  • Yield — Compound rewards automatically

Key considerations:

  • Smart contract risk is real — use audited protocols
  • Oracle manipulation is a major attack vector
  • Composability creates systemic risk
  • The same protocols can run on ETH and ETC

DeFi demonstrates the power of permissionless innovation on the EVM.