Section 16 of 16
Complete Protocol
Final Build
Submit your complete protocol and run the full test suite.
What You Built
You have written the entire Uniswap V2 protocol from scratch. Five contracts. Every function body. Every invariant. Every edge case handler. This is the same code that secures billions of dollars across hundreds of forks.
Here is what you built, contract by contract.
Part 1: Core
UniswapV2ERC20 is the LP token. It implements a full ERC-20 with one critical addition: permit() for gasless approvals via EIP-2612 signatures. Every pair inherits from this contract. The infinite approval optimization in transferFrom (skipping the allowance decrease when set to type(uint256).max) saves gas on every swap that goes through an approved router.
UniswapV2Pair is the AMM. It holds two token reserves, tracks cumulative prices for the TWAP oracle, and exposes three core operations:
mint() takes deposited tokens and issues LP shares. The first depositor gets sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY shares, where the minimum liquidity is permanently locked to prevent share inflation attacks. Subsequent depositors get shares proportional to the smaller ratio of their deposit to existing reserves.
burn() redeems LP shares for underlying tokens. The amounts are proportional to the share of total supply being burned.
swap() enforces the constant product invariant. After accounting for the 0.3% fee via adjusted balances (balance * 1000 - amountIn * 3), it verifies that adjustedBalance0 * adjustedBalance1 >= reserve0 * reserve1 * 1000^2. The _update function records cumulative price-time products for the TWAP oracle on every state change.
UniswapV2Factory deploys pairs using CREATE2, giving each pair a deterministic address computed from the two token addresses. The feeToSetter can enable protocol fees, which would mint LP tokens to a fee recipient based on the growth of sqrt(k).
Part 2: Periphery
UniswapV2Library provides the stateless math layer. sortTokens establishes canonical token ordering. pairFor computes CREATE2 addresses without any external calls. getAmountOut and getAmountIn implement the fee-adjusted constant product math. getAmountsOut and getAmountsIn chain calculations across multi-hop paths.
UniswapV2Router02 is the user-facing safety layer. It wraps every pair interaction with:
Slippage protection via minimum/maximum amount parameters on every function.
Deadline enforcement via the ensure modifier, preventing stale transactions.
Optimal liquidity calculation in _addLiquidity, which tries both directions to find the best deposit ratio within user constraints.
WETH wrapping and unwrapping for all ETH variants, with excess refunds.
Multi-hop routing in _swap, which chains pair calls by setting intermediate recipients to the next pair's address.
Fee-on-transfer support in _swapSupportingFeeOnTransferTokens, which reads actual balances instead of trusting pre-calculated amounts. This handles tokens like USDT that deduct fees on transfer.
Gasless approvals via permit variants on liquidity removal functions.
The Complete Test Suite
The final test validates all five contracts together. It checks:
The ERC-20 fundamentals (mint, burn, transfer, permit, infinite approval).
The pair mechanics (initialization, reentrancy guard, safe transfer, reserve updates, TWAP accumulation, MINIMUM_LIQUIDITY locking, K invariant enforcement with fee adjustment, flash swap callback support).
The factory (CREATE2 deterministic deployment, fee configuration).
The library math (token sorting, CREATE2 address computation, quote proportionality, getAmountOut fee calculation, getAmountIn rounding, path chaining).
The router safety (deadline enforcement, optimal liquidity calculation, pair creation on first add, ETH wrapping/unwrapping/refunding, multi-hop swap routing, FOT balance-based swaps, permit integration, slippage validation).
What To Do Next
Deploy to a testnet. Take your contracts, deploy them to Sepolia or Anvil, and interact with them. Create pairs. Add liquidity. Swap. Remove liquidity. See the state changes happen on chain.
Run Krait. Point the Krait auditor at your contracts. See what it finds. Compare its findings to the known Uniswap V2 design decisions.
Modify for your own project. Change the fee from 0.3% to something else. Add a new oracle mechanism. Implement concentrated liquidity ranges. Now that you understand every line, you know exactly what to change and what breaks if you change it wrong.
Build a frontend. Connect a React app to your deployed contracts. Show reserves, calculate swap previews, submit transactions. This is how DEX frontends work under the hood.
You did not just learn about Uniswap V2. You built it. Every function, every invariant, every edge case. That understanding does not go away.
Verify Your Understanding
What is the mathematical formula that the swap function enforces to guarantee fair pricing?
Three variables, two operators. The formula that gave this entire class of protocols its name.