sushh.com

Sushmita (aka meowy)

Uniswap V2/V3 Notes

Note: This is a curated educational resource combining information from official documentation, community resources, and AI assistance to provide a detailed learning guide and notes.

Uniswap revolutionized decentralized finance by creating an automated market maker (AMM) that allows anyone to trade tokens without traditional order books. As the protocol evolved from V2 to V3, significant improvements were made in capital efficiency and functionality. Let's dive deep into both versions to understand how they work and when to use each.


What is Uniswap and How Does it Work?

The AMM Revolution

Traditional exchanges use order books where buyers and sellers place orders at specific prices. Uniswap works completely differently - it uses liquidity pools where traders swap against pooled funds, with prices determined by mathematical formulas.

The Core Innovation: Instead of matching buyers with sellers, Uniswap matches traders with liquidity pools. When you want to trade ETH for USDC, you're not trading with another person - you're trading with a smart contract that holds both ETH and USDC.


Uniswap V2: Understanding the Foundation

What Does Uniswap V2 Swap Do?

Uniswap V2 swap allows you to exchange one token for another using the constant product formula: x * y = k. Here's how it works:

  • When you trade, you add Token A to the pool and remove Token B
  • The product of both token amounts must always remain constant
  • This automatically adjusts the price based on supply and demand
  • The more you trade, the worse the price gets (this is called "slippage")

V2 Swap Features Explained

1. Exact Input Swaps ("Sell All")

  • You specify exactly how much you want to sell
  • You get whatever the market gives you (minus slippage)
  • Perfect for: "I want to sell all my ETH for DAI"
  • Use case: Converting your entire token balance

2. Exact Output Swaps ("Buy Specific")

  • You specify exactly how much you want to buy
  • You pay whatever it costs (up to your limit)
  • Perfect for: "I need exactly 1000 DAI, whatever it costs"
  • Use case: Paying for something that requires an exact amount

How V2 Routing Works

Sometimes there's no direct trading pair between two tokens. V2 solves this with multi-hop routing:

Single Hop: Direct swap between two tokens (WETH ↔ DAI)

  • Cheaper gas costs
  • Better for popular pairs
  • Lower slippage

Multi-Hop: Route through an intermediary token (DAI → WETH → USDC)

  • Necessary when no direct pair exists
  • Higher gas costs (multiple swaps)
  • More slippage (price impact compounds)

V2 Swap Code Example Explained

function swapExactETHForDAI(uint256 amountOutMin) external payable returns (uint256) {
    // 1. Define the trading path
    address[] memory path = new address[](2);
    path[0] = WETH;  // Start with WETH
    path[1] = DAI;   // End with DAI
 
    // 2. Execute the swap
    uint256[] memory amounts = router.swapExactETHForTokens{value: msg.value}(
        amountOutMin,           // Minimum DAI you're willing to accept
        path,                   // The trading route
        msg.sender,             // Who receives the DAI
        block.timestamp + 300   // Transaction must complete within 5 minutes
    );
 
    return amounts[1]; // Return actual DAI received
}

What this code does:

  1. Path Definition: Tells Uniswap the route (ETH → DAI)
  2. Slippage Protection: amountOutMin prevents bad trades
  3. Deadline Protection: Prevents transactions from sitting in mempool too long
  4. Return Value: Shows exactly how much DAI you got

Uniswap V2 Liquidity: How Pool Creation Works

What Does V2 Liquidity Do?

V2 liquidity allows you to become a "market maker" by providing tokens to trading pools. Here's what happens:

  1. You deposit two tokens (e.g., ETH + USDC) into a pool
  2. You receive LP tokens representing your share of the pool
  3. Traders pay fees (0.3%) when they use your liquidity
  4. You earn those fees proportional to your share
  5. You can withdraw anytime by burning your LP tokens

V2 Pool Structure: The Math Behind It

Here's how a real V2 pool works with actual numbers:

