Exemple #1
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='Mooniswap-market-maker-keeper')
        self.add_arguments(parser=parser)
        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        self.mooniswap_factory = MooniFactory(
            web3=self.web3,
            factory_address=Address(self.arguments.mooniswap_factory_address))
        first_token_address = Address(self.arguments.first_token_address)
        second_token_address = Address(self.arguments.second_token_address)
        if first_token_address == Address(
                "0x0000000000000000000000000000000000000000"):
            self.first_token = EthToken(web3=self.web3,
                                        address=first_token_address)
        else:
            self.first_token = ERC20Token(
                web3=self.web3,
                address=Address(self.arguments.first_token_address))
        if second_token_address == Address(
                "0x0000000000000000000000000000000000000000"):
            self.second_token = EthToken(web3=self.web3,
                                         address=second_token_address)
        else:
            self.second_token = ERC20Token(
                web3=self.web3,
                address=Address(self.arguments.second_token_address))

        self.referral = Address(self.arguments.mooniswap_referral_address)

        self.token_first = Token(name=self.arguments.first_token_name,
                                 address=Address(
                                     self.arguments.first_token_address),
                                 decimals=self.arguments.first_token_decimals)

        self.token_second = Token(
            name=self.arguments.second_token_name,
            address=Address(self.arguments.second_token_address),
            decimals=self.arguments.second_token_decimals)

        self.mooniswap = self.mooniswap_factory.get_pair(
            first_token=self.token_first.address,
            second_token=self.token_second.address)

        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)

        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.max_delta_on_percent = self.arguments.max_delta_on_percent

        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser("chief-keeper")
        self.add_arguments(parser)
        parser.set_defaults(cageFacilitated=False)
        self.arguments = parser.parse_args(args)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        if self.arguments.dss_deployment_file:
            self.dss = DssDeployment.from_json(
                web3=self.web3,
                conf=open(self.arguments.dss_deployment_file, "r").read())
        else:
            self.dss = DssDeployment.from_node(web3=self.web3)

        self.deployment_block = self.arguments.chief_deployment_block

        self.max_errors = self.arguments.max_errors
        self.errors = 0

        self.confirmations = 0

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
Exemple #3
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='etherdelta-market-maker-keeper')

        self.add_arguments(parser=parser)

        parser.set_defaults(cancel_on_shutdown=False,
                            withdraw_on_shutdown=False)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.eth_reserve = Wad.from_number(self.arguments.eth_reserve)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.min_eth_deposit = Wad.from_number(self.arguments.min_eth_deposit)
        self.min_sai_deposit = Wad.from_number(self.arguments.min_sai_deposit)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments, self.tub)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        if self.eth_reserve <= self.min_eth_balance:
            raise Exception(
                "--eth-reserve must be higher than --min-eth-balance")

        assert (self.arguments.order_expiry_threshold >= 0)
        assert (self.arguments.order_no_cancel_threshold >=
                self.arguments.order_expiry_threshold)

        self.history = History()
        self.etherdelta = EtherDelta(web3=self.web3,
                                     address=Address(
                                         self.arguments.etherdelta_address))
        self.etherdelta_api = EtherDeltaApi(
            client_tool_directory="lib/pymaker/utils/etherdelta-client",
            client_tool_command="node main.js",
            api_server=self.arguments.etherdelta_socket,
            number_of_attempts=self.arguments.etherdelta_number_of_attempts,
            retry_interval=self.arguments.etherdelta_retry_interval,
            timeout=self.arguments.etherdelta_timeout)

        self.our_orders = list()
Exemple #4
0
    def __init__(self, arguments: Namespace, pyex_api: PyexAPI):

        setup_logging(arguments)

        if arguments.__contains__('web3'):
            self.web3 = arguments.web3
        else:
            web3_endpoint = f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}"
            web3_options = {"timeout": self.arguments.rpc_timeout}
            self.web3 = Web3(HTTPProvider(endpoint_uri=web3_endpoint, request_kwargs=web3_options))

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.bands_config = ReloadableConfig(arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(arguments)
        self.spread_feed = create_spread_feed(arguments)
        self.control_feed = create_control_feed(arguments)

        self.order_history_reporter = create_order_history_reporter(arguments)

        self.history = History()

        self.init_order_book_manager(arguments, pyex_api)
Exemple #5
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='oasis-market-maker-cancel')
        parser.add_argument("--endpoint-uri", type=str,
                            help="JSON-RPC uri (example: `http://localhost:8545`)")
        parser.add_argument("--rpc-host", default="localhost", type=str, help="[DEPRECATED] JSON-RPC host (default: `localhost')")
        parser.add_argument("--rpc-port", default=8545, type=int, help="[DEPRECATED] JSON-RPC port (default: `8545')")
        parser.add_argument("--rpc-timeout", help="JSON-RPC timeout (in seconds, default: 10)", default=10, type=int)
        parser.add_argument("--eth-from", help="Ethereum account from which to send transactions", required=True, type=str)
        parser.add_argument("--eth-key", type=str, nargs='*', help="Ethereum private key(s) to use")
        parser.add_argument("--oasis-address", help="Ethereum address of the OasisDEX contract", required=True, type=str)
        parser.add_argument("--gas-price", help="Gas price in Wei (default: node default)", default=0, type=int)
        self.arguments = parser.parse_args(args)

        if 'web3' in kwargs:
            self.web3 = kwargs['web3']
        elif self.arguments.endpoint_uri:
            self.web3: Web3 = web3_via_http(self.arguments.endpoint_uri, self.arguments.rpc_timeout)
        else:
            self.web3 = Web3(HTTPProvider(endpoint_uri=f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                                          request_kwargs={"timeout": self.arguments.rpc_timeout}))

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)
        self.otc = MatchingMarket(web3=self.web3, address=Address(self.arguments.oasis_address))

        logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.INFO)
