[Ethereum] 아비트라지를 위한 여정 10편 - 스왑 시 얻을 수 있는 양 구하기

0xDave·2022년 11월 6일
0

Ethereum

목록 보기
56/112

✨ getReserves()


>>> lp = Contract.from_explorer('0x033c3fc1fc13f803a233d262e24d1ec3fd4efb48')
>>> lp.token0()
'0x3Ee97d514BBef95a2f110e6B9b73824719030f7a'
>>> lp.token1()
'0xCE1bFFBD5374Dac86a2893119683F4911a2F7814'


>>> Contract.from_explorer(lp.token0()).symbol()
'sSPELL'
>>> Contract.from_explorer(lp.token1()).symbol()
'SPELL'

sSPELL과 SPELL의 LP 풀을 가져왔다.


    function getReserves()
        public
        view
        returns (
            uint112 _reserve0,
            uint112 _reserve1,
            uint32 _blockTimestampLast
        )
    {
        _reserve0 = reserve0;
        _reserve1 = reserve1;
        _blockTimestampLast = blockTimestampLast;
    }

trader Joe LP 풀의 컨트랙트에는 getReserves()라는 함수가 있는데 유니스왑V2에서 가져온 것 같다.


time을 import하고 가장 마지막 리턴 값을 빼면 풀이 변동 되고나서 얼마나 지났는지 알 수 있다. 초 단위로 표시되기 때문에 현재 약 33시간 정도 지난 것을 알 수 있다.(짜게 식은 spell..)

>>> lp.getReserves()
(1353632461902232159063440, 1852835505753373256745497, 1667635790)
>>> import time
>>> int(time.time()) - lp.getReserves()[-1]
120280

아래와 같은 방법으로 풀에 해당 토큰이 얼마나 있는지 알 수 있다.

>>> x0, y0 = lp.getReserves()[0:2]
>>> x0
1353632461902232159063440
>>> y0
1852835505753373256745497

📚 용어 정리



📐 dy 값 알아내기 (dx 예치시)


dx만큼의 토큰(token0)을 풀을 예치할 때 dy만큼 토큰(token1)을 얻을 수 있다. X * Y = K 공식을 조금만 이용하면 된다.

x0y0 = (x0 + dx(1-fee))*(y0 - dy)

예치량인 dx에 수수료를 곱해준다. 이 식을 정리하면 dy 값을 구할 수 있다.

dy = y0dx(1-fee) / (x0 + dx*(1-fee))


🔢 dx 값 알아내기 (dy 예치시)


반대의 경우도 같은 방법으로 구할 수 있다.

x0y0 = (x0 - dx)(y0 + dy*(1-fee))

이 식을 정리하면 dy 값이 나온다.

dx = x0dy(1-fee) / (y0 + dy*(1-fee))


🧑‍💻 수식 -> 코드로 구현해보자


def get_tokens_out_from_tokens_in(
    pool_reserves_token0,
    pool_reserves_token1,
    quantity_token0_in=0,
    quantity_token1_in=0,
    fee=0,
):
    # fails if two input tokens are passed, or if both are 0
    assert not (quantity_token0_in and quantity_token1_in)
    assert quantity_token0_in or quantity_token1_in

    if quantity_token0_in:
        return (pool_reserves_token1 * quantity_token0_in * (1 - fee)) // (
            pool_reserves_token0 + quantity_token0_in * (1 - fee)
        )

    if quantity_token1_in:
        return (pool_reserves_token0 * quantity_token1_in * (1 - fee)) // (
            pool_reserves_token1 + quantity_token1_in * (1 - fee)
        )

🤖 소스코드


import sys
import time
import os
from brownie import *
from decimal import Decimal  

# Change to match your explorer API key
SNOWTRACE_API_KEY = "XXX"

# Contract addresses
TRADERJOE_ROUTER_CONTRACT_ADDRESS = "0x60aE616a2155Ee3d9A68541Ba4544862310933d4"
TOKEN_POOL_CONTRACT_ADDRESS = "0x033C3Fc1fC13F803A233D262e24d1ec3fd4EFB48"

