Uniswap-V2-Periphery 코드 공부 내용 정리

장원령·2021년 8월 17일
0

Solidity

목록 보기
3/4

https://github.com/Uniswap/uniswap-v2-periphery

UniSwap이 무엇인지, 그리고 Core코드에 관해서는 아래의 링크에 정리하였으니, 참고하면 될 것 같다.

https://velog.io/@wrjang96/Uniswap-Core

1. Periphery 코드의 기본적인 개념

: Periphery 파일의 구조는 다음과 같다.

: Periphery에는 Core 프로젝트에 있는 컨트랙트들을 보다 쉽게 사용할 수 있는 도우미(helper) 컨트랙트들이 포함되어 있다.
: Core 프로젝트의 함수들을 사용자가 직접 호출할 경우 사용자의 실수로 인해 보안적 위험에 노출되기 쉽기 때문에 일반적으로는 Periphery에 정의된 컨트랙트를 통해 거래소 기능을 사용하는 것이 권장됩니다.
: Periphery 안의 UniswapV2Router02 컨트랙트가 현재 유니스왑 V2 웹인터페이에서 사용되는 컨트랙트입니다.

: 여기서 크게 눈여겨봐야할 부분은 Library, Router이다.

2. Router contract

: library를 사용하는 Router Contract는 안전하게 풀에 유동성을 swap,add,remove하고 이에 필요한 안전 체크를 진행한다.
: 컨트랙트로 유니스왑과 상호작용하고 싶을 경우, router를 통하도록 작성한다.

  • 주의할 점
    : 함수를 Remix에서 실행해보면, 빨간색과 주황색으로 그냥 함수와 WETH를 사용하는 함수의 색깔이 다를텐데, 이는 payable 포함 유무가 색깔로 드러난 것이다.

* WETH란?

https://www.steemcoinpan.com/hive-101145/@donekim/defi-wrapped-ether-weth

: ETH는 ERC-20토큰이 아니기 때문에, 탈중앙화 플랫폼에서 ETH를 편리하게 사용하고자 하는 토큰이 WETH이다.

2.1 유동성 추가

addLiquidity

: ERC-20 ERC-20 pair pool간에 유동성을 부여한다.

// **** ADD LIQUIDITY ****
    function _addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin
    ) internal virtual returns (uint amountA, uint amountB) {
        // create the pair if it doesn't exist yet
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
       // address(0) 은 생성되지 않은 경우, 즉 pair가 없을 때 를 의미한다. 
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }
        (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }
 function addLiquidity(
        address tokenA, // 풀토큰
        address tokenB, // 풀토큰 
        uint amountADesired, // amountBDesired/amountADesired 일때 더할 토큰 A의 유동성양
        uint amountBDesired,// amountADesired/amountBDesired 일때 더할 토큰 B의 유동성양
        uint amountAMin,
        uint amountBMin,
        // 유동성 풀에 넣을 최소의 토큰의 양 
        address to, // 받을 사람 
        uint deadline // 트랜잭션이 취소되기까지의 timestamp 시간 
    ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { // 앞서 선언한 데드라인 넘는지 확인 
        (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); // pair의 주소를 계산하는 함수 
        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
        liquidity = IUniswapV2Pair(pair).mint(to);// 유동성 생성 
    }

addLiquidityETH

: WETH가 관련된 유동성 추가

function addLiquidityETH(
        address token,
        uint amountTokenDesired,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
        (amountToken, amountETH) = _addLiquidity(
            token,
            WETH,
            amountTokenDesired,
            msg.value,
            amountTokenMin,
            amountETHMin
        );
        address pair = UniswapV2Library.pairFor(factory, token, WETH);
        TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
        IWETH(WETH).deposit{value: amountETH}();
        assert(IWETH(WETH).transfer(pair, amountETH));
        liquidity = IUniswapV2Pair(pair).mint(to);
        // refund dust eth, if any
        if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
    }

2.2 유동성 제거

removeLiquidity

// **** REMOVE LIQUIDITY ****
    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
        (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
        (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
        require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
        require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
    }

: 여기서 주목할 부분들은 다음과 같다.

(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);

: 이 부분의 코드는 앞서 우리가 넣었던 유동성 토큰을, 다시 꺼내고자 할때, 그 절대적인 양이 같지 않기 때문에 비율로 따져서 계산해야 한다. 그 계산된 값을 받아오는 것이다.

require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');

: docs에는 잘 설명이 되어있지 않은데, require의 경우에는 기존의 if문과는 달리 에러가 발생했을시 앞서 진행한 모든 과정들을 백지화 하는 특징을 지닌다. 즉, 이 코드에서는 '내가 지정한 최소 인출량보다 토큰이 작을 경우에, 지금까지 진행해온 모든 과정을 백지화 한다'라는 의미가 한 문장에 압축되어있는 것이라고 보면 된다.

removeLiquidityETH

function removeLiquidityETH(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
        (amountToken, amountETH) = removeLiquidity(
            token,
            WETH,
            liquidity,
            amountTokenMin,
            amountETHMin,
            address(this),
            deadline
        );
        TransferHelper.safeTransfer(token, to, amountToken);
        IWETH(WETH).withdraw(amountETH);
        TransferHelper.safeTransferETH(to, amountETH);
    }

2.3 유동성 교환

swapExactTokensForTokens

 function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
        amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        TransferHelper.safeTransferFrom(
            path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
        );
        _swap(amounts, path, to);
    }

swapExactETHForTokens

function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        payable
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
        amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
        require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
        IWETH(WETH).deposit{value: amounts[0]}();
        assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
        _swap(amounts, path, to);
    }

3. Library contract

: periphery에서 편리하게 데이터를 가져오고 가격을 구성하기 위한 기능들을 제공한다.

1개의 댓글

comment-user-thumbnail
2022년 3월 7일

addLiquidity 함수에서
amountBDesired/amountADesired 일때 더할 토큰 A의 유동성양 이라고 하셨는데
amountBDesired/amountADesired 일때 라는게 어떤 의미인지 이해가 되지 않습니다

답글 달기