Key Concepts Explained:

  1. Price Discovery: Price = Token B Reserve / Token A Reserve
  2. LP Token Formula: √(Reserve A × Reserve B) - this ensures fair distribution
  3. Constant Product: 1,000 × 3,000,000 = 3,000,000,000 (this never changes)
  4. Your Share: If you own 10% of LP tokens, you own 10% of both reserves

V2 Liquidity Features

Automatic Balancing: V2 always keeps your liquidity active across all possible prices

  • ✅ Simple and passive
  • ❌ Capital inefficient (most liquidity never used)

Auto-Compounding Fees: Trading fees automatically get added to the pool

  • ✅ Your position grows automatically
  • ❌ Fees are locked until you withdraw

Impermanent Loss: Your token ratio changes as prices move

  • If ETH price doubles, you'll have less ETH and more USDC than if you just held
  • You only "lose" compared to holding the tokens separately

V2 Liquidity Code Example Explained

function addLiquidity(
    address tokenA,
    address tokenB,
    uint256 amountA,
    uint256 amountB
) external returns (uint256 amountAUsed, uint256 amountBUsed, uint256 liquidity) {
    // 1. Transfer tokens from user to this contract
    IERC20(tokenA).transferFrom(msg.sender, address(this), amountA);
    IERC20(tokenB).transferFrom(msg.sender, address(this), amountB);
 
    // 2. Allow the router to spend our tokens
    IERC20(tokenA).approve(ROUTER, amountA);
    IERC20(tokenB).approve(ROUTER, amountB);
 
    // 3. Add liquidity to the pool
    (amountAUsed, amountBUsed, liquidity) = IUniswapV2Router(ROUTER).addLiquidity(
        tokenA,
        tokenB,
        amountA,        // How much Token A you want to add
        amountB,        // How much Token B you want to add
        1,              // Minimum Token A (slippage protection)
        1,              // Minimum Token B (slippage protection)
        msg.sender,     // Who receives the LP tokens
        block.timestamp + 300
    );
 
    // 4. Refund any unused tokens
    if (amountA > amountAUsed) {
        IERC20(tokenA).transfer(msg.sender, amountA - amountAUsed);
    }
    if (amountB > amountBUsed) {
        IERC20(tokenB).transfer(msg.sender, amountB - amountBUsed);
    }
}

What this code does:

  1. Token Transfer: Moves your tokens into the contract
  2. Approval: Allows Uniswap to use your tokens
  3. Liquidity Addition: Creates or adds to a pool
  4. Ratio Adjustment: Uniswap adjusts your token amounts to match pool ratio
  5. Refund: Returns any tokens that couldn't be used

Uniswap V3: The Revolutionary Upgrade

What Does V3 Swap Do Differently?

V3 swaps work similarly to V2 but with major improvements:

  1. Multiple Fee Tiers: Choose the right fee for each pair type
  2. Concentrated Liquidity: More efficient routing through focused liquidity
  3. Advanced Routing: Better pathfinding through multiple fee tiers

V3 Fee Tier System Explained

V3's biggest improvement is multiple fee tiers for different asset types:

Fee Tier Guide:

  • 0.01% (100): USDC/USDT, DAI/USDC (very stable, low risk)
  • 0.05% (500): ETH/stETH, WBTC/BTC (correlated assets)
  • 0.3% (3000): ETH/USDC, WBTC/ETH (standard volatile pairs)
  • 1% (10000): New tokens, very volatile pairs

Why This Matters: Stable pairs need lower fees to compete, while volatile pairs can charge more because of higher risk.

V3 Swap Code Example Explained