Exemple #6
0
    def __init__(self, args, **kwargs):
        parser = argparse.ArgumentParser("dsrdemo")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help=
            "Ethereum address from which to send transactions; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument(
            "--network",
            type=str,
            required=True,
            help=
            "Network that you're running the Keeper on (options, 'mainnet', 'kovan', 'testnet')"
        )

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=/path/to/keystore.json,pass_file=/path/to/passphrase.txt')"
        )
        self.arguments = parser.parse_args(args)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"https://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        # Instantiate the dss and dsr classes
        self.dss = DssDeployment.from_network(web3=self.web3,
                                              network=self.arguments.network)
        self.dsr = Dsr(self.dss, self.our_address)
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='paradex-market-maker-keeper')
        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.pair = self.arguments.pair.upper()
        self.token_buy = ERC20Token(web3=self.web3,
                                    address=Address(
                                        self.arguments.buy_token_address))
        self.token_sell = ERC20Token(web3=self.web3,
                                     address=Address(
                                         self.arguments.sell_token_address))
        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_max_decimals = None
        self.amount_max_decimals = None
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()
        self.zrx_exchange = ZrxExchangeV2(web3=self.web3,
                                          address=Address(
                                              self.arguments.exchange_address))
        self.paradex_api = ParadexApi(self.zrx_exchange,
                                      self.arguments.paradex_api_server,
                                      self.arguments.paradex_api_key,
                                      self.arguments.paradex_api_timeout)

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency, max_workers=1)
        self.order_book_manager.get_orders_with(
            lambda: self.paradex_api.get_orders(self.pair))
        self.order_book_manager.get_balances_with(lambda: self.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.paradex_api.cancel_order(order.order_id))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='bite-keeper')
        parser.add_argument("--rpc-host",
                            help="JSON-RPC host (default: `localhost')",
                            default="localhost",
                            type=str)
        parser.add_argument("--rpc-port",
                            help="JSON-RPC port (default: `8545')",
                            default=8545,
                            type=int)
        parser.add_argument("--rpc-timeout",
                            help="JSON-RPC timeout (in seconds, default: 10)",
                            default=10,
                            type=int)
        parser.add_argument(
            "--eth-from",
            help="Ethereum account from which to send transactions",
            required=True,
            type=str)
        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=/path/to/keystore.json,pass_file=/path/to/passphrase.txt')"
        )
        parser.add_argument("--tub-address",
                            help="Ethereum address of the Tub contract",
                            required=True,
                            type=str)
        parser.add_argument("--debug",
                            help="Enable debug output",
                            dest='debug',
                            action='store_true')
        self.arguments = parser.parse_args(args)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"https://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)
        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.vox = Vox(web3=self.web3, address=self.tub.vox())

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
Exemple #9
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='0x-market-maker-keeper')
        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.eth_address = Address(self.arguments.eth_token_address)

        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.bands_config = ReloadableConfig(self.arguments.config)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()

        # Delegate 0x specific init to a function to permit overload for 0xv3
        self.zrx_exchange = None
        self.zrx_relayer_api = None
        self.zrx_api = None
        self.pair = None
        self.init_zrx()

        self.placed_zrx_orders = []
        self.placed_zrx_orders_lock = Lock()

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(lambda: self.get_orders())
        self.order_book_manager.get_balances_with(lambda: self.get_balances())
        self.order_book_manager.place_orders_with(self.place_order_function)
        self.order_book_manager.cancel_orders_with(self.cancel_order_function)
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
Exemple #10
0
def web3():
    # These details are specific to the MCD testchain used for pymaker unit tests.
    web3 = Web3(HTTPProvider("http://0.0.0.0:8545"))
    web3.eth.defaultAccount = "0x50FF810797f75f6bfbf2227442e0c961a8562F4C"
    register_keys(web3,
                  ["key_file=lib/pymaker/tests/config/keys/UnlimitedChain/key1.json,pass_file=/dev/null",
                   "key_file=lib/pymaker/tests/config/keys/UnlimitedChain/key2.json,pass_file=/dev/null",
                   "key_file=lib/pymaker/tests/config/keys/UnlimitedChain/key3.json,pass_file=/dev/null",
                   "key_file=lib/pymaker/tests/config/keys/UnlimitedChain/key4.json,pass_file=/dev/null",
                   "key_file=lib/pymaker/tests/config/keys/UnlimitedChain/key.json,pass_file=/dev/null"])

    # reduce logspew
    logging.getLogger("web3").setLevel(logging.INFO)
    logging.getLogger("urllib3").setLevel(logging.INFO)
    logging.getLogger("asyncio").setLevel(logging.INFO)

    return web3
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='idex-market-maker-keeper')
        self.add_arguments(parser=parser)

        parser.set_defaults(cancel_on_shutdown=False,
                            withdraw_on_shutdown=False)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.eth_reserve = Wad.from_number(self.arguments.eth_reserve)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.min_eth_deposit = Wad.from_number(self.arguments.min_eth_deposit)
        self.min_sai_deposit = Wad.from_number(self.arguments.min_sai_deposit)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments, self.tub)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        if self.eth_reserve <= self.min_eth_balance:
            raise Exception(
                "--eth-reserve must be higher than --min-eth-balance")

        self.history = History()
        self.idex = IDEX(self.web3, Address(self.arguments.idex_address))
        self.idex_api = IDEXApi(self.idex, self.arguments.idex_api_server,
                                self.arguments.idex_timeout)
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='airswap-market-maker-keeper')

        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.airswap_api = AirswapApi(self.arguments.airswap_api_server,
                                      self.arguments.airswap_api_timeout)

        if self.arguments.buy_token_address == '0x0000000000000000000000000000000000000000':
            self.token_buy = EthToken(web3=self.web3,
                                      address=Address(
                                          self.arguments.buy_token_address))
        else:
            self.token_buy = ERC20Token(web3=self.web3,
                                        address=Address(
                                            self.arguments.buy_token_address))

        self.eth_token_sell = EthToken(
            web3=self.web3,
            address=Address(self.arguments.eth_sell_token_address))
        self.weth_token_sell = ERC20Token(
            web3=self.web3,
            address=Address(self.arguments.weth_sell_token_address))

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()
Exemple #13
0
def web3() -> Web3:
    # for local dockerized parity testchain
    web3 = Web3(HTTPProvider("http://0.0.0.0:8545"))
    web3.eth.defaultAccount = "0x50FF810797f75f6bfbf2227442e0c961a8562F4C"
    register_keys(web3,
                  ["key_file=tests/config/keys/UnlimitedChain/key1.json,pass_file=/dev/null",
                   "key_file=tests/config/keys/UnlimitedChain/key2.json,pass_file=/dev/null",
                   "key_file=tests/config/keys/UnlimitedChain/key3.json,pass_file=/dev/null",
                   "key_file=tests/config/keys/UnlimitedChain/key4.json,pass_file=/dev/null",
                   "key_file=tests/config/keys/UnlimitedChain/key.json,pass_file=/dev/null"])

    # reduce logspew
    logging.getLogger("web3").setLevel(logging.INFO)
    logging.getLogger("urllib3").setLevel(logging.INFO)
    logging.getLogger("asyncio").setLevel(logging.INFO)

    assert len(web3.eth.accounts) > 3
    return web3
Exemple #14
0
    def __init__(self, args: list, **kwargs):
        """Pass in arguements assign necessary variables/objects and instantiate other Classes"""

        parser = argparse.ArgumentParser("cage-keeper")
        self.add_arguments(parser=parser)

        parser.set_defaults(cageFacilitated=False)
        self.arguments = parser.parse_args(args)

        # Configure connection to the chain
        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})

        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        if self.arguments.dss_deployment_file:
            self.dss = DssDeployment.from_json(
                web3=self.web3,
                conf=open(self.arguments.dss_deployment_file, "r").read())
        else:
            self.dss = DssDeployment.from_node(web3=self.web3)

        self.deployment_block = self.arguments.vat_deployment_block

        self.max_errors = self.arguments.max_errors
        self.errors = 0

        self.cageFacilitated = self.arguments.cageFacilitated

        self.confirmations = 0

        # Create gas strategy
        if self.arguments.ethgasstation_api_key:
            self.gas_price = DynamicGasPrice(self.arguments)
        else:
            self.gas_price = DefaultGasPrice()

        setup_logging(self.arguments)
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser("chief-keeper")
        self.add_arguments(parser)
        parser.set_defaults(cageFacilitated=False)
        self.arguments = parser.parse_args(args)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        if self.arguments.dss_deployment_file:
            self.dss = DssDeployment.from_json(
                web3=self.web3,
                conf=open(self.arguments.dss_deployment_file, "r").read())
        else:
            self.dss = DssDeployment.from_node(web3=self.web3)

        self.deployment_block = self.arguments.chief_deployment_block

        self.max_errors = self.arguments.max_errors
        self.errors = 0

        self.confirmations = 0

        if self.arguments.fixed_gas_price is not None and self.arguments.fixed_gas_price > 0:
            self.gas_price_strategy = FixedGasPrice(
                gas_price=int(round(self.arguments.fixed_gas_price *
                                    self.GWEI)))
        else:
            self.gas_price_strategy = DefaultGasPrice()

        setup_logging(self.arguments)
