class BiteKeeper: """Keeper to bite undercollateralized cups.""" 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("--tub-address", help="Ethereum address of the Tub contract", required=True, type=str) parser.add_argument("--gas-price", help="Gas price in Wei (default: node default)", type=int) 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"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)) 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): for cup_id in range(self.tub.cupi()): self.check_cup(cup_id+1) def check_cup(self, cup_id): if not self.tub.safe(cup_id): self.tub.bite(cup_id).transact(gas_price=self.gas_price()) def gas_price(self): if self.arguments.gas_price: return FixedGasPrice(self.arguments.gas_price) else: return DefaultGasPrice()
class DAIv1(Market): def __init__(self, web3, dai_tub = '0x448a5065aeBB8E423F0896E6c5D525C040f59af3'): self.web3 = web3 self.tub = Tub(web3=web3, address=Address(dai_tub)) self.tap = Tap(web3=web3, address=self.tub.tap()) self.tokens = { 'MKR': ERC20Token(web3, self.tub.gov()), 'PETH': ERC20Token(web3, self.tub.skr()), 'WETH': ERC20Token(web3, self.tub.gem()), 'DAI': ERC20Token(web3, self.tub.sai()), } def get_cup(self, cup_id): cup = self.tub.cups(cup_id) return { 'id': cup.cup_id, 'lad': cup.lad.address, 'art': float(cup.art), 'ink': float(cup.ink), 'safe': self.tub.safe(cup_id) } def get_cups(self): last_cup_id = self.tub.cupi() cups = map(self.get_cup, range(1, last_cup_id+1)) not_empty_cups = filter(lambda cup: cup['lad'] != "0x0000000000000000000000000000000000000000", cups) return list(not_empty_cups) def get_pairs(self): pairs = ['PETH/DAI', 'PETH/WETH'] return pairs def get_orders(self, base, quote): depth = {'bids': [], 'asks': []} # PETH/DAI order book if base == 'PETH' and quote == 'DAI': # boom is a taker using a bid side from maker tap # a taker convert PETH to DAI using tap.bid(1) as price # maker side offer DAI in exchange for PETH (flap) # DAI qty offered by is min(joy - woe, 0) order = { 'price': float(self.tap.bid(Wad.from_number(1))), 'amount': float(min(self.tap.joy() - self.tap.woe(), Wad.from_number(0))), 'id': 'take:tap.boom()', } if order['amount'] > 0: depth['bids'].append(order) # bust is a taker using ask side from maker tap # a taker convert DAI to PETH using tap.ask(1) as price # maker side offer PETH from fog (flip) and PETH minted to cover woe (flop) # PETH qty offered by maker is fog+min(woe-joy, 0)/ask order = { 'price': float(self.tap.ask(Wad.from_number(1))), 'amount': float(self.tap.fog() + min(self.tap.woe() - self.tap.joy(), Wad.from_number(0)) / self.tap.ask(Wad.from_number(1))), 'id': 'take:tap.bust()', } if order['amount'] > 0: depth['asks'].append(order) # PETH/WETH order book if base == 'PETH' and quote == 'WETH': # exit is a taker using a bid side from maker tub # a taker PETH to WETH using tub.bid(1) as price # maker side offer WETH in exchange for PETH # WETH qty offered by maker is infinity (2**32 as a large number for infinity ...) order = { 'price': float(self.tub.bid(Wad.from_number(1))), 'amount': float(2**32), 'id': 'take:tub.exit()', } depth['bids'].append(order) # join is a taker using ask side from maker tub # a taker convert WETH to PETH usgin tub.ask(1) as price # maker side offer PETH in exchange for WETH # PETH qty offered by maker is infinity (2**32 as a large number for infinity ...) order = { 'price': float(self.tub.ask(Wad.from_number(1))), 'amount': float(2**32), 'id': 'take:tub.join()', } depth['asks'].append(order) return depth def get_accounts(self, manager_url): accounts = {} for addr in requests.get(manager_url).json(): accounts[addr] = { 'balance' : self.web3.eth.getBalance(addr), } accounts[addr]['tokens'] = {} for name, token in self.tokens.items(): balance = token.balance_of(Address(addr)) allowance = token.allowance_of(Address(addr), self.tub.address) #TODO check tap allowance ... if float(allowance) or float(balance): accounts[addr]['tokens'][name] = { 'allowance': float(allowance), 'balance': float(balance), } return accounts