function swapExactInputSingle(
    address tokenIn,
    address tokenOut,
    uint24 fee,              // NEW: Choose fee tier
    uint256 amountIn,
    uint256 amountOutMinimum
) external returns (uint256 amountOut) {
 
    IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
    IERC20(tokenIn).approve(SWAP_ROUTER, amountIn);
 
    // V3 uses a struct for parameters (cleaner than V2's long parameter list)
    ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
        tokenIn: tokenIn,
        tokenOut: tokenOut,
        fee: fee,                    // 100, 500, 3000, or 10000
        recipient: msg.sender,
        deadline: block.timestamp + 300,
        amountIn: amountIn,
        amountOutMinimum: amountOutMinimum,
        sqrtPriceLimitX96: 0        // Advanced: price limit feature
    });
 
    amountOut = ISwapRouter(SWAP_ROUTER).exactInputSingle(params);
}

Key V3 Improvements:

  1. Fee Selection: You choose which pool to use
  2. Parameter Struct: Cleaner, more organized parameters
  3. Price Limits: Advanced feature to stop swaps at certain prices
  4. Better Gas: More efficient routing algorithms

Uniswap V3 Liquidity: Concentrated Liquidity Revolution

What Does V3 Concentrated Liquidity Do?

V3's concentrated liquidity is the biggest innovation in DeFi AMMs. Instead of spreading your liquidity across all possible prices (like V2), you can focus it in specific price ranges where trading actually happens.

V3 Concentrated Liquidity Features Explained

Capital Efficiency: Up to 4000x more efficient than V2

  • V2: Your $1000 is spread from $0 to infinity
  • V3: Your $1000 is focused on $2800-$3200 (where trading happens)
  • Result: You earn the same fees with much less capital

Active Management Required: Your liquidity can go "out of range"

  • If ETH price moves outside your range, you stop earning fees
  • You need to rebalance or adjust your range
  • More work, but much higher potential returns

NFT Positions: Each liquidity position is a unique NFT

  • No fungible LP tokens like V2
  • Each position has custom parameters
  • Can't easily trade positions (they're unique)

Understanding V3's Tick System

V3 uses a tick system for precise price range definitions:

How Ticks Work:

  • Each tick = 0.01% price change
  • Tick spacing depends on fee tier (prevents too many small ranges)
  • Your position = [tickLower, tickUpper]
  • Example: ETH at $3000, you want $2800-$3200 range = specific tick numbers