Exemple #16
0
    def __init__(self, args: list, **kwargs):
        """Pass in arguements assign necessary variables/objects and instantiate other Classes"""

        parser = argparse.ArgumentParser("chief-keeper")

        parser.add_argument(
            "--rpc-host",
            type=str,
            default="https://localhost:8545",
            help="JSON-RPC host:port (default: 'localhost:8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--network",
            type=str,
            required=True,
            help=
            "Network that you're running the Keeper on (options, 'mainnet', 'kovan', 'testnet')"
        )

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help=
            "Ethereum address from which to send transactions; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=/path/to/keystore.json,pass_file=/path/to/passphrase.txt')"
        )

        parser.add_argument(
            "--dss-deployment-file",
            type=str,
            required=False,
            help=
            "Json description of all the system addresses (e.g. /Full/Path/To/configFile.json)"
        )

        parser.add_argument(
            "--chief-deployment-block",
            type=int,
            required=False,
            default=0,
            help=
            " Block that the Chief from dss-deployment-file was deployed at (e.g. 8836668"
        )

        parser.add_argument(
            "--max-errors",
            type=int,
            default=100,
            help=
            "Maximum number of allowed errors before the keeper terminates (default: 100)"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        parser.add_argument("--ethgasstation-api-key",
                            type=str,
                            default=None,
                            help="ethgasstation API key")
        parser.add_argument("--gas-initial-multiplier",
                            type=str,
                            default=1.0,
                            help="ethgasstation API key")
        parser.add_argument("--gas-reactive-multiplier",
                            type=str,
                            default=2.25,
                            help="gas strategy tuning")
        parser.add_argument("--gas-maximum",
                            type=str,
                            default=5000,
                            help="gas strategy tuning")

        parser.set_defaults(cageFacilitated=False)
        self.arguments = parser.parse_args(args)

        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else web3_via_http(
            endpoint_uri=self.arguments.rpc_host,
            timeout=self.arguments.rpc_timeout,
            http_pool_size=100)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        if self.arguments.dss_deployment_file:
            self.dss = DssDeployment.from_json(
                web3=self.web3,
                conf=open(self.arguments.dss_deployment_file, "r").read())
        else:
            self.dss = DssDeployment.from_network(
                web3=self.web3, network=self.arguments.network)

        self.deployment_block = self.arguments.chief_deployment_block

        self.max_errors = self.arguments.max_errors
        self.errors = 0

        self.confirmations = 0

        # Create dynamic gas strategy
        if self.arguments.ethgasstation_api_key:
            self.gas_price = DynamicGasPrice(self.arguments, self.web3)
        else:
            self.gas_price = DefaultGasPrice()

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
Exemple #17
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='ddex-market-maker-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument(
            "--exchange-address",
            type=str,
            required=True,
            help="Ethereum address of the 0x Exchange contract")

        parser.add_argument(
            "--ddex-api-server",
            type=str,
            default='https://api.ddex.io',
            help="Address of the Ddex API (default: 'https://api.ddex.io')")

        parser.add_argument(
            "--ddex-api-timeout",
            type=float,
            default=9.5,
            help="Timeout for accessing the Ddex API (in seconds, default: 9.5)"
        )

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--buy-token-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the buy token")

        parser.add_argument("--sell-token-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the sell token")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument("--gas-price",
                            type=int,
                            default=0,
                            help="Gas price (in Wei)")

        parser.add_argument(
            "--smart-gas-price",
            dest='smart_gas_price',
            action='store_true',
            help=
            "Use smart gas pricing strategy, based on the ethgasstation.info feed"
        )

        parser.add_argument(
            "--refresh-frequency",
            type=int,
            default=3,
            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        if self.arguments.pair != 'USStocks-DAI':
            self.pair = self.arguments.pair.upper()
        else:
            self.pair = self.arguments.pair

        self.token_buy = ERC20Token(web3=self.web3,
                                    address=Address(
                                        self.arguments.buy_token_address))
        self.token_sell = ERC20Token(web3=self.web3,
                                     address=Address(
                                         self.arguments.sell_token_address))
        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_max_decimals = None
        self.amount_max_decimals = None
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()
        self.zrx_exchange = ZrxExchange(web3=self.web3,
                                        address=Address(
                                            self.arguments.exchange_address))
        self.ddex_api = DdexApi(self.web3, self.arguments.ddex_api_server,
                                self.arguments.ddex_api_timeout)

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency, max_workers=1)
        self.order_book_manager.get_orders_with(
            lambda: self.ddex_api.get_orders(self.pair))
        self.order_book_manager.cancel_orders_with(
            lambda order: self.ddex_api.cancel_order(order.order_id))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='mpx-market-maker-keeper')
        self.add_arguments(parser=parser)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)

        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.token_buy = ERC20Token(web3=self.web3,
                                    address=Address(
                                        self.arguments.buy_token_address))
        self.token_sell = ERC20Token(web3=self.web3,
                                     address=Address(
                                         self.arguments.sell_token_address))

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_max_decimals = None
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()

        self.zrx_exchange = ZrxExchangeV2(web3=self.web3,
                                          address=Address(
                                              self.arguments.exchange_address))
        self.mpx_api = MpxApi(api_server=self.arguments.mpx_api_server,
                              zrx_exchange=self.zrx_exchange,
                              fee_recipient=Address(
                                  self.arguments.fee_address),
                              timeout=self.arguments.mpx_api_timeout,
                              our_address=self.arguments.eth_from)

        self.zrx_relayer_api = ZrxRelayerApiV2(
            exchange=self.zrx_exchange,
            api_server=self.arguments.mpx_api_server)
        self.zrx_api = ZrxApiV2(zrx_exchange=self.zrx_exchange,
                                zrx_api=self.zrx_relayer_api)

        markets = self.mpx_api.get_markets()['data']
        market = next(
            filter(
                lambda item: item['attributes']['pair-name'] == self.arguments.
                pair, markets))

        self.pair = MpxPair(self.arguments.pair, self.token_buy.address,
                            int(market['attributes']['base-token-decimals']),
                            self.token_sell.address,
                            int(market['attributes']['quote-token-decimals']))

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(lambda: self.get_orders())
        self.order_book_manager.get_balances_with(lambda: self.get_balances())
        self.order_book_manager.cancel_orders_with(self.cancel_order_function)
        self.order_book_manager.place_orders_with(self.place_order_function)
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
Exemple #19
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='auction-keeper')

        parser.add_argument(
            "--rpc-host",
            type=str,
            default="http://localhost:8545",
            help=
            "JSON-RPC endpoint URI with port (default: `http://localhost:8545')"
        )
        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")
        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument('--type',
                            type=str,
                            choices=['clip', 'flip', 'flap', 'flop'],
                            help="Auction type in which to participate")
        parser.add_argument(
            '--ilk',
            type=str,
            help=
            "Name of the collateral type for a clip or flip keeper (e.g. 'ETH-B', 'ZRX-A'); "
            "available collateral types can be found at the left side of the Oasis Borrow"
        )

        parser.add_argument(
            '--bid-only',
            dest='create_auctions',
            action='store_false',
            help="Do not take opportunities to create new auctions")
        parser.add_argument('--kick-only',
                            dest='bid_on_auctions',
                            action='store_false',
                            help="Do not bid on auctions")
        parser.add_argument(
            '--deal-for',
            type=str,
            nargs="+",
            help="List of addresses for which auctions will be dealt")

        parser.add_argument('--min-auction',
                            type=int,
                            default=1,
                            help="Lowest auction id to consider")
        parser.add_argument(
            '--max-auctions',
            type=int,
            default=1000,
            help="Maximum number of auctions to simultaneously interact with, "
            "used to manage OS and hardware limitations")
        parser.add_argument(
            '--min-collateral-lot',
            type=float,
            default=0,
            help=
            "Minimum lot size to create or bid upon/take from a collateral auction"
        )
        parser.add_argument(
            '--bid-check-interval',
            type=float,
            default=4.0,
            help=
            "Period of timer [in seconds] used to check bidding models for changes"
        )
        parser.add_argument(
            '--bid-delay',
            type=float,
            default=0.0,
            help=
            "Seconds to wait between bids, used to manage OS and hardware limitations"
        )
        parser.add_argument(
            '--shard-id',
            type=int,
            default=0,
            help=
            "When sharding auctions across multiple keepers, this identifies the shard"
        )
        parser.add_argument(
            '--shards',
            type=int,
            default=1,
            help=
            "Number of shards; should be one greater than your highest --shard-id"
        )

        parser.add_argument(
            '--from-block',
            type=int,
            help=
            "Starting block from which to find vaults to bite or debt to queue "
            "(set to block where MCD was deployed)")
        parser.add_argument(
            '--chunk-size',
            type=int,
            default=20000,
            help=
            "When batching chain history requests, this is the number of blocks for each request"
        )
        parser.add_argument(
            "--tokenflow-url",
            type=str,
            help=
            "When specified, urn history will be initialized using the TokenFlow API"
        )
        parser.add_argument("--tokenflow-key",
                            type=str,
                            help="API key for the TokenFlow endpoint")
        parser.add_argument(
            "--vulcanize-endpoint",
            type=str,
            help=
            "When specified, urn history will be initialized from a VulcanizeDB node"
        )
        parser.add_argument("--vulcanize-key",
                            type=str,
                            help="API key for the Vulcanize endpoint")

        parser.add_argument(
            '--vat-dai-target',
            type=str,
            help=
            "Amount of Dai to keep in the Vat contract or ALL to join entire token balance"
        )
        parser.add_argument(
            '--keep-dai-in-vat-on-exit',
            dest='exit_dai_on_shutdown',
            action='store_false',
            help=
            "Retain Dai in the Vat on exit, saving gas when restarting the keeper"
        )
        parser.add_argument('--keep-gem-in-vat-on-exit',
                            dest='exit_gem_on_shutdown',
                            action='store_false',
                            help="Retain collateral in the Vat on exit")
        parser.add_argument(
            '--return-gem-interval',
            type=int,
            default=300,
            help=
            "Period of timer [in seconds] used to check and exit won collateral"
        )

        parser.add_argument(
            "--model",
            type=str,
            nargs='+',
            help="Commandline to use in order to start the bidding model")

        parser.add_argument(
            "--oracle-gas-price",
            action='store_true',
            help="Use a fast gas price aggregated across multiple oracles")
        parser.add_argument("--ethgasstation-api-key",
                            type=str,
                            default=None,
                            help="EthGasStation API key")
        parser.add_argument("--etherscan-api-key",
                            type=str,
                            default=None,
                            help="Etherscan API key")
        parser.add_argument("--blocknative-api-key",
                            type=str,
                            default=None,
                            help="Blocknative API key")
        parser.add_argument(
            '--fixed-gas-price',
            type=float,
            default=None,
            help=
            "Uses a fixed value (in Gwei) instead of an external API to determine initial gas"
        )
        parser.add_argument("--poanetwork-url",
                            type=str,
                            default=None,
                            help="Alternative POANetwork URL")
        parser.add_argument(
            "--gas-initial-multiplier",
            type=float,
            default=1.0,
            help=
            "Adjusts the initial API-provided 'fast' gas price, default 1.0")
        parser.add_argument(
            "--gas-reactive-multiplier",
            type=float,
            default=1.125,
            help=
            "Increases gas price when transactions haven't been mined after some time"
        )
        parser.add_argument(
            "--gas-maximum",
            type=float,
            default=2000,
            help=
            "Places an upper bound (in Gwei) on the amount of gas to use for a single TX"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        # Configure connection to the chain
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else web3_via_http(
            endpoint_uri=self.arguments.rpc_host,
            timeout=self.arguments.rpc_timeout,
            http_pool_size=100)
        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        # Check configuration for retrieving urns/bites
        if self.arguments.type in ['clip', 'flip'] and self.arguments.create_auctions \
                and self.arguments.from_block is None \
                and self.arguments.tokenflow_url is None \
                and self.arguments.vulcanize_endpoint is None:
            raise RuntimeError(
                "One of --from-block, --tokenflow_url, or --vulcanize-endpoint must be specified "
                "to bite and kick off new collateral auctions")
        if self.arguments.type in ['clip', 'flip'] and not self.arguments.ilk:
            raise RuntimeError(
                "--ilk must be supplied when configuring a collateral auction keeper"
            )
        if self.arguments.type == 'flop' and self.arguments.create_auctions \
                and self.arguments.from_block is None:
            raise RuntimeError(
                "--from-block must be specified to kick off flop auctions")

        # Configure core and token contracts
        self.mcd = DssDeployment.from_node(web3=self.web3)
        self.vat = self.mcd.vat
        self.vow = self.mcd.vow
        self.mkr = self.mcd.mkr
        self.dai_join = self.mcd.dai_adapter
        if self.arguments.type in ['clip', 'flip']:
            self.collateral = self.mcd.collaterals[self.arguments.ilk]
            self.ilk = self.collateral.ilk
            self.gem_join = self.collateral.adapter
        else:
            self.collateral = None
            self.ilk = None
            self.gem_join = None

        # Configure auction contracts
        self.auction_contract = self.get_contract()
        self.auction_type = None
        is_collateral_auction = False
        self.is_dealable = True
        self.urn_history = None

        if isinstance(self.auction_contract, Clipper):
            self.auction_type = 'clip'
            is_collateral_auction = True
            self.min_collateral_lot = Wad.from_number(
                self.arguments.min_collateral_lot)
            self.is_dealable = False
            self.strategy = ClipperStrategy(self.auction_contract,
                                            self.min_collateral_lot)
        elif isinstance(self.auction_contract, Flipper):
            self.auction_type = 'flip'
            is_collateral_auction = True
            self.min_collateral_lot = Wad.from_number(
                self.arguments.min_collateral_lot)
            self.strategy = FlipperStrategy(self.auction_contract,
                                            self.min_collateral_lot)
        elif isinstance(self.auction_contract, Flapper):
            self.auction_type = 'flap'
            self.strategy = FlapperStrategy(self.auction_contract,
                                            self.mkr.address)
        elif isinstance(self.auction_contract, Flopper):
            self.auction_type = 'flop'
            self.strategy = FlopperStrategy(self.auction_contract)
        else:
            raise RuntimeError(
                f"{self.auction_contract} auction contract is not supported")

        if is_collateral_auction and self.arguments.create_auctions:
            if self.arguments.vulcanize_endpoint:
                self.urn_history = VulcanizeUrnHistoryProvider(
                    self.mcd, self.ilk, self.arguments.vulcanize_endpoint,
                    self.arguments.vulcanize_key)
            elif self.arguments.tokenflow_url:
                self.urn_history = TokenFlowUrnHistoryProvider(
                    self.web3, self.mcd, self.ilk,
                    self.arguments.tokenflow_url, self.arguments.tokenflow_key,
                    self.arguments.chunk_size)
            else:
                self.urn_history = ChainUrnHistoryProvider(
                    self.web3, self.mcd, self.ilk, self.arguments.from_block,
                    self.arguments.chunk_size)

        # Create the collection used to manage auctions relevant to this keeper
        if self.arguments.model:
            model_command = ' '.join(self.arguments.model)
        else:
            if self.arguments.bid_on_auctions:
                raise RuntimeError(
                    "--model must be specified to bid on auctions")
            else:
                model_command = ":"
        self.auctions = Auctions(auction_contract=self.auction_contract,
                                 model_factory=ModelFactory(model_command))
        self.auctions_lock = threading.Lock()
        # Since we don't want periodically-pollled bidding threads to back up, use a flag instead of a lock.
        self.is_joining_dai = False
        self.dead_since = {}
        self.lifecycle = None

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))

        # Create gas strategy used for non-bids and bids which do not supply gas price
        self.gas_price = DynamicGasPrice(self.arguments, self.web3)

        # Configure account(s) for which we'll deal auctions
        self.deal_all = False
        self.deal_for = set()
        if self.is_dealable:
            if self.arguments.deal_for is None:
                self.deal_for.add(self.our_address)
            elif len(
                    self.arguments.deal_for
            ) == 1 and self.arguments.deal_for[0].upper() in ["ALL", "NONE"]:
                if self.arguments.deal_for[0].upper() == "ALL":
                    self.deal_all = True
                # else no auctions will be dealt
            elif len(self.arguments.deal_for) > 0:
                for account in self.arguments.deal_for:
                    self.deal_for.add(Address(account))

        # reduce logspew
        logging.getLogger('urllib3').setLevel(logging.INFO)
        logging.getLogger("web3").setLevel(logging.INFO)
        logging.getLogger("asyncio").setLevel(logging.INFO)
        logging.getLogger("requests").setLevel(logging.INFO)
