This is a study note of Uniswap v2.
Uniswap is an automated liquidity protocol powered by a constant product formula. Each Uniswap smart contract, or pair, manages a liquidity pool made up of reserves of two ERC-20 tokens. Anyone can become a liquidity provider (LP) for a pool by depositing two underlying tokens in return for pool tokens. The pool tokens track pro-rata LP shares of the total reserves, and can be redeemed for the underlying assets at any time.
Uniswap applies a 0.3% trade fee, which is added to reserves. As a result, the invariant (constant product) actually increases for each trade. The increase functions as a payout to LPs, which is realized when they burn their pool tokens to withdraw their portion of toal reserves.
The Uniswap ecosystem has three types of users: liquidity providers (LPs), traders, and developers.
- There are different types of LPs
- Passive LPs are token holders who invest their assets to earn trading fees.
- Professional LPs develop market making stategy to profit from their liquidity positions across different DeFi projects.
- Token projects use Uniswap as liquid marketplace for their tokens.
- Some DeFi pioneers explore liquidity for their experiments.
- There are several categories of traders
- Speculators swap tokens
- Arbitrage bots seek cross-platform edges
- DApp users buy tokens
- Smart contracts execute trades
- Developers build pojrects in Uniswap ecosystem
- Front-end UI
- DEX aggregators
- New DeFi tools
Uniswap V2 consists of core contracts and periphery contracts. The core has a singleton factory and many pair contracts. The factory creates and index pair contracts. A pair contract has two purposes: serving AMM and keeping track of pool token balance. Both types of contracts are quite minimal and not user-friedly. The periphery contracts provides user-friendly and domain-specific interactions with the core. The periphery library provides several convenience functions for fetching data and pricing. The periphery router contract provides basic trading and liquidity management functionality.
There are some design decisions:
- Sending tokens first: for safety reason, tokens must be transferred to pairs before swap is called (the one exception is flash swaps).
- Using a wrapped ETH token WETH.
- Minimum liquidity to ameliorate errors and increase the minimum tick size for liquidity provision.
2 How Are Prices Determined?
The core doesn’t provide a pricing function. It only check whether the invariant was satisfied after every trade. Trades are priced in the periphery. A contract must look up the current reserves of a pair to get the current price. It is not safe to perform the lookup and rely on the results because of the front-running attack. A safe way is to check an off-chain Oracle to find a fair price and calculate the optimal input/output amounts, then use the router to perform a swap with a slippage tolerance (0.5% by default). Alternately, a native oracle can be used.
3 LP Returns
The divergence in price between deposit and withdrawl brings impermanent loss (or divergence loss) to LPs. The amout is
loss = 2 * sqrt(price_ratio) / (1 + price_ratio) - 1. The loss is the same for both direction – doubling in price results in the same loss rate as a halving.
Therefore the actual return for LPs is a balance between the divergence loss and the accumulated fees. When the prices of withdraw and deposit are the same, a LP gets all trading fees. If the price movement is large, an LP may loss money.
Uniswap conducted auditing for Uniswap V2:
- formal verification of core smart contracts
- code review of all smart contracts
- numerical error analysis
There are two primary categories of risks:
- Static errors that can be fixed by logic checks in routers
- sending too many tokens in a swap
- allowing transactions to linger in the mempool too long
- Dynamic rik is about runtime pricing. It can be fixed by checking oracle price.