Why Tick Spacing Matters:

  • Prevents liquidity fragmentation
  • Lower fees = tighter spacing (stable coins need precision)
  • Higher fees = wider spacing (volatile pairs don't need as much precision)

V3 Liquidity Code Example Explained

function mintNewPosition(
    address token0,
    address token1,
    uint24 fee,
    int24 tickLower,      // Price range start
    int24 tickUpper,      // Price range end
    uint256 amount0Desired,
    uint256 amount1Desired
) external returns (
    uint256 tokenId,      // Your NFT ID
    uint128 liquidity,    // Liquidity amount added
    uint256 amount0,      // Actual token0 used
    uint256 amount1       // Actual token1 used
) {
    // 1. Transfer tokens to contract
    IERC20(token0).transferFrom(msg.sender, address(this), amount0Desired);
    IERC20(token1).transferFrom(msg.sender, address(this), amount1Desired);
 
    // 2. Approve position manager
    IERC20(token0).approve(POSITION_MANAGER, amount0Desired);
    IERC20(token1).approve(POSITION_MANAGER, amount1Desired);
 
    // 3. Set up position parameters
    INonfungiblePositionManager.MintParams memory params =
        INonfungiblePositionManager.MintParams({
            token0: token0,
            token1: token1,
            fee: fee,                    // Choose your fee tier
            tickLower: tickLower,        // Your range start
            tickUpper: tickUpper,        // Your range end
            amount0Desired: amount0Desired,
            amount1Desired: amount1Desired,
            amount0Min: 0,               // Slippage protection
            amount1Min: 0,               // Slippage protection
            recipient: address(this),    // Who owns the NFT
            deadline: block.timestamp + 300
        });
 
    // 4. Mint the position NFT
    (tokenId, liquidity, amount0, amount1) =
        INonfungiblePositionManager(POSITION_MANAGER).mint(params);
 
    // Note: Unlike V2, you need to manually collect fees later
}

What makes this different from V2:

  1. Tick Range: You specify exactly where your liquidity is active
  2. NFT Return: You get a unique NFT instead of fungible tokens
  3. Fee Tier Choice: You pick the appropriate fee level
  4. Manual Fee Collection: Fees don't auto-compound

V2 vs V3: The Complete Comparison

Architecture and Philosophy Differences

The fundamental differences between V2 and V3 affect every aspect of how they operate:

Detailed Feature Comparison

Liquidity Provision:

  • V2: Spread across entire price curve (0 to ∞)
  • V3: Concentrated in custom price ranges
  • Impact: V3 can be 4000x more capital efficient

Position Management:

  • V2: Set it and forget it (passive)
  • V3: Active management required (rebalancing)
  • Impact: V3 needs more attention but offers higher returns

Fee Structure:

  • V2: Fixed 0.3% for all pairs
  • V3: 0.01%, 0.05%, 0.3%, 1% based on asset type
  • Impact: More appropriate pricing for different risk levels

Fee Collection:

  • V2: Automatically reinvested (compounding)
  • V3: Manual collection required
  • Impact: V3 gives more control but requires action

Token Representation:

  • V2: Fungible ERC20 LP tokens
  • V3: Unique ERC721 NFTs
  • Impact: V2 positions can be easily traded, V3 positions are unique

Gas Costs:

  • V2: Lower (simpler calculations)
  • V3: Higher (complex tick math)
  • Impact: V3 trades cost ~15-20% more gas

Performance Comparison: Real Numbers

Capital Efficiency Example:

  • V2: $10,000 spread across $0-∞, earns fees on ~5% of trades
  • V3: $10,000 concentrated in $2800-$3200, earns fees on ~80% of trades
  • Result: Same fees earned with much less capital

Risk vs Reward:

  • V2: Lower risk, predictable returns, passive
  • V3: Higher risk, higher potential returns, active management needed

Impermanent Loss:

  • V2: Gradual across all price movements
  • V3: More concentrated but can be mitigated with tight ranges

When to Use V2 vs V3: Decision Framework

Use Uniswap V2 When:

You Want Simplicity:

  • Set it and forget it liquidity provision
  • Don't want to actively manage positions
  • Predictable behavior and returns

Gas Costs Matter:

  • Frequent small trades
  • Building applications where every gas unit counts
  • Users are very price-sensitive

Token Characteristics:

  • Long-tail tokens without V3 pools
  • Very new tokens with uncertain price ranges
  • Tokens with extreme volatility

Development Simplicity:

  • Building simple DeFi protocols
  • Need predictable AMM behavior
  • Want to avoid complex tick calculations

Use Uniswap V3 When:

Capital Efficiency is Key:

  • Limited capital but want maximum returns
  • Professional liquidity provision
  • Sophisticated DeFi strategies

You Can Actively Manage:

  • Have time and tools for monitoring
  • Can rebalance positions regularly
  • Understand the risks of going out of range

Asset Characteristics:

  • Stablecoin pairs (tight ranges work great)
  • Well-established trading pairs
  • Assets with predictable trading ranges

Advanced Features Needed:

  • Multiple fee tiers
  • Precise price range targeting
  • Advanced trading strategies

Smart Contract Addresses Reference

For developers building on Uniswap, here are the key contract addresses on Ethereum mainnet:

V2 Contracts:

  • Router: Handles all swaps and liquidity operations
  • Factory: Creates new trading pairs

V3 Contracts:

  • Router: Handles swaps with advanced features
  • Factory: Creates pools with different fee tiers
  • Position Manager: Manages NFT liquidity positions
  • Quoter: Provides price quotes without executing trades