Exemple #20
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='idex-market-maker-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument("--tub-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the Tub contract")

        parser.add_argument("--idex-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the IDEX contract")

        parser.add_argument(
            "--idex-api-server",
            type=str,
            default='https://api.idex.market',
            help=
            "Address of the IDEX API server (default: 'https://api.idex.market')"
        )

        parser.add_argument(
            "--idex-timeout",
            type=float,
            default=9.5,
            help="Timeout for accessing the IDEX API (in seconds, default: 9.5)"
        )

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument(
            "--eth-reserve",
            type=float,
            required=True,
            help=
            "Amount of ETH which will never be deposited so the keeper can cover gas"
        )

        parser.add_argument(
            "--min-eth-balance",
            type=float,
            default=0,
            help="Minimum ETH balance below which keeper will cease operation")

        parser.add_argument(
            "--min-eth-deposit",
            type=float,
            required=True,
            help=
            "Minimum amount of ETH that can be deposited in one transaction")

        parser.add_argument(
            "--min-sai-deposit",
            type=float,
            required=True,
            help=
            "Minimum amount of SAI that can be deposited in one transaction")

        parser.add_argument("--gas-price",
                            type=int,
                            default=0,
                            help="Gas price (in Wei)")

        parser.add_argument(
            "--smart-gas-price",
            dest='smart_gas_price',
            action='store_true',
            help=
            "Use smart gas pricing strategy, based on the ethgasstation.info feed"
        )

        parser.add_argument("--ethgasstation-api-key",
                            type=str,
                            default=None,
                            help="ethgasstation API key")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        parser.set_defaults(cancel_on_shutdown=False,
                            withdraw_on_shutdown=False)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.eth_reserve = Wad.from_number(self.arguments.eth_reserve)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.min_eth_deposit = Wad.from_number(self.arguments.min_eth_deposit)
        self.min_sai_deposit = Wad.from_number(self.arguments.min_sai_deposit)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments, self.tub)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        if self.eth_reserve <= self.min_eth_balance:
            raise Exception(
                "--eth-reserve must be higher than --min-eth-balance")

        self.history = History()
        self.idex = IDEX(self.web3, Address(self.arguments.idex_address))
        self.idex_api = IDEXApi(self.idex, self.arguments.idex_api_server,
                                self.arguments.idex_timeout)
