/Uniswap-v2

🦄🦄 Single & multi hop swap, add & remove liquidity, flash swap ⚡⚡

Primary LanguageSolidity

Contributors Forks Stargazers Issues MIT License LinkedIn


Logo

Uniswap V2

Uniswap V2
Explore the docs »

View Demo · Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Constant Product AMM
  5. Impermanent Loss on Uniswap v2

About The Project

This project shows how to interact with the main functions of Uniswap V2

(back to top)

Built With

  • Hardhat
  • Ethers

(back to top)

Getting Started

To get a local copy up and running follow these simple example steps.

Prerequisites

  • npm

    npm install npm@latest -g
  • hardhat

    npm install --save-dev hardhat
    npm install @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle

    run:

    npx hardhat

    verify:

    npx hardhat verify --network goerli "contract address" "pair address"

Installation

  1. Clone the repo

    git clone https://github.com/Aboudoc/Uniswap-v2.git
  2. Install NPM packages

    npm install
  3. Dependencies

    npm i @uniswap/v2-core @uniswap/v2-periphery

(back to top)

Usage

If you need testnet funds, use the Alchemy testnet faucet.

This project shows how to swap, add and remove liquidity

Constant Product AMM

Uniswap V2 is a Constant product AMM (automated market maker) <=> a decentralized exchange where 2 tokens are traded. You can find a deep overview of CPAMM in this repo

Impermanent Loss on Uniswap v2

How to calculate Impermanent Loss in a constant product AMM?

If the price does not change at all, the d = 1 and there is no loss in providing liquidity to CPAMM

if the price changes either down or up, then the LP will experience some loss

How to calculate impermanent loss

Test

Let's see how to derive this equation. Find x and y

Test

We solved y and x in terms of L and P. We are now ready to solve IL(d)

Test

Le's find out what are V1 and Vhold in terms of token y.

Below we multiplied x1 by P1 to get the price of x1 in term of y

Test

We are now ready to solve the equation of IL(d)

Test

(back to top)

Test

Test

Uniswap V2 Single Hop Swap

This contract introduces 2 functions to swap tokens on Uniswap V2

swapExactTokensForTokens - Sell all of input token. swapTokensForExactTokens - Buy specific amount of output token.

State variables

  1. Address of tokens (2 or 3) and the address of the router
  2. Set interfaces for tokens and router

Function swapSingleHopExactAmountIn

  1. Transfer amountIn from msg.sender
  2. Approve amountIn to router
  3. Set the path
  4. Call swapExactTokensForTokens on IUniswapV2Router

Function swapSingleHopExactAmountOut

  1. Transfer amountInMaxfrom msg.sender
  2. Approve amountInMax to router
  3. Set the path
  4. Call swapTokensForExactTokens on IUniswapV2Router and store amount of WETH spent by Uniswap in amounts (uint[])
  5. Refund excess WETH to msg.sender. Amount of WETH spent by Uniswap is stored in amounts[0]

Uniswap V2 Multi Hop Swap

Sell DAI and buy CRV.

However there is no DAI - CRV pool, so we will execute multi hop swaps, DAI to WETH and then WETH to CRV.

State variables

  1. Address of tokens and the address of the router
  2. Set interfaces for tokens and router

Function swapMultiHopExactAmountIn

This function will swap all of DAI for maximum amount of CRV. It will execute multi hop swaps from DAI to WETH and then WETH to CRV.

  1. Transfer amountIn from msg.sender
  2. Approve amountIn to router
  3. Setup the swapping path
  4. Send CRV to msg.sender

Function swapMultiHopExactAmountOut

This function will swap minimum DAI to obtain a specific amount of CRV. It will execute multi hop swaps from DAI to WETH and then WETH to CRV.

  1. Transfer amountInMaxfrom msg.sender
  2. Approve amountInMax to router
  3. Setup the swapping path
  4. Call swapTokensForExactTokens on IUniswapV2Router and store amount of DAI spent by Uniswap in amounts (uint[])
  5. Refund DAI to msg.sender if not all of DAI was spent. Amount of DAI spent by Uniswap is stored in amounts[0]

(back to top)

Uniswap V2 Liquidity Delta

When we add or remove tokens from a Uniswap V2 pool, the liquidity changes.

Let's see how to derive the liquidity delta

As always, we start with the definition:

Test

Preliminary math to derive these equations (green square)

Test

Now we have the math needed, let's now derive the equation for the liquidity delta

