--- name: AMM & Lending Protocol Design Patterns description: Use this skill when designing, auditing, or integrating with AMM (Uniswap-style) or lending protocols — to understand core mathematical formulas, architectural patterns, composability risks, and common implementation pitfalls. ecosystem: ethereum type: defi-guide source: community confidence: high version: 1.0.0 time_sensitivity: evergreen tags: - amm - uniswap - lending - aave - compound - defi-patterns - liquidity - interest-rate updated_at: 2026-03-26T00:00:00.000Z --- # AMM & Lending Protocol Design Patterns Distilled from DeFiLlama data covering the top AMM protocols ($8.7B TVL combined: Uniswap V3 $1.71B, Curve $1.85B, PancakeSwap $1.67B, Raydium $1.05B) and lending protocols ($42.9B TVL combined: Aave V3 $23.9B, Morpho $6.7B, Sky/MakerDAO $7.3B, Compound $1.3B). --- ## AMM Design Patterns ### 1. Constant Product Formula (x·y = k) The foundational AMM invariant, introduced by Uniswap V1/V2: ``` x * y = k ``` Where `x` and `y` are reserve amounts of token A and B. The spot price is `x/y`. Every swap moves along the curve, maintaining `k`. **Key properties:** - Price impact grows non-linearly with trade size - Price slippage formula: `output = (y * amountIn) / (x + amountIn)` - 0.3% fee adds: `amountInWithFee = amountIn * 997 / 1000` **Implementation pattern (Uniswap V2 style):** ```solidity function swap(uint amount0Out, uint amount1Out, ...) external { uint balance0 = IERC20(token0).balanceOf(address(this)) - amount0Out; uint balance1 = IERC20(token1).balanceOf(address(this)) - amount1Out; require(balance0 * balance1 >= uint(reserve0) * reserve1, "K"); // invariant check } ``` **Pitfall:** K-invariant check alone doesn't prevent flash loan exploitation — must check K *after* the callback resolves. ### 2. Concentrated Liquidity (Uniswap V3) Uniswap V3 allows LPs to provide liquidity within a custom price range `[P_lower, P_upper]`. This achieves up to 4000× more capital efficiency for stable pairs but requires active management. **Key formulas:** ``` liquidity L = sqrt(x * y) x = L * (sqrt(P_upper) - sqrt(P)) / (sqrt(P) * sqrt(P_upper)) y = L * (sqrt(P) - sqrt(P_lower)) ``` **Design implications:** - Single-sided liquidity: if price moves outside range, LP holds 100% of the out-of-range token - Fee tiers: 0.01% (stable pairs), 0.05% (major pairs), 0.30% (standard), 1.00% (exotic) - Tick spacing: lower fee tiers have finer tick granularity **Implementation pitfalls:** - Out-of-range positions earn no fees — active management required - Tick math requires careful rounding (round against LP to prevent K violations) - `sqrtPriceX96` fixed-point format introduces precision constraints ### 3. StableSwap / Curve Formula For assets that should trade near 1:1 (e.g., USDC/USDT, stETH/ETH), the StableSwap invariant: ``` A * n^n * sum(x_i) + D = A * D * n^n + D^(n+1) / (n^n * prod(x_i)) ``` Where `A` is the amplification coefficient controlling how "flat" the curve is near equilibrium. **Design implications:** - Higher `A` → less price slippage near peg, more divergence when depeg occurs - `A` should be governed carefully — sudden changes break arbitrage assumptions - Used by all major stablecoin pools, LSD pools (stETH/ETH), and cross-chain bridges ### 4. AMM Fee Calculation & LP Token Math **LP token minting (Uniswap V2 first deposit):** ```solidity liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY; ``` **Subsequent deposits (proportional):** ```solidity liquidity = min(amount0 * totalSupply / reserve0, amount1 * totalSupply / reserve1); ``` **Critical pitfall:** If an attacker donates tokens to the pool before the first LP deposits, they can inflate the price and manipulate the `sqrt(amount0 * amount1)` calculation for the first LP. Fix: enforce `MINIMUM_LIQUIDITY = 1000` permanently burned to lock the initial ratio. ### 5. Impermanent Loss (IL) IL is the loss LPs experience vs holding when prices diverge: ``` IL = 2 * sqrt(price_ratio) / (1 + price_ratio) - 1 ``` | Price change | IL | |---|---| | 2× (or 0.5×) | 5.7% | | 5× (or 0.2×) | 25.5% | | 10× (or 0.1×) | 42.5% | **For protocol designers:** IL protection mechanisms (IL insurance, single-sided staking) attempt to compensate LPs but introduce significant protocol risk if miscalibrated. --- ## Lending Protocol Design Patterns ### 6. Utilization-Based Interest Rate Curves The standard lending interest rate model (Compound/Aave): ``` if utilization <= kink: borrowRate = baseRate + (utilization / kink) * slopeBeforeKink else: borrowRate = baseRate + slopeBeforeKink + ((utilization - kink) / (1 - kink)) * slopeAfterKink ``` **Typical parameters (Aave USDC):** - Base rate: 0% - Kink: 80% utilization - Slope before kink: 4% APR - Slope after kink: 75% APR (aggressive — incentivizes repayments) **Why the kink matters:** At 80% utilization, the rate jumps sharply. This protects depositors from bank runs — if too many users borrow, rates become punitive, incentivizing repayment before the pool empties. ### 7. Health Factor & Liquidation Mechanics **Health Factor:** ``` HF = sum(collateral_i * liquidationThreshold_i) / totalDebt ``` When `HF < 1`, the position is liquidatable. **Liquidation process (Aave-style):** 1. Liquidator calls `liquidationCall(collateralAsset, debtAsset, user, debtToCover)` 2. Liquidator repays `debtToCover` on behalf of user 3. Liquidator receives `debtToCover * liquidationBonus` in collateral 4. Typical liquidation bonus: 5–15% depending on collateral risk tier **Implementation pitfall — liquidation cascade:** When collateral prices drop sharply, many positions become liquidatable simultaneously. Mass liquidations create selling pressure on the collateral asset, which drops prices further. Aave mitigates this via: - Gradual liquidation (cap on single liquidation amount) - Supply caps per asset - Risk-tiered isolation mode for high-volatility assets ### 8. Flash Loans Flash loans are uncollateralized loans that must be repaid within the same transaction: ```solidity function flashLoan(address receiver, address asset, uint256 amount, bytes calldata params) external { uint256 balanceBefore = IERC20(asset).balanceOf(address(this)); IERC20(asset).transfer(receiver, amount); IFlashLoanReceiver(receiver).executeOperation(asset, amount, fee, params); require(IERC20(asset).balanceOf(address(this)) >= balanceBefore + fee, "not repaid"); } ``` **Legitimate use cases:** Arbitrage, self-liquidation, collateral swaps, one-tx leverage entry/exit. **Attack surface:** Flash loans amplify every oracle manipulation and reentrancy attack. Never use spot prices from the same block as a flash loan for any financial decision. ### 9. Reserve Factor & Protocol Revenue Lending protocols extract fees by taking a cut of interest payments: ``` supplierAPY = borrowAPY * utilizationRate * (1 - reserveFactor) protocolRevenue = totalInterestPaid * reserveFactor ``` Typical reserve factors: 5–25%. Higher reserve factors reduce depositor APY and make the protocol less competitive. ### 10. ERC-4626 Tokenized Vault Standard Modern lending implementations use ERC-4626 to standardize vault interfaces: ```solidity function deposit(uint256 assets, address receiver) external returns (uint256 shares); function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); function convertToShares(uint256 assets) external view returns (uint256); function convertToAssets(uint256 shares) external view returns (uint256); ``` **Critical invariant:** `convertToAssets(convertToShares(x)) <= x`. If not maintained, vault is exploitable. **Common pitfall:** Inflation attack on first deposit (same as AMM). Mitigation: virtual shares/assets offset (`totalAssets + 1 / totalSupply + 1`). --- ## Composability Patterns & Risks ### Flash Loan + AMM + Lending Interaction The typical DeFi "Lego" attack pattern: 1. Flash loan 1M USDC 2. Deposit USDC as collateral in lending protocol 3. Borrow asset X against it 4. Sell X on AMM to manipulate price 5. Exploit the manipulated price 6. Repay flash loan **Defense:** Protocols should use time-weighted prices, not spot prices, for any action that can follow a flash loan in the same block. ## Design Checklist - [ ] AMM: Is the K-invariant checked after the full callback resolves? - [ ] AMM V3: Is tick rounding direction correct (round against LP)? - [ ] AMM first deposit: Is MINIMUM_LIQUIDITY enforced to prevent donation attacks? - [ ] Lending: Does the interest rate curve have a kink to protect against bank runs? - [ ] Lending: Are liquidations capped per transaction to prevent cascades? - [ ] Flash loans: Does any price oracle read in the same block become exploitable? - [ ] ERC-4626: Is the `convertToShares`/`convertToAssets` round-trip safe against inflation attacks?