[Ethereum] 아비트라지를 위한 여정 13편 - OOP & Factory

0xDave·2022년 11월 27일
0

Ethereum

목록 보기
64/112

여러 토큰들이나 풀을 모니터링 할 때 일일이 코드를 만들어주는 것은 시간도 많이 들고 비효율적이다. 따라서 토큰같은 경우 ERC20 Class를 만들어서 토큰 주소만 입력해주면 봇을 만들 수 있도록 짜놓는 것이 좋다.

소스코드


class Erc20Token:
    """
    Represents an ERC-20 token. Must be initialized with an address.
    Brownie will load the Contract object from the supplied ABI if given, 
    then attempt to load the verified ABI from the block explorer.
    If both methods fail, it will attempt to use a supplied ERC-20 ABI
    """

    def __init__(
        self,
        address: str,
        user: network.account.LocalAccount,
        abi: list = None,
        oracle_address: str = None,
    ) -> None:
        self.address = address
        self._user = user
        if abi:
            try:
                self._contract = Contract.from_abi(
                    name="", address=self.address, abi=abi
                )
            except:
                raise
        else:
            try:
                self._contract = Contract.from_explorer(self.address)
            except:
                self._contract = Contract.from_abi(
                    name="", address=self.address, abi=ERC20
                )
        self.name = self._contract.name.call()
        self.symbol = self._contract.symbol.call()
        self.decimals = self._contract.decimals.call()
        self.balance = self._contract.balanceOf.call(self._user)
        self.normalized_balance = self.balance / (10 ** self.decimals)
        if oracle_address:
            self._price_oracle = ChainlinkPriceContract(address=oracle_address)
            self.price = self._price_oracle.price
        print(f"• {self.symbol} ({self.name})")

    def _str__(self):
        return self.symbol

    def get_approval(self, external_address: str):
        return self._contract.allowance.call(self._user.address, external_address)

    def set_approval(self, external_address: str, value: int):
        if value == "unlimited":
            value = 2 ** 256 - 1

        try:
            self._contract.approve(
                external_address,
                value,
                {"from": self._user.address},
            )
        except Exception as e:
            print(f"Exception in token_approve: {e}")
            raise

    def update_balance(self):
        self.balance = self._contract.balanceOf.call(self._user)
        self.normalized_balance = self.balance / (10 ** self.decimals)

    def update_price(self):
        self.price = self._price_oracle.update_price()

__init__은 constructor와 같은 개념이다. 첫 파라미터로 self를 넣어주고 나머지 주소와 abi 등을 받아서 만들어진다. 이 때 -> None은 리턴 형태를 말한다. 나머지 메소드 부분에서도 self를 인자로 받는 것을 알 수 있는데 이는 파이썬 메소드의 특징으로 클래스로부터 만들어진 객체를 의미한다.

파이썬의 클래스와 메소드 부분의 자세한 설명은 점프 투 파이썬에 잘 나와있다.


객체 만들기


weth = Erc20Token(
    address='0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
    user=testAccount,
    abi=ERC20
    )

위와 같이 파라미터에 적절하게 값을 매칭해서 넣어주면 된다.


Factory


Factory는 DEX에서 pair pool을 만들어주는 역할을 한다. 따라서 우리가 어떤 토큰으로 pool을 만들거나 유동성을 공급할 때 Factory 컨트랙트를 사용한다. 우리는 이걸 역이용해서 아비트라지를 시도 할 토큰 쌍을 검색하거나 불러올 때 사용할 것이다.

각 DEX별로 Factory의 컨트랙트는 다르다. Trader Joe의 경우, 0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10이다. 현재 Trader Joe는 Liquidity Book으로 프로젝트를 업그레이드 하고 있기 때문에 나중에 컨트랙트가 바뀔 수 있으니 주의하자.


$ brownie console --network avax-main

>>> tj_factory = Contract.from_explorer('0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10')
>>> tj_factory.allPairsLength()
17287

현재 pair의 개수는 17287개다.


>>> tj_factory.getPair('0x3Ee97d514BBef95a2f110e6B9b73824719030f7a','0xCE1bFFBD5374Dac86a2893119683F4911a2F7814')
'0x033C3Fc1fC13F803A233D262e24d1ec3fd4EFB48'

getPair로 sSPELL-SPELL 풀의 컨트랙트 주소를 알 수 있다. 인자로 넘겨주는 토큰 컨트랙트의 주수는 앞 뒤가 바뀌어도 상관없다.


>>> tj_factory.getPair('0x3Ee97d514BBef95a2f110e6B9b73824719030f7a','0x1db749847c4abb991d8b6032102383e6bfd9b1c7')
'0x0000000000000000000000000000000000000000'

그런데 가끔 뜬금없는 토큰 주소를 넣으면 pair가 없을 수 있다. 따라서 이런 경우를 예외처리 해줘야 한다.


for id in range(tj_factory.allPairsLength()):
    _ = Contract.from_explorer(tj_factory.allPairs(id), silent=True)
    print(f"POOL {id}: {_.token0()} - {_.token1()}\n")

요런 식으로 풀의 모든 데이터를 가져올 수도 있다.


출처 및 참고자료


  1. The Great Object-Oriented Rewrite
  2. 05-1 클래스
profile
Just BUIDL :)

0개의 댓글