[Ethereum] 아비트라지를 위한 여정 5편

0xDave·2022년 10월 1일
0

Ethereum

목록 보기
21/112

⚙️ 세팅


>>> router = Contract.from_explorer('0x60aE616a2155Ee3d9A68541Ba4544862310933d4')
>>> mim = Contract.from_explorer('0x130966628846bfd36ff31a822705796e8cb8c18d')
>>> dai = Contract.from_explorer('0xd586e7f844cea2f87f50152665bcbc2c279d8d70')
>>> mim.balanceOf(user.address)
0
>>> dai.balanceOf(user.address)
0

각 토큰 컨트랙트 주소를 불러오고, user가 토큰을 얼마나 가지고 있는지 확인해본다.


>>> mim.allowance(user.address, router.address)
0
>>> dai.allowance(user.address, router.address)
0

이제 user가 router에 어느정도의 규모로 권한을 위임했는지 확인한다. 현재는 어떤 권한도 위임하지 않았기 때문에 기본값인 0으로 나온다. Router를 통해 자동으로 스왑하려면 해당 토큰을 스왑할 수 있는 권한을 모두 Router에 넘겨야 한다. 유니스왑이 현재 다른 dApp에 비해 안전하다고는 하지만 언제 어떤 일이 터질지는 아무도 모른다. 아무리 신뢰하는 dApp이라도 권한을 위임할 때는 항상 주의할 필요가 있다.


🧲 swapAVAXForExactTokens()


유니스왑에 있는 swapETHForExactTokens() 함수는 이더리움을 다른 ERC-20 토큰으로 정확한 양에 맞게 바꿔주는 역할을 한다. 아발란체 라우터에도 이와 같은 역할을 하는 swapAVAXForExactTokens() 함수가 있다.


    function swapAVAXForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    )
        external
        payable
        virtual
        override
        ensure(deadline)
        returns (uint256[] memory amounts)
    {
        require(path[0] == WAVAX, "JoeRouter: INVALID_PATH");
        amounts = JoeLibrary.getAmountsIn(factory, amountOut, path);
        require(amounts[0] <= msg.value, "JoeRouter: EXCESSIVE_INPUT_AMOUNT");
        IWAVAX(WAVAX).deposit{value: amounts[0]}();
        assert(
            IWAVAX(WAVAX).transfer(
                JoeLibrary.pairFor(factory, path[0], path[1]),
                amounts[0]
            )
        );
        _swap(amounts, path, to);
        // refund dust eth, if any
        if (msg.value > amounts[0])
            TransferHelper.safeTransferAVAX(msg.sender, msg.value - amounts[0]);
    }

인자로는 다음과 같다.

  1. 스왑을 통해 받고 싶은 토큰의 양
  2. 팔고싶은 토큰, 사고싶은 토큰으로 페어를 이룬 리스트
  3. 구매한 토큰을 어디로 전송할 지
  4. 스왑 마감기한

실제로 AVAX를 팔아서 5개의 MIM을 산다고 했을 때 코드는 아래처럼 하면 된다. 마감기한은 time.time()으로 현재 시간을 가져오고 30초를 더해서 설정했는데, 이대로 하면 UNIX 상 밀리초로 인식돼서 1000을 곱해줘야 한다.(그 전에 int()를 써서 정수로 만들어줘야 함) 만약 30초 동안 스왑이 진행되지 않는다면 해당 스왑은 라우터에 의해 revert 될 것이다.


>>> router.swapAVAXForExactTokens(5*(10**mim.decimals()), [wavax.address, mim.address], user.address, 1000*int(time.time()+30), {'from':user.address, 'value':0.3*10**18})

마지막에 인자에는 없는 from, value 값이 들어가는데, 어디서 토큰이 왔고 스왑할 토큰의 양은 어느정도인지 Brownie에 알려주는 용도라고 한다. 함수에 포함되지 않은 인자가 왜 필요한지 아직까지는 잘 모르겠다.. 현재 아발란체 시세가 1개에 $16.79이기 때문에 5개의 MIM을 구매하려면 0.3개 정도가 필요하다.

시험삼아 1달러로 바꿔봤는데 성공!!


✨ Approve()


이제 MIM을 DAI로 스왑하기 위해 approve를 해보자. 아까 추가로 더 스왑해서 5 MIM이 있는 상황이다. 시험삼아 그 반절인 2.5 MIM만 approve 해보자.

>>> mim.approve(router.address, 2.5*10**mim.decimals(), {'from':user.address})

얼마나 approve 했는지 나오진 않지만 성공!


💸 MIM -> DAI 스왑


이제 본격적으로 approve 한 만큼 스왑을 해보자. 우리가 사용할 함수는 swapExactTokensForTokens()이다.

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

콘솔에는 다음과 같이 입력하면 된다.

>>> router.swapExactTokensForTokens(2.5*10**mim.decimals(), 2.5*10**dai.decimals(), [mim.address, dai.address ], user.address, 1000*int(time.time()+30), {'from':user.address})

하지만 에러가 난다. 그 이유는 amountOutMin 값 즉, 최소로 받을 DAI 양을 2.5로 설정했기 때문. 이렇게 하면 2.5개의 MIM을 2.5개의 DAI로 바꿔줘라는 얘긴데 이건 MIM과 DAI의 비율이 정확히 1:1 혹은 그 이상이 되어야 한다.(1:1.1 등등) 따라서 현재 거래 비율이 1:1을 만족시키지 못해서 revert가 난 것.


>>>router.swapExactTokensForTokens(2.5*10**mim.decimals(), 0.95*2.5*10**dai.decimals(), [mim.address, dai.address], user.address, 1000*int(time.time()+30), {'from':user.address})

따라서 좀 더 여유있게 1:0.95비율로 스왑을 해보면

1:0.96의 비율로 스왑 한 것을 볼 수 있다.

profile
Just BUIDL :)

0개의 댓글