Exemple #21
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='auction-keeper')

        parser.add_argument(
            "--rpc-host",
            type=str,
            default="http://localhost:8545",
            help=
            "JSON-RPC endpoint URI with port (default: `http://localhost:8545')"
        )
        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")
        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument('--type',
                            type=str,
                            choices=['flip', 'flap', 'flop'],
                            help="Auction type in which to participate")
        parser.add_argument(
            '--ilk',
            type=str,
            help=
            "Name of the collateral type for a flip keeper (e.g. 'ETH-B', 'ZRX-A'); "
            "available collateral types can be found at the left side of the CDP Portal"
        )

        parser.add_argument(
            '--bid-only',
            dest='create_auctions',
            action='store_false',
            help="Do not take opportunities to create new auctions")
        parser.add_argument('--kick-only',
                            dest='bid_on_auctions',
                            action='store_false',
                            help="Do not bid on auctions")
        parser.add_argument(
            '--deal-for',
            type=str,
            nargs="+",
            help="List of addresses for which auctions will be dealt")

        parser.add_argument('--min-auction',
                            type=int,
                            default=1,
                            help="Lowest auction id to consider")
        parser.add_argument(
            '--max-auctions',
            type=int,
            default=1000,
            help="Maximum number of auctions to simultaneously interact with, "
            "used to manage OS and hardware limitations")
        parser.add_argument(
            '--min-flip-lot',
            type=float,
            default=0,
            help="Minimum lot size to create or bid upon a flip auction")
        parser.add_argument(
            '--bid-check-interval',
            type=float,
            default=2.0,
            help=
            "Period of timer [in seconds] used to check bidding models for changes"
        )
        parser.add_argument(
            '--bid-delay',
            type=float,
            default=0.0,
            help=
            "Seconds to wait between bids, used to manage OS and hardware limitations"
        )
        parser.add_argument(
            '--shard-id',
            type=int,
            default=0,
            help=
            "When sharding auctions across multiple keepers, this identifies the shard"
        )
        parser.add_argument(
            '--shards',
            type=int,
            default=1,
            help=
            "Number of shards; should be one greater than your highest --shard-id"
        )

        parser.add_argument(
            "--vulcanize-endpoint",
            type=str,
            help=
            "When specified, frob history will be queried from a VulcanizeDB lite node, "
            "reducing load on the Ethereum node for flip auctions")
        parser.add_argument(
            '--from-block',
            type=int,
            help=
            "Starting block from which to find vaults to bite or debt to queue "
            "(set to block where MCD was deployed)")

        parser.add_argument(
            '--vat-dai-target',
            type=float,
            help="Amount of Dai to keep in the Vat contract (e.g. 2000)")
        parser.add_argument(
            '--keep-dai-in-vat-on-exit',
            dest='exit_dai_on_shutdown',
            action='store_false',
            help=
            "Retain Dai in the Vat on exit, saving gas when restarting the keeper"
        )
        parser.add_argument('--keep-gem-in-vat-on-exit',
                            dest='exit_gem_on_shutdown',
                            action='store_false',
                            help="Retain collateral in the Vat on exit")

        parser.add_argument(
            "--model",
            type=str,
            required=True,
            nargs='+',
            help="Commandline to use in order to start the bidding model")
        parser.add_argument(
            "--max-gem-balance",
            type=float,
            required=True,
            help=
            "Max gem (e.g. ETH, BAT) balance to store in keeper account before selling for DAI"
        )
        parser.add_argument(
            "--max-gem-sale",
            type=float,
            required=True,
            help=
            "Max gem (e.g. ETH, BAT) to sell in a single transaction in order to reduce risk of slippage"
        )
        parser.add_argument("--gem-eth-ratio",
                            type=float,
                            default=1,
                            help="gem/eth ratio for estimating gas price")
        parser.add_argument(
            "--profit-margin",
            type=float,
            default=0.01,
            help="Minimum percent discount from feed price for bidding")
        parser.add_argument(
            "--tab-discount",
            type=float,
            nargs=4,
            help=
            "Enables adaptive profit margins based on the amount of total Dai currently being auctioned. Useful for detecting when large CDPs or rapid declines in ETH price have occurred. Profit margins will be added to base profit margin.  Usage = tab level 1 in dai, discount 1, tab level 2 in dai, discount 2",
            default=[100000, .15, 25000, .01])
        parser.add_argument(
            "--bid-start-time",
            type=float,
            default=30,
            help="Number of minutes before auction end time to start bidding")
        parser.add_argument(
            "--force-premium",
            dest='force_premium',
            action='store_true',
            help=
            "Use to enable bidding with negative margins (i.e. above the market feed rate)"
        )
        gas_group = parser.add_mutually_exclusive_group()
        gas_group.add_argument("--ethgasstation-api-key",
                               type=str,
                               default=None,
                               help="ethgasstation API key")
        gas_group.add_argument('--etherchain-gas-price',
                               dest='etherchain_gas',
                               action='store_true',
                               help="Use etherchain.org gas price")
        gas_group.add_argument('--poanetwork-gas-price',
                               dest='poanetwork_gas',
                               action='store_true',
                               help="Use POANetwork gas price")
        gas_group.add_argument(
            '--fixed-gas-price',
            type=float,
            default=None,
            help=
            "Uses a fixed value (in Gwei) instead of an external API to determine initial gas"
        )
        parser.add_argument("--poanetwork-url",
                            type=str,
                            default=None,
                            help="Alternative POANetwork URL")
        parser.add_argument(
            "--gas-initial-multiplier",
            type=float,
            default=1.0,
            help=
            "Adjusts the initial API-provided 'fast' gas price, default 1.0")
        parser.add_argument(
            "--gas-reactive-multiplier",
            type=float,
            default=2.25,
            help=
            "Increases gas price when transactions haven't been mined after some time"
        )
        parser.add_argument(
            "--gas-maximum",
            type=float,
            default=5000,
            help=
            "Places an upper bound (in Gwei) on the amount of gas to use for a single TX"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        # Configure connection to the chain
        provider = HTTPProvider(
            endpoint_uri=self.arguments.rpc_host,
            request_kwargs={'timeout': self.arguments.rpc_timeout})
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            provider)
        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        # Check configuration for retrieving urns/bites
        if self.arguments.type == 'flip' and self.arguments.create_auctions \
                and self.arguments.from_block is None and self.arguments.vulcanize_endpoint is None:
            raise RuntimeError(
                "Either --from-block or --vulcanize-endpoint must be specified to kick off "
                "flip auctions")
        if self.arguments.type == 'flip' and not self.arguments.ilk:
            raise RuntimeError(
                "--ilk must be supplied when configuring a flip keeper")
        if self.arguments.type == 'flop' and self.arguments.create_auctions \
                and self.arguments.from_block is None:
            raise RuntimeError(
                "--from-block must be specified to kick off flop auctions")

        if self.arguments.type != 'flip':
            raise RuntimeError("Thrifty Keeper only works with Flip auctions")

        if self.arguments.profit_margin < 0 and not self.arguments.force_premium:
            raise RuntimeError(
                "Negative profit margins will place bids above market price.  Run with '--force-premium' to override"
            )

        # Configure core and token contracts
        self.mcd = DssDeployment.from_node(web3=self.web3)
        self.vat = self.mcd.vat
        self.cat = self.mcd.cat
        self.vow = self.mcd.vow
        self.mkr = self.mcd.mkr
        self.dai_join = self.mcd.dai_adapter
        if self.arguments.type == 'flip':
            self.collateral = self.mcd.collaterals[self.arguments.ilk]
            self.ilk = self.collateral.ilk
            self.gem_join = self.collateral.adapter
        else:
            self.collateral = None
            self.ilk = None
            self.gem_join = None

        # Configure auction contracts
        self.flipper = self.collateral.flipper if self.arguments.type == 'flip' else None
        self.flapper = self.mcd.flapper if self.arguments.type == 'flap' else None
        self.flopper = self.mcd.flopper if self.arguments.type == 'flop' else None
        self.urn_history = None
        if self.flipper:
            self.min_flip_lot = Wad.from_number(self.arguments.min_flip_lot)
            self.strategy = FlipperStrategy(self.flipper, self.min_flip_lot)
            self.urn_history = UrnHistory(self.web3, self.mcd, self.ilk,
                                          self.arguments.from_block,
                                          self.arguments.vulcanize_endpoint)
        elif self.flapper:
            self.strategy = FlapperStrategy(self.flapper, self.mkr.address)
        elif self.flopper:
            self.strategy = FlopperStrategy(self.flopper)
        else:
            raise RuntimeError("Please specify auction type")

        # Create the collection used to manage auctions relevant to this keeper
        self.auctions = Auctions(
            flipper=self.flipper.address if self.flipper else None,
            flapper=self.flapper.address if self.flapper else None,
            flopper=self.flopper.address if self.flopper else None,
            model_factory=ModelFactory(' '.join(self.arguments.model)))
        self.auctions_lock = threading.Lock()
        self.dead_since = {}
        self.lifecycle = None

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))

        # Create gas strategy used for non-bids and bids which do not supply gas price
        self.gas_price = DynamicGasPrice(self.arguments)

        self.vat_dai_target = Wad.from_number(self.arguments.vat_dai_target) if \
            self.arguments.vat_dai_target is not None else None

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))

        ###Thrifty Keeper
        self.balance_manager = Balance_Manager(
            self.our_address, self.web3, self.mcd, self.ilk, self.gem_join,
            self.vat_dai_target, self.arguments.max_gem_balance,
            self.arguments.max_gem_sale, self.arguments.gem_eth_ratio,
            self.arguments.profit_margin, self.arguments.tab_discount,
            self.arguments.bid_start_time)

        # Configure account(s) for which we'll deal auctions
        self.deal_all = False
        self.deal_for = set()
        if self.arguments.deal_for is None:
            self.deal_for.add(self.our_address)
        elif len(
                self.arguments.deal_for
        ) == 1 and self.arguments.deal_for[0].upper() in ["ALL", "NONE"]:
            if self.arguments.deal_for[0].upper() == "ALL":
                self.deal_all = True
            # else no auctions will be dealt
        elif len(self.arguments.deal_for) > 0:
            for account in self.arguments.deal_for:
                self.deal_for.add(Address(account))

        # reduce logspew
        logging.getLogger('urllib3').setLevel(logging.INFO)
        logging.getLogger("web3").setLevel(logging.INFO)
        logging.getLogger("asyncio").setLevel(logging.INFO)
        logging.getLogger("requests").setLevel(logging.INFO)
    def __init__(self, args, **kwargs):
        parser = argparse.ArgumentParser("arbitrage-keeper")

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument("--tub-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the Tub contract")

        parser.add_argument("--tap-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the Tap contract")

        parser.add_argument(
            "--exchange-address",
            type=str,
            help="Ethereum address of the 0x Exchange contract")

        parser.add_argument("--oasis-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the OasisDEX contract")

        parser.add_argument(
            "--oasis-support-address",
            type=str,
            required=False,
            help="Ethereum address of the OasisDEX support contract")

        parser.add_argument("--relayer-api-server",
                            type=str,
                            help="Address of the 0x Relayer API")

        parser.add_argument(
            "--relayer-per-page",
            type=int,
            default=100,
            help=
            "Number of orders to fetch per one page from the 0x Relayer API (default: 100)"
        )

        parser.add_argument(
            "--tx-manager",
            type=str,
            help=
            "Ethereum address of the TxManager contract to use for multi-step arbitrage"
        )

        parser.add_argument("--gas-price",
                            type=int,
                            default=0,
                            help="Gas price in Wei (default: node default)")

        parser.add_argument(
            "--base-token",
            type=str,
            required=True,
            help="The token all arbitrage sequences will start and end with")

        parser.add_argument(
            "--min-profit",
            type=float,
            required=True,
            help="Minimum profit (in base token) from one arbitrage operation")

        parser.add_argument(
            "--max-engagement",
            type=float,
            required=True,
            help="Maximum engagement (in base token) in one arbitrage operation"
        )

        parser.add_argument(
            "--max-errors",
            type=int,
            default=100,
            help=
            "Maximum number of allowed errors before the keeper terminates (default: 100)"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.tap = Tap(web3=self.web3,
                       address=Address(self.arguments.tap_address))
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.skr = ERC20Token(web3=self.web3, address=self.tub.skr())

        self.zrx_exchange = ZrxExchange(web3=self.web3, address=Address(self.arguments.exchange_address)) \
            if self.arguments.exchange_address is not None else None
        self.zrx_relayer_api = ZrxRelayerApi(exchange=self.zrx_exchange, api_server=self.arguments.relayer_api_server) \
            if self.arguments.relayer_api_server is not None else None

        self.otc = MatchingMarket(
            web3=self.web3,
            address=Address(self.arguments.oasis_address),
            support_address=Address(self.arguments.oasis_support_address)
            if self.arguments.oasis_support_address is not None else None)

        self.base_token = ERC20Token(web3=self.web3,
                                     address=Address(
                                         self.arguments.base_token))
        self.min_profit = Wad.from_number(self.arguments.min_profit)
        self.max_engagement = Wad.from_number(self.arguments.max_engagement)
        self.max_errors = self.arguments.max_errors
        self.errors = 0

        if self.arguments.tx_manager:
            self.tx_manager = TxManager(web3=self.web3,
                                        address=Address(
                                            self.arguments.tx_manager))
            if self.tx_manager.owner() != self.our_address:
                raise Exception(
                    f"The TxManager has to be owned by the address the keeper is operating from."
                )
        else:
            self.tx_manager = None

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
Exemple #23
0
print(web3.clientVersion)

"""
Purpose: Tests pymaker on chains or layer-2s where multi-collateral Dai is not deployed.

Argument:           Reqd?   Example:
Ethereum node URI   yes     https://localhost:8545
Ethereum address    no      0x0000000000000000000000000000000aBcdef123
Private key         no      key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
Gas tip (GWEI)      no      9
"""


if len(sys.argv) > 3:
    web3.eth.defaultAccount = sys.argv[2]
    register_keys(web3, [sys.argv[3]])
    our_address = Address(web3.eth.defaultAccount)
    run_transactions = True
elif len(sys.argv) > 2:
    our_address = Address(sys.argv[2])
    run_transactions = False
else:
    our_address = None
    run_transactions = False

gas_strategy = DefaultGasPrice() if len(sys.argv) <= 4 else \
    GeometricGasPrice(web3=web3,
                      initial_price=None,
                      initial_tip=int(float(sys.argv[4]) * GeometricGasPrice.GWEI),
                      every_secs=5,
                      max_price=50 * GeometricGasPrice.GWEI)
import os
import sys
import time
from web3 import Web3, HTTPProvider

from pymaker import Address
from pymaker.deployment import DssDeployment
from pymaker.keys import register_keys
from pymaker.model import Token
from pymaker.numeric import Wad, Ray
from tests.conftest import create_risky_cdp, is_cdp_safe


web3 = Web3(HTTPProvider(endpoint_uri=os.environ['ETH_RPC_URL'], request_kwargs={"timeout": 30}))
web3.eth.defaultAccount = sys.argv[1]   # ex: 0x0000000000000000000000000000000aBcdef123
register_keys(web3, [sys.argv[2]])      # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass

logging.basicConfig(format='%(asctime)-15s %(levelname)-8s %(message)s', level=logging.DEBUG)
# reduce logspew
logging.getLogger('urllib3').setLevel(logging.INFO)
logging.getLogger("web3").setLevel(logging.INFO)
logging.getLogger("asyncio").setLevel(logging.INFO)
logging.getLogger("requests").setLevel(logging.INFO)

mcd = DssDeployment.from_node(web3)
our_address = Address(web3.eth.defaultAccount)
collateral = mcd.collaterals[str(sys.argv[3])] if len(sys.argv) > 2 else mcd.collaterals['ETH-A']
ilk = mcd.vat.ilk(collateral.ilk.name)
urn = mcd.vat.urn(collateral.ilk, our_address)

    def __init__(self, args, **kwargs):
        """Pass in arguements assign necessary variables/objects and instantiate other Classes"""

        parser = argparse.ArgumentParser("simple-arbitrage-keeper")

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help=
            "Ethereum address from which to send transactions; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            required=True,
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=/path/to/keystore.json,pass_file=/path/to/passphrase.txt')"
        )

        parser.add_argument(
            "--uniswap-entry-exchange",
            type=str,
            required=True,
            help=
            "Ethereum address of the Uniswap Exchange contract for the entry token market; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--uniswap-arb-exchange",
            type=str,
            required=True,
            help=
            "Ethereum address of the Uniswap Exchange contract for the arb token market; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--oasis-address",
            type=str,
            required=True,
            help=
            "Ethereum address of the OasisDEX contract; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--oasis-api-endpoint",
            type=str,
            required=True,
            help=
            "Endpoint of of the Oasis V2 REST API (e.g. 'https://kovan-api.oasisdex.com' )"
        )

        parser.add_argument(
            "--relayer-per-page",
            type=int,
            default=100,
            help=
            "Number of orders to fetch per one page from the 0x Relayer API (default: 100)"
        )

        parser.add_argument(
            "--tx-manager",
            type=str,
            required=True,
            help=
            "Ethereum address of the TxManager contract to use for multi-step arbitrage; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--gas-price",
            type=int,
            default=0,
            help=
            "Gas price in Wei (default: node default), (e.g. 1000000000 for 1 GWei)"
        )

        parser.add_argument(
            "--entry-token",
            type=str,
            required=True,
            help=
            "The token address that the bot starts and ends with in every transaction; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--arb-token",
            type=str,
            required=True,
            help=
            "The token address that arbitraged between both exchanges; checksummed (e.g. '0x12AebC')"
        )

        parser.add_argument(
            "--arb-token-name",
            type=str,
            required=True,
            help=
            "The token name that arbitraged between both exchanges (e.g. 'SAI', 'WETH', 'REP')"
        )

        parser.add_argument(
            "--min-profit",
            type=int,
            required=True,
            help=
            "Ether amount of minimum profit (in base token) from one arbitrage operation (e.g. 1 for 1 Sai min profit)"
        )

        parser.add_argument(
            "--max-engagement",
            type=int,
            required=True,
            help=
            "Ether amount of maximum engagement (in base token) in one arbitrage operation (e.g. 100 for 100 Sai max engagement)"
        )

        parser.add_argument(
            "--max-errors",
            type=int,
            default=100,
            help=
            "Maximum number of allowed errors before the keeper terminates (default: 100)"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"https://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        self.sai = ERC20Token(
            web3=self.web3,
            address=Address(
                '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359'))  # Mainnet Sai
        self.dai = ERC20Token(
            web3=self.web3,
            address=Address(
                '0x6b175474e89094c44da98b954eedeac495271d0f'))  # Mainnet Dai

        self.ksai = ERC20Token(
            web3=self.web3,
            address=Address(
                '0xC4375B7De8af5a38a93548eb8453a498222C4fF2'))  #Kovan Sai
        self.kdai = ERC20Token(
            web3=self.web3,
            address=Address(
                '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa'))  #Kovan Dai

        self.entry_token = ERC20Token(web3=self.web3,
                                      address=Address(
                                          self.arguments.entry_token))
        self.arb_token = ERC20Token(web3=self.web3,
                                    address=Address(self.arguments.arb_token))
        self.arb_token.name = self.arguments.arb_token_name \
            if self.arguments.arb_token_name != 'WETH' else 'ETH'


        self.uniswap_entry_exchange = UniswapWrapper(self.web3, self.entry_token.address, Address(self.arguments.uniswap_entry_exchange)) \
            if self.arguments.uniswap_entry_exchange is not None else None

        self.uniswap_arb_exchange = UniswapWrapper(self.web3, self.arb_token.address, Address(self.arguments.uniswap_arb_exchange)) \
            if self.arguments.uniswap_arb_exchange is not None else None

        self.oasis_api_endpoint = OasisAPI(api_server=self.arguments.oasis_api_endpoint,
                                           entry_token_name=self.token_name(self.entry_token.address),
                                           arb_token_name=self.arb_token.name) \
            if self.arguments.oasis_api_endpoint is not None else None

        self.oasis = MatchingMarket(web3=self.web3,
                                    address=Address(
                                        self.arguments.oasis_address))

        self.min_profit = Wad(int(self.arguments.min_profit * 10**18))
        self.max_engagement = Wad(int(self.arguments.max_engagement * 10**18))
        self.max_errors = self.arguments.max_errors
        self.errors = 0

        if self.arguments.tx_manager:
            self.tx_manager = TxManager(web3=self.web3,
                                        address=Address(
                                            self.arguments.tx_manager))
            if self.tx_manager.owner() != self.our_address:
                raise Exception(
                    f"The TxManager has to be owned by the address the keeper is operating from."
                )
        else:
            self.tx_manager = None

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='uniswap-market-maker-keeper')

        parser.add_argument(
            "--endpoint-uri",
            type=str,
            default="http://localhost:8545",
            help="JSON-RPC uri (default: `http://localhost:8545`)")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--token-config",
                            type=str,
                            required=True,
                            help="Token configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-accepted-delay",
            type=int,
            default=60,
            help=
            "Number of seconds the keeper will tolerate the price feed being null before removing liquidity"
        )

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the price feed (in seconds, default: 86400)")

        parser.add_argument(
            "--max-add-liquidity-slippage",
            type=int,
            default=2,
            help=
            "Maximum percentage off the desired amount of liquidity to add in add_liquidity()"
        )

        parser.add_argument(
            "--accepted-price-slippage-up",
            type=float,
            required=True,
            help=
            "Percentage difference between Uniswap exchange rate and aggregated price above which liquidity would be added"
        )

        parser.add_argument(
            "--accepted-price-slippage-down",
            type=float,
            required=True,
            help=
            "Percentage difference between Uniswap exchange rate and aggregated price below which liquidity would be added"
        )

        parser.add_argument("--target-a-min-balance",
                            type=float,
                            required=True,
                            help="Minimum balance of token A to maintain.")

        parser.add_argument("--target-a-max-balance",
                            type=float,
                            required=True,
                            help="Minimum balance of token A to maintain.")

        parser.add_argument("--target-b-min-balance",
                            type=float,
                            required=True,
                            help="Minimum balance of token B to maintain.")

        parser.add_argument("--target-b-max-balance",
                            type=float,
                            required=True,
                            help="Minimum balance of token B to maintain.")

        parser.add_argument(
            "--factory-address",
            type=str,
            default="0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
            help=
            "Address of the UniswapV2 Factory smart contract used to create new pools"
        )

        parser.add_argument(
            "--router-address",
            type=str,
            default="0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
            help=
            "Address of the UniswapV2 RouterV2 smart contract used to handle liquidity management"
        )

        parser.add_argument(
            "--initial-delay",
            type=int,
            default=10,
            help="Initial number of seconds to wait before placing liquidity")

        parser.add_argument(
            '--staking-rewards-name',
            type=StakingRewardsName,
            choices=StakingRewardsName,
            help="Name of contract to stake liquidity tokens with")

        parser.add_argument(
            "--staking-rewards-contract-address",
            type=str,
            help="Address of contract to stake liquidity tokens with")

        parser.add_argument(
            "--staking-rewards-target-reward-amount",
            type=float,
            help="Address of contract to stake liquidity tokens with")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        add_gas_arguments(parser)
        self.arguments = parser.parse_args(args)

        setup_logging(self.arguments)

        self.web3: Web3 = web3_via_http(self.arguments.endpoint_uri,
                                        self.arguments.rpc_timeout)
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.web3.eth.defaultAccount)
        if 'web3' not in kwargs:
            register_keys(self.web3, self.arguments.eth_key)

        self.gas_price = GasPriceFactory().create_gas_price(
            self.web3, self.arguments)

        # TODO: Add a more sophisticated regex for different variants of eth on the exchange
        # Record if eth is in pair, so can check which liquidity method needs to be used
        self.is_eth = 'ETH' in self.pair()

        # Identify which token is ETH, so we can provide the arguments to Uniswap Router in expected order
        self.eth_position = 1
        if self.is_eth:
            self.eth_position = 0 if self.pair().split('-')[0] == 'ETH' else 1

        self.reloadable_config = ReloadableConfig(self.arguments.token_config)
        self._last_config_dict = None
        self._last_config = None
        self.token_config = self.get_token_config().token_config

        self.token_a, self.token_b = self.instantiate_tokens(self.pair())

        self.uniswap = UniswapV2(self.web3, self.token_a, self.token_b,
                                 self.our_address,
                                 Address(self.arguments.router_address),
                                 Address(self.arguments.factory_address))

        # instantiate specific StakingRewards depending on arguments
        self.staking_rewards = StakingRewardsFactory().create_staking_rewards(
            self.arguments, self.web3)
        self.staking_rewards_target_reward_amount = self.arguments.staking_rewards_target_reward_amount

        # configure price feed
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.price_feed_accepted_delay = self.arguments.price_feed_accepted_delay
        self.control_feed = create_control_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.feed_price_null_counter = 0

        # testing_feed_price is used by the integration tests in tests/test_uniswapv2.py, to test different pricing scenarios
        # as the keeper consistently checks the price, some long running state variable is needed to
        self.testing_feed_price = False
        self.test_price = Wad.from_number(0)

        # initalize uniswap price
        self.uniswap_current_exchange_price = self.uniswap.get_exchange_rate()

        # set target min and max amounts for each side of the pair
        # balance doesnt exceed some level, as an effective stop loss against impermanent loss
        self.target_a_min_balance = Wad.from_number(
            self.arguments.target_a_min_balance)
        self.target_a_max_balance = Wad.from_number(
            self.arguments.target_a_max_balance)
        self.target_b_min_balance = Wad.from_number(
            self.arguments.target_b_min_balance)
        self.target_b_max_balance = Wad.from_number(
            self.arguments.target_b_max_balance)

        self.accepted_price_slippage_up = Wad.from_number(
            self.arguments.accepted_price_slippage_up / 100)
        self.accepted_price_slippage_down = Wad.from_number(
            self.arguments.accepted_price_slippage_down / 100)
        self.max_add_liquidity_slippage = Wad.from_number(
            self.arguments.max_add_liquidity_slippage / 100)
