Lesson 24 / 25

DeFi Building Blocks and Layer 2s

Understand AMMs, lending, stablecoins and scaling, and their risks.

Decentralised finance in brief

DeFi composes contracts into financial services. Automated market makers (AMMs) such as Uniswap hold pools of two tokens and price trades with a formula; the classic constant product model keeps x * y = k, so buying one token raises its price, with slippage growing for larger trades; liquidity providers earn fees but face impermanent loss when prices move. Lending protocols (such as Aave and Compound) let users deposit collateral and borrow other assets, with liquidations when collateral value falls below a threshold, relying on oracles. Stablecoins track a currency through fiat reserves (USDC), crypto over-collateralisation (DAI and its successors) or other mechanisms, some of which have failed spectacularly. ERC-4626 standardises yield-bearing vaults with shares. Flash loans lend any amount within a single transaction if repaid, useful for arbitrage and, unfortunately, for attacks. Layer 2 rollups reduce fees for all of these, and since the Dencun upgrade (2024), rollups post data in cheaper blobs. DeFi carries smart contract, oracle, liquidity, governance, regulatory and counterparty risks; this course teaches the technology, not investment advice.

Constant-product swap math

Output amount for an x*y=k pool with a 0.3% fee, as in Uniswap v2.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

library ConstantProduct {
    error InsufficientLiquidity();
    error InsufficientInput();

    /// Given amountIn of token A and reserves (reserveIn, reserveOut),
    /// returns how much token B the pool gives out after a 0.3% fee.
    function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
        internal
        pure
        returns (uint256 amountOut)
    {
        if (amountIn == 0) revert InsufficientInput();
        if (reserveIn == 0 || reserveOut == 0) revert InsufficientLiquidity();
        uint256 amountInWithFee = amountIn * 997;                    // 0.3% fee kept in the pool
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * 1000 + amountInWithFee;
        amountOut = numerator / denominator;                          // rounds down in the pool's favour
    }
}

// Example: reserves 1,000 A and 1,000 B, swap in 100 A:
//   out = 100*997*1000 / (1000*1000 + 100*997) = 99,700,000 / 1,099,700 ≈ 90.66 B
// The ideal price ignoring slippage and fees would give 100 B.
// A router should also take a minAmountOut (slippage limit) and a deadline from the user.

Always set slippage limits

A swap without a minimum output amount can be sandwiched by bots: they buy before you and sell after you, and you receive far less. Slippage limits and deadlines protect users.

Quick check: In a constant-product AMM, what happens to the price of token B as you buy more of it in one trade?

  • It rises, so larger trades get worse average prices (slippage)
  • It stays the same
  • It falls
  • Trading stops
Answer

It rises, so larger trades get worse average prices (slippage) — x*y=k makes each additional unit more expensive as reserves of B shrink.