Checklist for building a Uniswap V2 clone

It’s very educational to rebuild Uniswap v2 from scratch using modern Solidity (or Huff if you really want to do it in hard mode). Here are some pointers and suggestions for doing so.

  • Use an updated version of solidity. Be aware that this will lead to syntactic changes.
  • Replace the fixed point numbers with custom data types.
  • Use the Solady ERC20 for the ERC20 to save gas.
  • Don’t use Uniswap V2’s current reentrancy protection, it isn’t gas efficient anymore, use OpenZeppelin’s or some other alternative.
  • Be careful to add unchecked in the price oracle, it expects to overflow.
  • Don’t use safeMath with Solidity 0.8.0 or higher.
  • If you are not implementing the router separately, safety checks for slippage need to be built into the contract. EOAs cannot send tokens as part of the transaction.
  • Be sure to put the re-entrancy lock in the right places. Is Uniswap V2 subject to read-only reentrancy? Why or why not?
  • The factory contract can be simplified without assembly because of Solidity updates.
  • Watch out for fee-on-transfer tokens or re-basing tokens.
  • The square root function can be done more efficiently with the Solady library, but be sure you are rounding in the correct direction.
  • Uniswap hardcodes the fee with magic numbers, this isn’t an ideal way to write code.
  • Don’t forget to follow the professional solidity style guide (which Uniswap V2 does not follow).
  • The way the factory tracks pairs isn’t gas efficient, try to improve it.
  • Some storage variables in the original implementation could be made to be immutable (immutable variables were not available at the time Uniswap V2 was launched).
  • Custom errors will lead to a cheaper deployment cost than require statements (generally)
  • When burning the initial supply, make sure that the totalSupply does not go down to zero. Otherwise, the defense against the first deposit attack will not work. Some implementations of burn reduce the total supply rather than locking up the funds as Uniswap intends.
  • Be careful to burn, mint, and update reserves in the correct order when computing shares.
  • Uniswap V2’s _safeTransfer is subject to the memory expansion attack (very unlikely, but still should be guarded against). Since only one bool will be read, it’s best to returndatacopy only one word. Try to avoid getting into the habit of reading the entire return data from other contracts.
  • A good invariant test is liquidity should never go down if burn is not called.
  • Don’t use the balance variables or pool balance as an oracle, as these are vulnerable to flash loan attacks.
  • Remember to always round in favor of the pool when doing trades or minting or burning shares.
  • Don’t forget to write unit tests.
  • Give our book on Solidity gas optimization a read for inspiration about how to improve performance (after you finish your second or third draft of the Uniswap clone and have written tests). If you want to do this in hard mode, there are several opportunities to improve the gas using assembly without substantial sacrifices in readability.
  • Try out DamnVulnerableDefi Puppet V2. It should be easy for you to solve at this point.

Learn more with RareSkills

This material is part of our advanced Solidity Bootcamp. Please see the program to learn more.

Originally Published November 1, 2023

get_D() and get_y() in Curve StableSwap

get_D() and get_y() in Curve StableSwap This article shows algebraically step-by-step how the code for get_D() and get_y() are derived from the StableSwap invariant. Given the StableSwap Invariant: $$ An^n\sum x_i +D=An^nD+\frac{D^{n+1}}{n^n\prod x_i} $$ There are two frequent math operations we wish to conduct with it: Compute $D$ given fixed values for $A$, and the […]

Fixed Point Arithmetic in Solidity (Using Solady, Solmate, and ABDK as Examples)

Fixed Point Arithmetic in Solidity (Using Solady, Solmate, and ABDK as Examples) A fixed-point number is an integer that stores only the numerator of a fraction — while the denominator is implied. This type of arithmetic is not necessary in most programming languages because they have floating point numbers. It is necessary in Solidity because […]

Uniswap V2: Calculating the Settlement Price of an AMM Swap

Uniswap V2: Calculating the Settlement Price of an AMM Swap This article explains how to determine the price settlement of a trading pair in an Automated Market Maker (AMM). It answers the question of “How many token X can be swapped for token Y from the AMM?”. The swap() function on Uniswap V2 requires you […]

How Chainlink Price Feeds Work

How Chainlink Price Feeds Work Chainlink price oracles are smart contracts with public view functions that return the price of a particular asset denominated in USD. Off-chain nodes collect the prices from various sources like exchanges and write the price data to the smart contract. Here is the smart contract for getting the price of […]