Exemple #27
0
    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='leverj-market-maker-keeper')

        parser.add_argument(
            "--leverj-api-server",
            type=str,
            default="https://test.leverj.io",
            help=
            "Address of the leverj API server (default: 'https://test.leverj.io')"
        )

        parser.add_argument("--account-id",
                            type=str,
                            default="",
                            help="Address of leverj api account id")

        parser.add_argument("--api-key",
                            type=str,
                            default="",
                            help="Address of leverj api key")

        parser.add_argument("--api-secret",
                            type=str,
                            default="",
                            help="Address of leverj api secret")

        parser.add_argument(
            "--leverj-timeout",
            type=float,
            default=9.5,
            help=
            "Timeout for accessing the Leverj API (in seconds, default: 9.5)")

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to watch our trades")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument(
            "--refresh-frequency",
            type=int,
            default=3,
            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--leverage",
                            type=float,
                            default=1.0,
                            help="Leverage chosen for futures orders")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        self.web3 = Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)

        setup_logging(self.arguments)

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)
        self.target_price_lean = Wad(0)
        self.leverage = self.arguments.leverage

        self.history = History()

        self.leverj_api = LeverjFuturesAPI(
            web3=self.web3,
            api_server=self.arguments.leverj_api_server,
            account_id=self.arguments.account_id,
            api_key=self.arguments.api_key,
            api_secret=self.arguments.api_secret,
            timeout=self.arguments.leverj_timeout)

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(
            lambda: self.leverj_api.get_orders(self.pair()))
        self.order_book_manager.get_balances_with(
            lambda: self.leverj_api.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.leverj_api.cancel_order(order.order_id))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='airswap-market-maker-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument("--orderserver-port",
                            type=str,
                            default='5004',
                            help="Port of the order server (default: '5004')")

        parser.add_argument(
            "--orderserver-host",
            type=str,
            default='127.0.0.1',
            help="host of the order server (default: '127.0.0.1')")

        parser.add_argument(
            "--airswap-api-server",
            type=str,
            default='http://localhost:5005',
            help="Address of the Airswap API (default: 'http://localhost:5005')"
        )

        parser.add_argument(
            "--airswap-api-timeout",
            type=float,
            default=9.5,
            help=
            "Timeout for accessing the Airswap API (in seconds, default: 9.5)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument(
            "--exchange-address",
            type=str,
            required=True,
            help="Ethereum address of the 0x Exchange contract")

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--buy-token-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the buy token")

        parser.add_argument("--sell-token-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the sell token")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.airswap_api = AirswapApi(self.arguments.airswap_api_server,
                                      self.arguments.airswap_api_timeout)

        if self.arguments.buy_token_address == '0x0000000000000000000000000000000000000000':
            self.token_buy = EthToken(web3=self.web3,
                                      address=Address(
                                          self.arguments.buy_token_address))
        else:
            self.token_buy = ERC20Token(web3=self.web3,
                                        address=Address(
                                            self.arguments.buy_token_address))

        if self.arguments.sell_token_address == '0x0000000000000000000000000000000000000000':
            self.token_sell = EthToken(web3=self.web3,
                                       address=Address(
                                           self.arguments.sell_token_address))
        else:
            self.token_sell = ERC20Token(
                web3=self.web3,
                address=Address(self.arguments.sell_token_address))

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        self.history = History()
Exemple #29
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='auction-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")
        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")
        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")
        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument('--type',
                            type=str,
                            choices=['flip', 'flap', 'flop'],
                            help="Auction type in which to participate")
        parser.add_argument(
            '--ilk',
            type=str,
            help=
            "Name of the collateral type for a flip keeper (e.g. 'ETH-B', 'ZRX-A'); "
            "available collateral types can be found at the left side of the CDP Portal"
        )

        parser.add_argument(
            '--bid-only',
            dest='create_auctions',
            action='store_false',
            help="Do not take opportunities to create new auctions")
        parser.add_argument(
            '--max-auctions',
            type=int,
            default=100,
            help="Maximum number of auctions to simultaneously interact with, "
            "used to manage OS and hardware limitations")
        parser.add_argument(
            '--min-flip-lot',
            type=float,
            default=0,
            help="Minimum lot size to create or bid upon a flip auction")

        parser.add_argument(
            "--vulcanize-endpoint",
            type=str,
            help=
            "When specified, frob history will be queried from a VulcanizeDB lite node, "
            "reducing load on the Ethereum node for flip auctions")
        parser.add_argument(
            '--from-block',
            type=int,
            help=
            "Starting block from which to look at history (set to block where MCD was deployed)"
        )

        parser.add_argument(
            '--vat-dai-target',
            type=float,
            help="Amount of Dai to keep in the Vat contract (e.g. 2000)")
        parser.add_argument(
            '--keep-dai-in-vat-on-exit',
            dest='exit_dai_on_shutdown',
            action='store_false',
            help=
            "Retain Dai in the Vat on exit, saving gas when restarting the keeper"
        )
        parser.add_argument('--keep-gem-in-vat-on-exit',
                            dest='exit_gem_on_shutdown',
                            action='store_false',
                            help="Retain collateral in the Vat on exit")

        parser.add_argument(
            "--model",
            type=str,
            required=True,
            nargs='+',
            help="Commandline to use in order to start the bidding model")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        # Configure connection to the chain
        if self.arguments.rpc_host.startswith("http"):
            endpoint_uri = f"{self.arguments.rpc_host}:{self.arguments.rpc_port}"
        else:
            # Should probably default this to use TLS, but I don't want to break existing configs
            endpoint_uri = f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}"
        self.web3: Web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=endpoint_uri,
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)
        self.our_address = Address(self.arguments.eth_from)

        # Check configuration for retrieving urns/bites
        if self.arguments.type == 'flip' and self.arguments.create_auctions \
                and self.arguments.from_block is None and self.arguments.vulcanize_endpoint is None:
            raise RuntimeError(
                "Either --from-block or --vulcanize-endpoint must be specified to kick off "
                "flip auctions")
        if self.arguments.type == 'flip' and not self.arguments.ilk:
            raise RuntimeError(
                "--ilk must be supplied when configuring a flip keeper")
        if self.arguments.type == 'flop' and self.arguments.create_auctions \
                and self.arguments.from_block is None:
            raise RuntimeError(
                "--from-block must be specified to kick off flop auctions")

        # Configure core and token contracts
        mcd = DssDeployment.from_node(web3=self.web3)
        self.vat = mcd.vat
        self.cat = mcd.cat
        self.vow = mcd.vow
        self.mkr = mcd.mkr
        self.dai_join = mcd.dai_adapter
        if self.arguments.type == 'flip':
            self.collateral = mcd.collaterals[self.arguments.ilk]
            self.ilk = self.collateral.ilk
            self.gem_join = self.collateral.adapter
        else:
            self.collateral = None
            self.ilk = None
            self.gem_join = None

        # Configure auction contracts
        self.flipper = self.collateral.flipper if self.arguments.type == 'flip' else None
        self.flapper = mcd.flapper if self.arguments.type == 'flap' else None
        self.flopper = mcd.flopper if self.arguments.type == 'flop' else None
        self.urn_history = None
        if self.flipper:
            self.min_flip_lot = Wad.from_number(self.arguments.min_flip_lot)
            self.strategy = FlipperStrategy(self.flipper, self.min_flip_lot)
            self.urn_history = UrnHistory(self.web3, mcd, self.ilk,
                                          self.arguments.from_block,
                                          self.arguments.vulcanize_endpoint)
        elif self.flapper:
            self.strategy = FlapperStrategy(self.flapper, self.mkr.address)
        elif self.flopper:
            self.strategy = FlopperStrategy(self.flopper)
        else:
            raise RuntimeError("Please specify auction type")

        # Create the collection used to manage auctions relevant to this keeper
        self.auctions = Auctions(
            flipper=self.flipper.address if self.flipper else None,
            flapper=self.flapper.address if self.flapper else None,
            flopper=self.flopper.address if self.flopper else None,
            model_factory=ModelFactory(' '.join(self.arguments.model)))
        self.auctions_lock = threading.Lock()
        self.dead_auctions = set()

        self.vat_dai_target = Wad.from_number(self.arguments.vat_dai_target) if \
            self.arguments.vat_dai_target is not None else None

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
        # reduce logspew
        logging.getLogger('urllib3').setLevel(logging.INFO)
        logging.getLogger("web3").setLevel(logging.INFO)
        logging.getLogger("asyncio").setLevel(logging.INFO)
        logging.getLogger("requests").setLevel(logging.INFO)