Test

(back to top)

Uniswap V2 Add and Remove Liquidity

Deposit your tokens into an Uniswap V2 pool to earn trading fees.

This is called adding liquidity.

Remove liquidity to withdraw your tokens and claim your trading fees.

State variables

  1. Address of tokens and the addresses of the router and the factory. Declare pair variable
  2. Set interfaces for tokens, router and factory

Constructor

  1. Setup pair (IERC20) by calling getPair() on factory

Function addLiquidity

This function adds liquidity to the Uniswap WETH - DAI pool.

  1. Transfer wethAmountDesired and daiAmountDesired from msg.sender
  2. Approve amountwethAmountDesired and daiAmountDesired to router
  3. Call addLiqiuidity() on router and store wethAmount, daiAmount and liquidity returned from the function call
  4. Refund to msg.sender, excess WETH and DAI that were not added to liquidity

Function removeLiquidity

This function removes liquidity from the Uniswap WETH - DAI pool.

  1. Transfer liquidity from msg.sender
  2. Approve liquidity to router
  3. Call removeLiqiuidity() on router

(back to top)

Uniswap V2 Flash Swap

Tokens in the pool can be borrowed as long as they are repaid in the same transaction plus fee on borrow.

This is called flash swap.

The contract inherit from IUniswapV2Callee

State Variables

  1. Address of tokens and the address of the factory
  2. Set WETH and factory interface then declare pair (IUniswapV2Pair)

Constructor

  1. Call getPair() on factory and store the result inside pair variable (which is a IUniswapV2Pair interface)

Funuction flashSwap

  1. Prepare data of bytes to send. This can be any data, as long as it is not empty Uniswap will trigger a flash swap. For this example, we encode WETH and msg.sender.
  2. Call swap()on pair. Find below swap() from IUniswapV2Pair
function swap(
    uint amount0Out,
    uint amount1Out,
    address to,
    bytes calldata data
) external;

amount0Out: Amount of token0 to withdraw from the pool => 0

amount1Out:Amount of token1 to withdraw from the pool => wethAmount

to: Recipient of tokens in the pool => address(this)

data: Data to send to uniswapV2Call => data

Function uniswapV2Call

This function is called by the DAI/WETH pair contract after we called pair.swap.

Immediately before the pool calls this function, the amount of tokens that we requested to borrow is sent. Inside this function, we write our custom code and then repay the borrowed amount plus some fees.

  1. Require that msg.sender is pair. Only pair contract should be able to call this function.
  2. Require sender is this contract. Initiator of the flash swap should be this contract.
  3. Decode data. Inside flashSwap we've encoded WETH and msg.sender.
  4. Once the data is decoded, we would write our custom code here (arbitrage). We only emitted events for this example
  5. Calculate total amount to repay
  6. Transfer fee amount of WETH from caller (about 0.3% fee, +1 to round up)
  7. Repay WETH to pair, amount borrowed plus fee

(back to top)

Forking mainnet

When we fork 🍴 the mainnet, we have the current state of the blockchain running locally on our system, including all contracts deployed on it and all transactions performed on it.

  1. Setup hardhat.config
  2. Find a whale on etherscan

hardhat.config.js

  networks: {
        hardhat: {
          forking: {
            url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`,
       },
     },
  }

Note: Replace the ${} component of the URL with your personal Alchemy API key.

.config

const DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
const DAI_WHALE = process.env.DAI_WHALE;

module.exports = {
  DAI,
  DAI_WHALE,
};

.env

ALCHEMY_API_KEY=...

Terminal 1

npx hardhat test --network localhost

Terminal 2

ALCHEMY_API_KEY=...
npx hardhat node --fork https://eth-mainnet.g.alchemy.com/v2/$ALCHEMY_API_KEY

(back to top)

Note

This contract assumes that token0 and token1 both have same decimals

Consider Uniswap trading fee = 0.3%

Further reading

(...soon)

Sources

(back to top)

Roadmap

  • Uniswap V3 TWAP
  • Further reading
  • Deploy script
  • Unit test

See the open issues for a full list of proposed features (and known issues).

(back to top)

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)

License

Distributed under the MIT License. See LICENSE.txt for more information.

(back to top)

Contact

Reda Aboutika - @twitter - reda.aboutika@gmail.com

Project Link: https://github.com/Aboudoc/Uniswap-v2.git

(back to top)

Acknowledgments

(back to top)