Exemple #1
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 #2
0
if from_block:
    started = datetime.now()
    print(f"Connecting to {sys.argv[1]}...")
    uh = ChainUrnHistoryProvider(web3, mcd, ilk, from_block)
    urns_chain = uh.get_urns()
    elapsed: timedelta = datetime.now() - started
    print(
        f"Found {len(urns_chain)} urns from block {from_block} in {elapsed.seconds} seconds"
    )
    assert len(urns_chain) > 0

# Retrieve data from Vulcanize
if vulcanize_endpoint:
    started = datetime.now()
    print(f"Connecting to {vulcanize_endpoint}...")
    uh = VulcanizeUrnHistoryProvider(mcd, ilk, vulcanize_endpoint,
                                     vulcanize_key)
    urns_vdb = uh.get_urns()
    elapsed: timedelta = datetime.now() - started
    print(
        f"Found {len(urns_vdb)} urns from Vulcanize in {elapsed.seconds} seconds"
    )
    assert len(urns_vdb) > 0

# Retrieve data from TokenFlow
if tokenflow_endpoint:
    started = datetime.now()
    print(f"Connecting to {tokenflow_endpoint}...")
    uh = TokenFlowUrnHistoryProvider(web3, mcd, ilk, tokenflow_endpoint,
                                     tokenflow_key)
    urns_tf = uh.get_urns()
    elapsed: timedelta = datetime.now() - started