Exemple #30
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='etherdelta-market-maker-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument("--tub-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the Tub contract")

        parser.add_argument("--etherdelta-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the EtherDelta contract")

        parser.add_argument(
            "--etherdelta-socket",
            type=str,
            required=True,
            help="Ethereum address of the EtherDelta API socket")

        parser.add_argument(
            "--etherdelta-number-of-attempts",
            type=int,
            default=3,
            help=
            "Number of attempts of running the tool to talk to the EtherDelta API socket"
        )

        parser.add_argument(
            "--etherdelta-retry-interval",
            type=int,
            default=10,
            help=
            "Retry interval for sending orders over the EtherDelta API socket")

        parser.add_argument(
            "--etherdelta-timeout",
            type=int,
            default=120,
            help="Timeout for sending orders over the EtherDelta API socket")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument("--order-age",
                            type=int,
                            required=True,
                            help="Age of created orders (in blocks)")

        parser.add_argument(
            "--order-expiry-threshold",
            type=int,
            default=0,
            help=
            "Remaining order age (in blocks) at which order is considered already expired, which"
            " means the keeper will send a new replacement order slightly ahead"
        )

        parser.add_argument(
            "--order-no-cancel-threshold",
            type=int,
            default=0,
            help=
            "Remaining order age (in blocks) below which keeper does not try to cancel orders,"
            " assuming that they will probably expire before the cancel transaction gets mined"
        )

        parser.add_argument(
            "--eth-reserve",
            type=float,
            required=True,
            help=
            "Amount of ETH which will never be deposited so the keeper can cover gas"
        )

        parser.add_argument(
            "--min-eth-balance",
            type=float,
            default=0,
            help="Minimum ETH balance below which keeper will cease operation")

        parser.add_argument(
            "--min-eth-deposit",
            type=float,
            required=True,
            help=
            "Minimum amount of ETH that can be deposited in one transaction")

        parser.add_argument(
            "--min-sai-deposit",
            type=float,
            required=True,
            help=
            "Minimum amount of SAI that can be deposited in one transaction")

        parser.add_argument(
            '--cancel-on-shutdown',
            dest='cancel_on_shutdown',
            action='store_true',
            help=
            "Whether should cancel all open orders on EtherDelta on keeper shutdown"
        )

        parser.add_argument(
            '--withdraw-on-shutdown',
            dest='withdraw_on_shutdown',
            action='store_true',
            help=
            "Whether should withdraw all tokens from EtherDelta on keeper shutdown"
        )

        parser.add_argument("--gas-price",
                            type=int,
                            default=0,
                            help="Gas price (in Wei)")

        parser.add_argument(
            "--smart-gas-price",
            dest='smart_gas_price',
            action='store_true',
            help=
            "Use smart gas pricing strategy, based on the ethgasstation.info feed"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        parser.set_defaults(cancel_on_shutdown=False,
                            withdraw_on_shutdown=False)

        self.arguments = parser.parse_args(args)
        setup_logging(self.arguments)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        register_keys(self.web3, self.arguments.eth_key)

        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.eth_reserve = Wad.from_number(self.arguments.eth_reserve)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.min_eth_deposit = Wad.from_number(self.arguments.min_eth_deposit)
        self.min_sai_deposit = Wad.from_number(self.arguments.min_sai_deposit)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments, self.tub)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)

        if self.eth_reserve <= self.min_eth_balance:
            raise Exception(
                "--eth-reserve must be higher than --min-eth-balance")

        assert (self.arguments.order_expiry_threshold >= 0)
        assert (self.arguments.order_no_cancel_threshold >=
                self.arguments.order_expiry_threshold)

        self.history = History()
        self.etherdelta = EtherDelta(web3=self.web3,
                                     address=Address(
                                         self.arguments.etherdelta_address))
        self.etherdelta_api = EtherDeltaApi(
            client_tool_directory="lib/pymaker/utils/etherdelta-client",
            client_tool_command="node main.js",
            api_server=self.arguments.etherdelta_socket,
            number_of_attempts=self.arguments.etherdelta_number_of_attempts,
            retry_interval=self.arguments.etherdelta_retry_interval,
            timeout=self.arguments.etherdelta_timeout)

        self.our_orders = list()