def test_comparison(self, deployment: Deployment): # expect assert deployment.vox == deployment.vox assert deployment.vox == Vox(web3=deployment.web3, address=deployment.vox.address) assert deployment.vox != Vox(web3=deployment.web3, address=deployment.top.address)
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))
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("--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="Buy/sell bands configuration file") parser.add_argument( "--price-feed", type=str, help= "Source of price feed. Tub price feed will be used if not specified" ) parser.add_argument( "--price-feed-expiry", type=int, default=120, help="Maximum age of non-Tub price feed (in seconds, default: 120)" ) 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 with either terminate or not start at all" ) 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( "--gas-price-increase", type=int, help="Gas price increase (in Wei) if no confirmation within" " `--gas-price-increase-every` seconds") parser.add_argument( "--gas-price-increase-every", type=int, default=120, help="Gas price increase frequency (in seconds, default: 120)") parser.add_argument("--gas-price-max", type=int, help="Maximum gas price (in Wei)") parser.add_argument("--gas-price-file", type=str, help="Gas price configuration file") 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) 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) self.tub = Tub(web3=self.web3, address=Address(self.arguments.tub_address)) self.vox = Vox(web3=self.web3, address=self.tub.vox()) self.sai = ERC20Token(web3=self.web3, address=self.tub.sai()) self.gem = ERC20Token(web3=self.web3, address=self.tub.gem()) logging.basicConfig( format='%(asctime)-15s %(levelname)-8s %(message)s', level=(logging.DEBUG if self.arguments.debug else logging.INFO)) logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO) logging.getLogger('requests.packages.urllib3.connectionpool').setLevel( logging.INFO) 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.price_feed, self.arguments.price_feed_expiry, self.tub, self.vox) 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.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()
def __init__(self): web3 = Web3(HTTPProvider("http://localhost:8555")) web3.eth.defaultAccount = web3.eth.accounts[0] our_address = Address(web3.eth.defaultAccount) sai = DSToken.deploy(web3, 'DAI') sin = DSToken.deploy(web3, 'SIN') skr = DSToken.deploy(web3, 'PETH') gem = DSToken.deploy(web3, 'WETH') gov = DSToken.deploy(web3, 'MKR') pip = DSValue.deploy(web3) pep = DSValue.deploy(web3) pit = DSVault.deploy(web3) vox = Vox.deploy(web3, per=Ray.from_number(1)) tub = Tub.deploy(web3, sai=sai.address, sin=sin.address, skr=skr.address, gem=gem.address, gov=gov.address, pip=pip.address, pep=pep.address, vox=vox.address, pit=pit.address) tap = Tap.deploy(web3, tub.address) top = Top.deploy(web3, tub.address, tap.address) tub._contract.functions.turn(tap.address.address).transact() etherdelta = EtherDelta.deploy( web3, admin=Address('0x1111100000999998888877777666665555544444'), fee_account=Address('0x8888877777666665555544444111110000099999'), account_levels_addr=Address( '0x0000000000000000000000000000000000000000'), fee_make=Wad.from_number(0.01), fee_take=Wad.from_number(0.02), fee_rebate=Wad.from_number(0.03)) # set permissions dad = DSGuard.deploy(web3) dad.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact() tub.set_authority(dad.address).transact() for auth in [sai, sin, skr, gem, gov, pit, tap, top]: auth.set_authority(dad.address).transact() # approve tub.approve(directly()) tap.approve(directly()) # mint some GEMs gem.mint(Wad.from_number(1000000)).transact() self.snapshot_id = web3.manager.request_blocking("evm_snapshot", []) self.web3 = web3 self.our_address = our_address self.sai = sai self.sin = sin self.skr = skr self.gem = gem self.gov = gov self.vox = vox self.tub = tub self.tap = tap self.top = top self.etherdelta = etherdelta
def __init__(self): web3 = Web3(EthereumTesterProvider()) web3.eth.defaultAccount = web3.eth.accounts[0] our_address = Address(web3.eth.defaultAccount) sai = DSToken.deploy(web3, 'DAI') sin = DSToken.deploy(web3, 'SIN') skr = DSToken.deploy(web3, 'PETH') gem = DSToken.deploy(web3, 'WETH') gov = DSToken.deploy(web3, 'MKR') pip = DSValue.deploy(web3) pep = DSValue.deploy(web3) pit = DSVault.deploy(web3) vox = Vox.deploy(web3, per=Ray.from_number(1)) tub = Tub.deploy(web3, sai=sai.address, sin=sin.address, skr=skr.address, gem=gem.address, gov=gov.address, pip=pip.address, pep=pep.address, vox=vox.address, pit=pit.address) tap = Tap.deploy(web3, tub.address) top = Top.deploy(web3, tub.address, tap.address) tub._contract.transact().turn(tap.address.address) otc = MatchingMarket.deploy(web3, 2600000000) etherdelta = EtherDelta.deploy(web3, admin=Address('0x1111100000999998888877777666665555544444'), fee_account=Address('0x8888877777666665555544444111110000099999'), account_levels_addr=Address('0x0000000000000000000000000000000000000000'), fee_make=Wad.from_number(0.01), fee_take=Wad.from_number(0.02), fee_rebate=Wad.from_number(0.03)) # set permissions dad = DSGuard.deploy(web3) dad.permit(DSGuard.ANY, DSGuard.ANY, DSGuard.ANY).transact() tub.set_authority(dad.address).transact() for auth in [sai, sin, skr, gem, gov, pit, tap, top]: auth.set_authority(dad.address).transact() # whitelist pairs otc.add_token_pair_whitelist(sai.address, gem.address).transact() # approve tub.approve(directly()) tap.approve(directly()) # mint some GEMs gem.mint(Wad.from_number(1000000)).transact() web3.providers[0].rpc_methods.evm_snapshot() self.web3 = web3 self.our_address = our_address self.sai = sai self.sin = sin self.skr = skr self.gem = gem self.gov = gov self.vox = vox self.tub = tub self.tap = tap self.top = top self.otc = otc self.etherdelta = etherdelta
def test_fail_when_no_contract_under_that_address(self, deployment: Deployment): # expect with pytest.raises(Exception): Vox(web3=deployment.web3, address=Address('0xdeadadd1e5500000000000000000000000000000'))
def __init__(self, args: list, **kwargs): parser = argparse.ArgumentParser(prog='radarrelay-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("--tub-address", type=str, required=True, help="Ethereum address of the Tub contract") parser.add_argument( "--exchange-address", type=str, required=True, help="Ethereum address of the 0x Exchange contract") parser.add_argument("--weth-address", type=str, required=True, help="Ethereum address of the WETH token") parser.add_argument("--relayer-api-server", type=str, required=True, help="Address of the 0x Relayer API") parser.add_argument("--config", type=str, required=True, help="Buy/sell bands configuration file") parser.add_argument( "--price-feed", type=str, help= "Source of price feed. Tub price feed will be used if not specified" ) parser.add_argument( "--price-feed-expiry", type=int, default=120, help="Maximum age of non-Tub price feed (in seconds, default: 120)" ) parser.add_argument( "--order-expiry", type=int, required=True, help="Expiration time of created orders (in seconds)") parser.add_argument( "--order-expiry-threshold", type=int, default=0, help= "Order expiration time at which order is considered already expired (in seconds)" ) parser.add_argument( "--min-eth-balance", type=float, default=0, help= "Minimum ETH balance below which keeper with either terminate or not start at all" ) parser.add_argument( '--cancel-on-shutdown', dest='cancel_on_shutdown', action='store_true', help= "Whether should cancel all open orders on RadarRelay on keeper shutdown" ) parser.add_argument("--gas-price", type=int, default=0, help="Gas price (in Wei)") parser.add_argument( "--gas-price-increase", type=int, help="Gas price increase (in Wei) if no confirmation within" " `--gas-price-increase-every` seconds") parser.add_argument( "--gas-price-increase-every", type=int, default=120, help="Gas price increase frequency (in seconds, default: 120)") parser.add_argument("--gas-price-max", type=int, help="Maximum gas price (in Wei)") parser.add_argument("--gas-price-file", type=str, help="Gas price configuration file") 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") 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 self.our_address = Address(self.arguments.eth_from) self.tub = Tub(web3=self.web3, address=Address(self.arguments.tub_address)) self.vox = Vox(web3=self.web3, address=self.tub.vox()) self.sai = ERC20Token(web3=self.web3, address=self.tub.sai()) self.ether_token = ERC20Token(web3=self.web3, address=Address( self.arguments.weth_address)) logging.basicConfig( format='%(asctime)-15s %(levelname)-8s %(message)s', level=(logging.DEBUG if self.arguments.debug else logging.INFO)) logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO) logging.getLogger('requests.packages.urllib3.connectionpool').setLevel( logging.INFO) 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.price_feed, self.arguments.price_feed_expiry, self.tub, self.vox) self.radar_relay = ZrxExchange(web3=self.web3, address=Address( self.arguments.exchange_address)) self.radar_relay_api = ZrxRelayerApi( exchange=self.radar_relay, api_server=self.arguments.relayer_api_server)
def __init__(self, args: list, **kwargs): parser = argparse.ArgumentParser(prog='oasis-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("--tub-address", type=str, required=True, help="Ethereum address of the Tub contract") parser.add_argument("--oasis-address", type=str, required=True, help="Ethereum address of the OasisDEX contract") parser.add_argument("--config", type=str, required=True, help="Buy/sell bands configuration file") parser.add_argument( "--price-feed", type=str, help= "Source of price feed. Tub price feed will be used if not specified" ) parser.add_argument( "--price-feed-expiry", type=int, default=120, help="Maximum age of non-Tub price feed (in seconds, default: 120)" ) parser.add_argument( "--round-places", type=int, default=2, help="Number of decimal places to round order prices to (default=2)" ) parser.add_argument( "--min-eth-balance", type=float, default=0, help= "Minimum ETH balance below which keeper with either terminate or not start at all" ) parser.add_argument("--gas-price", type=int, default=0, help="Gas price (in Wei)") parser.add_argument( "--gas-price-increase", type=int, help="Gas price increase (in Wei) if no confirmation within" " `--gas-price-increase-every` seconds") parser.add_argument( "--gas-price-increase-every", type=int, default=120, help="Gas price increase frequency (in seconds, default: 120)") parser.add_argument("--gas-price-max", type=int, help="Maximum gas price (in Wei)") parser.add_argument("--gas-price-file", type=str, help="Gas price configuration file") 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") 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 self.our_address = Address(self.arguments.eth_from) self.otc = MatchingMarket(web3=self.web3, address=Address( self.arguments.oasis_address)) self.tub = Tub(web3=self.web3, address=Address(self.arguments.tub_address)) self.vox = Vox(web3=self.web3, address=self.tub.vox()) self.sai = ERC20Token(web3=self.web3, address=self.tub.sai()) self.gem = ERC20Token(web3=self.web3, address=self.tub.gem()) logging.basicConfig( format='%(asctime)-15s %(levelname)-8s %(message)s', level=(logging.DEBUG if self.arguments.debug else logging.INFO)) logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO) logging.getLogger('requests.packages.urllib3.connectionpool').setLevel( logging.INFO) 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.price_feed, self.arguments.price_feed_expiry, self.tub, self.vox)
def __init__(self, args: list, **kwargs): parser = argparse.ArgumentParser(prog='bite-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", 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( "--graphql-url", type=str, default="https://sai-mainnet.makerfoundation.com/v1", help="GraphQL URL " + "(default: `https://sai-mainnet.makerfoundation.com/v1')" ) parser.add_argument( "--bitecdps-address", help="Ethereum address of the BiteCdps contract", type=str ) parser.add_argument( "--top", help="Quickly process N top bites, (default: 500)", default=500, type=int ) parser.add_argument( "--chunks", help="Process top bites in chunks of N, (default: 100)", default=100, type=int ) parser.add_argument( "--debug", help="Enable debug output", dest='debug', action='store_true' ) 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 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()) self.top = self.arguments.top self.chunks = self.arguments.chunks if self.arguments.bitecdps_address and self.arguments.graphql_url: self.use_bitecdps = True self.bitecdps = BiteCdps( web3=self.web3, address=Address(self.arguments.bitecdps_address) ) self.graphql_url = self.arguments.graphql_url else: self.use_bitecdps = False logging.basicConfig( format='%(asctime)-15s %(levelname)-8s %(message)s', level=(logging.DEBUG if self.arguments.debug else logging.INFO) )
class BiteKeeper: """Keeper to bite undercollateralized cups.""" logger = logging.getLogger('bite-all-keeper') def __init__(self, args: list, **kwargs): parser = argparse.ArgumentParser(prog='bite-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", 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( "--graphql-url", type=str, default="https://sai-mainnet.makerfoundation.com/v1", help="GraphQL URL " + "(default: `https://sai-mainnet.makerfoundation.com/v1')" ) parser.add_argument( "--bitecdps-address", help="Ethereum address of the BiteCdps contract", type=str ) parser.add_argument( "--top", help="Quickly process N top bites, (default: 500)", default=500, type=int ) parser.add_argument( "--chunks", help="Process top bites in chunks of N, (default: 100)", default=100, type=int ) parser.add_argument( "--debug", help="Enable debug output", dest='debug', action='store_true' ) 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 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()) self.top = self.arguments.top self.chunks = self.arguments.chunks if self.arguments.bitecdps_address and self.arguments.graphql_url: self.use_bitecdps = True self.bitecdps = BiteCdps( web3=self.web3, address=Address(self.arguments.bitecdps_address) ) self.graphql_url = self.arguments.graphql_url else: self.use_bitecdps = False logging.basicConfig( format='%(asctime)-15s %(levelname)-8s %(message)s', level=(logging.DEBUG if self.arguments.debug else logging.INFO) ) def main(self): with Lifecycle(self.web3) as lifecycle: self.lifecycle = lifecycle lifecycle.on_block(self.check_all_cups) def check_all_cups(self): if self.tub._contract.functions.off().call(): self.logger.info('Single Collateral Dai has been Caged') self.logger.info('Starting to bite all cups in the tub contract') # Read some things that wont change across cups axe = self.tub.axe() # Liquidation penalty [RAY] Fixed at 1 RAY at cage par = self.vox.par() # Dai Targe Price [RAY] Typically 1 RAY tag = self.tub.tag() # Ref/Oracle price [RAY] Fixed at shutdown if self.use_bitecdps: self.call_bitecdps() else: for cup_id in range(self.tub.cupi()): self.check_cup(cup_id+1, axe, par, tag) self.lifecycle.terminate() else: self.logger.info('Single Collateral Dai live') def check_cup(self, cup_id, axe: Ray, par: Ray, tag: Ray): cup = self.tub.cups(cup_id) rue = Ray(self.tub.tab(cup_id)) # Amount of Debt[RAY] # Amount owed in SKR, including liquidation penalty # var owe = rdiv(rmul(rmul(rue, axe), vox.par()), tag()); owe = ((rue * axe) * par) / tag # Bite cups with owe over a threshold that haven't been bitten before if owe > Ray.from_number(0) and cup.art != Wad.from_number(0): self.logger.info( f'Bite cup {cup_id} with owe of {owe} and ink of {cup.ink}' ) self.tub.bite(cup_id).transact(gas_price=self.gas_price()) def call_bitecdps(self): self.logger.info(f'Will bite top {self.top} CDPs') # cdps = [i+1 for i in range(self.tub.cupi())] cdps = self.get_cdps() self.logger.info(f'found {len(cdps)} CDPs') for i in range(0, len(cdps), self.chunks): chunk = cdps[i:i+self.chunks] self.logger.info(f'BiteCdps.bite({chunk})') self.bitecdps.bite(chunk).transact(gas_price=self.gas_price()) def get_cdps(self): client = GraphQLClient(self.graphql_url) result = client.execute(QUERY.replace('XXX', f'{self.top}')) data = json.loads(result) cdps = [] for cdp in data["data"]["allCups"]["nodes"]: cdps.append(cdp["id"]) return cdps def gas_price(self): """ IncreasingGasPrice """ GWEI = 1000000000 return IncreasingGasPrice(initial_price=5*GWEI, increase_by=10*GWEI, every_secs=60, max_price=300*GWEI)
class BiteKeeper: """Keeper to bite undercollateralized cups.""" logger = logging.getLogger('bite-all-keeper') 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)) def main(self): with Lifecycle(self.web3) as lifecycle: lifecycle.on_block(self.check_all_cups) def check_all_cups(self): if self.tub._contract.functions.off().call(): self.logger.info('Single Collateral Dai has been Caged') self.logger.info('Starting to bite all cups in the tub contract') # Read some things that wont change across cups axe = self.tub.axe( ) # Liquidation penalty [RAY] Fixed at 1 RAY at cage par = self.vox.par() # Dai Targe Price [RAY] Typically 1 RAY tag = self.tub.tag() # Ref/Oracle price [RAY] Fixed at shutdown for cup_id in range(self.tub.cupi()): self.check_cup(cup_id + 1, axe, par, tag) else: self.logger.info('Single Collateral Dai live') def check_cup(self, cup_id, axe: Ray, par: Ray, tag: Ray): cup = self.tub.cups(cup_id) rue = Ray(self.tub.tab(cup_id)) # Amount of Debt[RAY] # Amount owed in SKR, including liquidation penalty # var owe = rdiv(rmul(rmul(rue, axe), vox.par()), tag()); owe = ((rue * axe) * par) / tag # Bite cups with owe over a threshold that haven't been bitten before if owe > Ray.from_number(0) and cup.art != Wad.from_number(0): self.logger.info( f'Bite cup {cup_id} with owe of {owe} and ink of {cup.ink}') self.tub.bite(cup_id).transact(gas_price=self.gas_price()) def gas_price(self): """ IncreasingGasPrice """ GWEI = 1000000000 return IncreasingGasPrice(initial_price=5 * GWEI, increase_by=10 * GWEI, every_secs=60, max_price=300 * GWEI)