Uniswap v2

  • Uniswap is an automated liquidity protocol powered by a constant product formula and implemented in a system of non-upgradeable smart contracts.

  • 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 an equivalent value of each underlying token in return for pool tokens. These tokens track pro-rata LP shares of the total reserves, and can be redeemed for the underlying assets at any time

Constant Product

Expressed as x * y = k, states that trades must not change the product (k) of a pair’s reserve balances (x and y). Because k remains unchanged from the reference frame of a trade, it is often referred to as the invariant.

  • Uniswap applies a 0.30% fee to trades, which is added to reserves. As a result, each trade actually increases k

  • This functions as a payout to LPs, which is realized when they burn their pool tokens to withdraw their portion of total reserves

  • Because the relative price of the two pair assets can only be changed through trading, divergences between the Uniswap price and external prices create arbitrage opportunities. This mechanism ensures that Uniswap prices always trend toward the market-clearing price.

Architecture

Uniswap V2 Core are the essential Uniswap V2 smart contracts:

Uniswap V2 Periphery (periphery) is an initial set of helpers, including:

Logic related to trader security or ease-of-use is implemented in external helper contracts. External helpers can be improved and replaced without needing to migrate liquidity, which improves on the flexibility and modularity of Uniswap.

Core Contracts

Singleton Factory

  • Holds the generic bytecode responsible for powering pairs

  • Create one and only one smart contract per unique token pair

  • Contains logic to turn on the protocol charge

  • Create Pair Function:

    • checks that token A and token B are different addresses

    • check that neither are the zero address

    • check that pair does not already exist

    • get bytecode for pair contract

    • calculate create2 salt for these two token addresses

    • deploy pair contract using create2

    • initialise the pair contract with the token addresses

    • update the pair mapping to track created pairs

function createPair(address tokenA, address tokenB) 
		external returns (address pair) {
				bytes memory bytecode = type(UniswapV2Pair).creationCode;
        bytes32 salt = keccak256(abi.encodePacked(token0, token1));
        assembly {
            pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
		}

Pairs

  • Serve as automated market makers

  • Keep track of pool token balances

  • Expose data which can be used to build decentralized price oracles

  • Swap Function:

    • to address: the user

    • why are there two out tokens

    • checks that input token amounts are positive

    • checks that pool has sufficient tokens to transfer to caller

    • checks that caller has sent sufficient amount of input tokens

    • calculate and remove 0.3% uniswap fee from input tokens

    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) 
    		external lock {}

Design Decisions

Sending Tokens

  • typically, smart contracts require the user to give approval of the token contract to itself, before calling transferFrom on the token

  • however, this is not how v2 pair contracts accept tokens

  • v2 pairs check their token balances at the end of every interaction

  • Then, at the beginning of the next interaction, current balances are differenced against the stored values to determine the amount of tokens that were sent by the current interactor.

  • tokens must be transferred to the pair before calling any token-requiring method (except in Flash Swaps)

WETH

  • Unlike Uniswap V1 pools, V2 pairs do not support ETH directly, so ETH⇄ERC-20 pairs must be emulated with WETH

  • motivation behind this choice was to remove ETH-specific code in the core to have a leaner codebase

  • The router convert ETH to WETH so that users may utilise it directly.

Minimum Liquidity

  • To ameliorate rounding errors and increase the theoretical minimum tick size (smallest increment) for liquidity provision, pairs burn the first MINIMUM_LIQUIDITY (returns 1000 for all pairs) pool tokens.

  • For most pairs, this will represent a trivial value. The burning happens automatically during the first liquidity provision, after which point the totalSupply is forevermore bounded.

  • For the first LP provision, LP tokens are burnt so the market maker cannot “rug”.

Last updated