os.environ["SNOWTRACE_TOKEN"] = SNOWTRACE_API_KEY

def get_tokens_out_from_tokens_in(
    pool_reserves_token0,
    pool_reserves_token1,
    quantity_token0_in=0,
    quantity_token1_in=0,
    fee=0,
):
    # fails if two input tokens are passed, or if both are 0
    assert not (quantity_token0_in and quantity_token1_in)
    assert quantity_token0_in or quantity_token1_in

    if quantity_token0_in:
        return (pool_reserves_token1 * quantity_token0_in * (1 - fee)) // (
            pool_reserves_token0 + quantity_token0_in * (1 - fee)
        )

    if quantity_token1_in:
        return (pool_reserves_token0 * quantity_token1_in * (1 - fee)) // (
            pool_reserves_token1 + quantity_token1_in * (1 - fee)
        )

def contract_load(address, alias):
    # Attempts to load the saved contract by alias.
    # If not found, fetch from network explorer and set alias.
    try:
        contract = Contract(alias)
    except ValueError:
        contract = Contract.from_explorer(address)
        contract.set_alias(alias)
    finally:
        print(f"• {alias}")
        return contract

def get_swap_rate(token_in_quantity, token_in_address, token_out_address, contract):
    try:
        return contract.getAmountsOut(
            token_in_quantity, [token_in_address, token_out_address]
        )
    except Exception as e:
        print(f"Exception in get_swap_rate: {e}")
        return False

try:
    network.connect("avax-main")
except:
    sys.exit(
        "Could not connect to Avalanche! Verify that brownie lists the Avalanche Mainnet using 'brownie networks list'"
    )

print("\nContracts loaded:")
lp = contract_load(TOKEN_POOL_CONTRACT_ADDRESS, "TraderJoe LP: SPELL-sSPELL")
router = contract_load(TRADERJOE_ROUTER_CONTRACT_ADDRESS, "TraderJoe: Router")

token0 = Contract.from_explorer(lp.token0.call())
token1 = Contract.from_explorer(lp.token1.call())

print()
print(f"token0 = {token0.symbol.call()}")
print(f"token1 = {token1.symbol.call()}")

print()
print("*** Getting Pool Reserves *** ")
x0, y0 = lp.getReserves.call()[0:2]
print(f"token0: \t\t\t{x0}")
print(f"token1: \t\t\t{y0}")

print()
print("*** Calculating hypothetical swap: 500,000 SPELL to sSPELL @ 0.3% fee ***")
quote = router.getAmountsOut(500_000 * (10 ** 18), [token1.address, token0.address])[-1]
tokens_out = get_tokens_out_from_tokens_in(
    pool_reserves_token0=x0,
    pool_reserves_token1=y0,
    quantity_token1_in=500_000 * (10 ** 18),
    fee=Decimal("0.003"),
)
print()
print(f"Calculated Tokens Out: \t\t{tokens_out}")
print(f"Router Quoted getAmountsOut: \t{quote}")
print(f"Difference: \t\t\t{quote - tokens_out}")

print()
print("*** Calculating hypothetical swap: 500,000 sSPELL to SPELL @ 0.3% fee ***")
quote = router.getAmountsOut(
    500_000 * (10 ** 18),
    [token0.address, token1.address],
)[-1]
tokens_out = get_tokens_out_from_tokens_in(
    pool_reserves_token0=x0,
    pool_reserves_token1=y0,
    quantity_token0_in=500_000 * (10 ** 18),
    fee=Decimal("0.003"),
)
print()
print(f"Calculated Tokens Out: \t\t{tokens_out}")
print(f"Router Quoted getAmountsOut: \t{quote}")
print(f"Difference: \t\t\t{quote - tokens_out}")
profile
Just BUIDL :)

0개의 댓글