def vat(self) -> Address: return Address(self._contract.call().vat())
def flopper(self) -> Address: return Address(self._contract.call().flopper())
def gal_address(web3): return Address(web3.eth.accounts[3])
def __init__(self, lognote: LogNote): assert isinstance(lognote, LogNote) self.ilk = str(Web3.toText(lognote.arg1)).replace('\x00', '') self.urn = Address(Web3.toHex(lognote.arg2)[26:]) self.collateral_owner = Address(Web3.toHex(lognote.arg3)[26:])
# 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) endpoint_uri = sys.argv[1] # ex: https://localhost:8545 web3 = Web3( HTTPProvider(endpoint_uri=endpoint_uri, request_kwargs={"timeout": 30})) if len(sys.argv) > 3: web3.eth.defaultAccount = sys.argv[ 2] # ex: 0x0000000000000000000000000000000aBcdef123 register_keys( web3, [sys.argv[3]] ) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass our_address = Address(web3.eth.defaultAccount) run_transactions = True else: our_address = Address(sys.argv[2]) run_transactions = False mcd = DssDeployment.from_node(web3) collateral = mcd.collaterals['ETH-A'] ilk = collateral.ilk if run_transactions: collateral.approve(our_address) past_blocks = 100 class TestApp: def __init__(self):
def owner(self) -> Address: return Address(self._contract.call().owner())
def get_orders(self, p_token: Token = None, b_token: Token = None, block_ident: Optional[str] = None) -> List[Order]: """Get all active orders. If both `p_token` and `b_token` are specified, orders will be filtered by these. In case of the _MatchingMarket_ implementation, order enumeration will be much efficient if these two parameters are supplied, as then orders can be fetched using `getBestOffer` and a series of `getWorseOffer` calls. This approach will result in much lower number of calls comparing to the naive 0..get_last_order_id approach, especially if the number of inactive orders is very high. Either none or both of these parameters have to be specified. Args: `p_token`: Token object (see `model.py`) of the `pay_token` to filter the orders by. `b_token`: Token object (see `model.py`) of the `buy_token` to filter the orders by. `block_ident`: Block identifier can either be a str, int or None and will fallback to 'latest' block. Used for historical retreival of orders. Returns: A list of `Order` objects representing all active orders on Oasis. """ assert((isinstance(p_token, Token) and isinstance(b_token, Token)) or ((p_token is None) and (b_token is None))) assert(isinstance(block_ident, int) or isinstance(block_ident, str) or (block_ident is None)) block_ident = 'latest' if block_ident is None else block_ident if (p_token is None) or (b_token is None): pay_token = None buy_token = None else: pay_token = p_token.address buy_token = b_token.address if pay_token is not None and buy_token is not None: orders = [] if self._support_contract: result = self._support_contract.functions.getOffers(self.address.address, pay_token.address, buy_token.address).call(block_identifier=block_ident) while True: count = 0 for i in range(0, 100): if result[3][i] != '0x0000000000000000000000000000000000000000': count += 1 orders.append(Order(market=self, order_id=result[0][i], maker=Address(result[3][i]), pay_token=pay_token, pay_amount=p_token.normalize_amount(Wad(result[1][i])), buy_token=buy_token, buy_amount=b_token.normalize_amount(Wad(result[2][i])), timestamp=result[4][i])) if count == 100: next_order_id = self._contract.functions.getWorseOffer(orders[-1].order_id).call(block_identifier=block_ident) result = self._support_contract.functions.getOffers(self.address.address, next_order_id).call(block_identifier=block_ident) else: break else: order_id = self._contract.functions.getBestOffer(pay_token.address, buy_token.address).call(block_identifier=block_ident) while order_id != 0: order = self.get_order(order_id, block_ident) if order is not None: orders.append(order) order_id = self._contract.functions.getWorseOffer(order_id).call(block_identifier=block_ident) return sorted(orders, key=lambda order: order.order_id) else: return super(MatchingMarket, self).get_orders(pay_token, buy_token)
class ZrxExchangeV2(Contract): """A client for the 0x V2 exchange contract. You can find the `0x V2` exchange contract here: <https://etherscan.io/address/0x4f833a24e1f95d70f028921e27040ca56e09ab0b>. Attributes: web3: An instance of `Web` from `web3.py`. address: Ethereum address of the _0x_ `Exchange` contract. """ abi = Contract._load_abi(__name__, 'abi/ExchangeV2.abi') bin = Contract._load_bin(__name__, 'abi/ExchangeV2.bin') _ZERO_ADDRESS = Address("0x0000000000000000000000000000000000000000") ORDER_INFO_TYPE = '(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)' @staticmethod def deploy(web3: Web3, zrx_asset: str): """Deploy a new instance of the 0x `Exchange` contract. Args: web3: An instance of `Web` from `web3.py`. zrx_token: The address of the ZRX token this exchange will use. Returns: A `ZrxExchange` class instance. """ return ZrxExchangeV2(web3=web3, address=Contract._deploy(web3, ZrxExchangeV2.abi, ZrxExchangeV2.bin, [])) def __init__(self, web3: Web3, address: Address): assert (isinstance(web3, Web3)) assert (isinstance(address, Address)) self.web3 = web3 self.address = address self._contract = self._get_contract(web3, self.abi, address) def zrx_asset(self) -> str: """Get the asset data of the ZRX token contract associated with this `ExchangeV2` contract. Returns: The asset data of the `ZRX` token. """ return str(bytes_to_hexstring(self._contract.call().ZRX_ASSET_DATA())) def zrx_token(self) -> Address: """Get the address of the ZRX token contract associated with this `ExchangeV2` contract. Returns: The address of the `ZRX` token. """ return Address("0x" + self.zrx_asset()[-40:]) def asset_transfer_proxy(self, proxy_id: str) -> Address: """Get the address of the `ERC20Proxy` contract associated with this `Exchange` contract. Returns: The address of the `ERC20Proxy` token. """ assert (isinstance(proxy_id, str)) return Address(self._contract.call().getAssetProxy( hexstring_to_bytes(proxy_id))) def approve(self, tokens: List[ERC20Token], approval_function): """Approve the 0x ERC20Proxy contract to fully access balances of specified tokens. In case of 0x V2, it's the ERC20Proxy contract that actually gets the approvals, not the 0x Exchange contract itself. In addition to the tokens specified as the `tokens` parameter, the ZRX token always gets approved as well as without it the 0x Exchange contract wouldn't be able to charge maker and taker fees. For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager` in `pymaker.approval`. Args: tokens: List of :py:class:`pymaker.token.ERC20Token` class instances. approval_function: Approval function (i.e. approval mode). """ assert (isinstance(tokens, list)) assert (callable(approval_function)) for token in tokens: # TODO + [ERC20Token(web3=self.web3, address=self.zrx_token())] approval_function(token, self.asset_transfer_proxy(ERC20Asset.ID), '0x ERC20Proxy contract') def past_fill(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogFill]: """Synchronously retrieve past LogFill events. `LogFill` events are emitted by the 0x contract every time someone fills an order. Args: number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from. event_filter: Filter which will be applied to returned events. Returns: List of past `LogFill` events represented as :py:class:`pymaker.zrx.LogFill` class. """ assert (isinstance(number_of_past_blocks, int)) assert (isinstance(event_filter, dict) or (event_filter is None)) return self._past_events(self._contract, 'Fill', LogFill, number_of_past_blocks, event_filter) def past_cancel(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogCancel]: """Synchronously retrieve past LogCancel events. `LogCancel` events are emitted by the 0x contract every time someone cancels an order. Args: number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from. event_filter: Filter which will be applied to returned events. Returns: List of past `LogCancel` events represented as :py:class:`pymaker.zrx.LogCancel` class. """ assert (isinstance(number_of_past_blocks, int)) assert (isinstance(event_filter, dict) or (event_filter is None)) return self._past_events(self._contract, 'Cancel', LogCancel, number_of_past_blocks, event_filter) def create_order(self, pay_asset: Asset, pay_amount: Wad, buy_asset: Asset, buy_amount: Wad, expiration: int) -> Order: """Creates a new order. The `maker_fee`, `taker_fee` and `fee_recipient` fields are by default set to zero. Before signing the order and submitting it to the relayer, they may need to be populated using the `calculate_fees()` method of the `ZrxRelayerApi` class. Args: pay_asset: The asset you want to put on sale. pay_amount: Amount of the `pay_asset` token you want to put on sale. buy_asset: The asset you want to be paid with. buy_amount: Amount of the `buy_asset` you want to receive. expiration: Unix timestamp (in seconds) when the order will expire. Returns: New order as an instance of the :py:class:`pymaker.zrx.Order` class. """ assert (isinstance(pay_asset, Asset)) assert (isinstance(pay_amount, Wad)) assert (isinstance(buy_asset, Asset)) assert (isinstance(buy_amount, Wad)) assert (isinstance(expiration, int)) return Order(exchange=self, sender=self._ZERO_ADDRESS, maker=Address(self.web3.eth.defaultAccount), taker=self._ZERO_ADDRESS, maker_fee=Wad(0), taker_fee=Wad(0), pay_asset=pay_asset, pay_amount=pay_amount, buy_asset=buy_asset, buy_amount=buy_amount, salt=self.random_salt(), fee_recipient=self._ZERO_ADDRESS, expiration=expiration, exchange_contract_address=self.address, signature=None) def _get_order_info(self, order): assert (isinstance(order, Order)) method_signature = self.web3.sha3( text=f"getOrderInfo({self.ORDER_INFO_TYPE})")[0:4] method_parameters = encode_single(f"({self.ORDER_INFO_TYPE})", [self._order_tuple(order)]) request = bytes_to_hexstring(method_signature + method_parameters) response = self.web3.eth.call({ 'to': self.address.address, 'data': request }) response_decoded = decode_single("((uint8,bytes32,uint256))", response) return response_decoded def get_order_hash(self, order: Order) -> str: """Calculates hash of an order. Args: order: Order you want to calculate the hash of. Returns: Order hash as a hex string starting with `0x`. """ assert (isinstance(order, Order)) # the hash depends on the exchange contract address as well assert (order.exchange_contract_address == self.address) return bytes_to_hexstring(self._get_order_info(order)[0][1]) def get_unavailable_buy_amount(self, order: Order) -> Wad: """Return the order amount which was either taken or cancelled. Args: order: Order you want to get the unavailable amount of. Returns: The unavailable amount of the order (i.e. the amount which was either taken or cancelled), expressed in terms of the `buy_token` token. """ assert (isinstance(order, Order)) order_info = self._get_order_info(order)[0] if order_info[0] in [ 0, # INVALID, // Default value 1, # INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount 2, # INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount 4, # EXPIRED, // Order has already expired 5, # FULLY_FILLED, // Order is fully filled 6 ]: # CANCELLED // Order has been cancelled return order.buy_amount else: return Wad(order_info[2]) def sign_order(self, order: Order) -> Order: """Signs an order so it can be submitted to the relayer. Order will be signed by the `web3.eth.defaultAccount` account. Args: order: Order you want to sign. Returns: Signed order. Copy of the order passed as a parameter with the `signature` field filled with signature. """ assert (isinstance(order, Order)) signature = eth_sign(hexstring_to_bytes(self.get_order_hash(order)), self.web3) v, r, s = to_vrs(signature) signed_order = copy.copy(order) signed_order.signature = bytes_to_hexstring(bytes([v])) + \ bytes_to_hexstring(r)[2:] + \ bytes_to_hexstring(s)[2:] + \ "03" # EthSign return signed_order def fill_order(self, order: Order, fill_buy_amount: Wad) -> Transact: """Fills an order. Args: order: The order to be filled. fill_buy_amount: The amount (in terms of `buy_token` of the original order) to be filled. Returns: A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction. """ assert (isinstance(order, Order)) assert (isinstance(fill_buy_amount, Wad)) method_signature = self.web3.sha3( text=f"fillOrder({self.ORDER_INFO_TYPE},uint256,bytes)")[0:4] method_parameters = encode_single( f"({self.ORDER_INFO_TYPE},uint256,bytes)", [ self._order_tuple(order), fill_buy_amount.value, hexstring_to_bytes(order.signature) ]) request = bytes_to_hexstring(method_signature + method_parameters) return Transact(self, self.web3, self.abi, self.address, self._contract, None, [request]) def cancel_order(self, order: Order) -> Transact: """Cancels an order. Args: order: Order you want to cancel. Returns: A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction. """ assert (isinstance(order, Order)) method_signature = self.web3.sha3( text=f"cancelOrder({self.ORDER_INFO_TYPE})")[0:4] method_parameters = encode_single(f"({self.ORDER_INFO_TYPE})", [self._order_tuple(order)]) request = bytes_to_hexstring(method_signature + method_parameters) return Transact(self, self.web3, self.abi, self.address, self._contract, None, [request]) @staticmethod def _order_tuple(order): return (order.maker.address, order.taker.address, order.fee_recipient.address, order.sender.address, order.pay_amount.value, order.buy_amount.value, order.maker_fee.value, order.taker_fee.value, order.expiration, order.salt, hexstring_to_bytes(order.pay_asset.serialize()), hexstring_to_bytes(order.buy_asset.serialize())) @staticmethod def random_salt() -> int: return int(time.time() * 1000) def __repr__(self): return f"ZrxExchangeV2('{self.address}')"
def _create_signature(self, params: str) -> str: assert(isinstance(params, str)) return eth_sign(bytes(params, 'utf-8'), self.web3, self.api_secret, False, Address(self.account_id))
def approve_token(self, token_address: str, amount: int) -> Transact: assert(isinstance(token_address, str)) assert(isinstance(amount, int)) token_contract = self._get_contract(self.web3, self.token_abi, Address(token_address)) return Transact(self, self.web3, self.token_abi, Address(token_address), token_contract, "approve",[self.address.address, int(amount)], {}).transact(from_address=self.middle_account)
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)
class ZrxExchange(Contract): """A client for the 0x exchange contract. You can find the source code of the `0x` exchange contract here: <https://etherscan.io/address/0x12459c951127e0c374ff9105dda097662a027093#code>. Attributes: web3: An instance of `Web` from `web3.py`. address: Ethereum address of the _0x_ `Exchange` contract. """ abi = Contract._load_abi(__name__, 'abi/Exchange.abi') bin = Contract._load_bin(__name__, 'abi/Exchange.bin') _ZERO_ADDRESS = Address("0x0000000000000000000000000000000000000000") @staticmethod def deploy(web3: Web3, zrx_token: Address, token_transfer_proxy: Address): """Deploy a new instance of the 0x `Exchange` contract. Args: web3: An instance of `Web` from `web3.py`. zrx_token: The address of the ZRX token this exchange will use. token_transfer_proxy: The address of the token transfer proxy this exchange will use. Returns: A `ZrxExchange` class instance. """ return ZrxExchange(web3=web3, address=Contract._deploy(web3, ZrxExchange.abi, ZrxExchange.bin, [ zrx_token.address, token_transfer_proxy.address ])) def __init__(self, web3: Web3, address: Address): assert(isinstance(web3, Web3)) assert(isinstance(address, Address)) self.web3 = web3 self.address = address self._contract = self._get_contract(web3, self.abi, address) def zrx_token(self) -> Address: """Get the address of the ZRX token contract associated with this `Exchange` contract. Returns: The address of the `ZRX` token. """ return Address(self._contract.call().ZRX_TOKEN_CONTRACT()) def token_transfer_proxy(self) -> Address: """Get the address of the `TokenTransferProxy` contract associated with this `Exchange` contract. Returns: The address of the `TokenTransferProxy` token. """ return Address(self._contract.call().TOKEN_TRANSFER_PROXY_CONTRACT()) def approve(self, tokens: List[ERC20Token], approval_function): """Approve the 0x Exchange TokenTransferProxy contract to fully access balances of specified tokens. In case of 0x, it's the TokenTransferProxy contract that actually gets the approvals, not the 0x Exchange contract itself. In addition to the tokens specified as the `tokens` parameter, the ZRX token always gets approved as well as without it the 0x Exchange contract wouldn't be able to charge maker and taker fees. For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager` in `pymaker.approval`. Args: tokens: List of :py:class:`pymaker.token.ERC20Token` class instances. approval_function: Approval function (i.e. approval mode). """ assert(isinstance(tokens, list)) assert(callable(approval_function)) for token in tokens + [ERC20Token(web3=self.web3, address=self.zrx_token())]: approval_function(token, self.token_transfer_proxy(), '0x Exchange contract') def on_fill(self, handler, event_filter: dict = None): """Subscribe to LogFill events. `LogFill` events are emitted by the 0x contract every time someone fills an order. Args: handler: Function which will be called for each subsequent `LogFill` event. This handler will receive a :py:class:`pymaker.`zrx.LogFill` class instance. event_filter: Filter which will be applied to event subscription. """ assert(callable(handler)) assert(isinstance(event_filter, dict) or (event_filter is None)) self._on_event(self._contract, 'LogFill', LogFill, handler, event_filter) def on_cancel(self, handler, event_filter: dict = None): """Subscribe to LogCancel events. `LogCancel` events are emitted by the 0x contract every time someone cancels an order. Args: handler: Function which will be called for each subsequent `LogCancel` event. This handler will receive a :py:class:`pymaker.`zrx.LogCancel` class instance. event_filter: Filter which will be applied to event subscription. """ assert(callable(handler)) assert(isinstance(event_filter, dict) or (event_filter is None)) self._on_event(self._contract, 'LogCancel', LogCancel, handler, event_filter) def past_fill(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogFill]: """Synchronously retrieve past LogFill events. `LogFill` events are emitted by the 0x contract every time someone fills an order. Args: number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from. event_filter: Filter which will be applied to returned events. Returns: List of past `LogFill` events represented as :py:class:`pymaker.zrx.LogFill` class. """ assert(isinstance(number_of_past_blocks, int)) assert(isinstance(event_filter, dict) or (event_filter is None)) return self._past_events(self._contract, 'LogFill', LogFill, number_of_past_blocks, event_filter) def past_cancel(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogCancel]: """Synchronously retrieve past LogCancel events. `LogCancel` events are emitted by the 0x contract every time someone cancels an order. Args: number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from. event_filter: Filter which will be applied to returned events. Returns: List of past `LogCancel` events represented as :py:class:`pymaker.zrx.LogCancel` class. """ assert(isinstance(number_of_past_blocks, int)) assert(isinstance(event_filter, dict) or (event_filter is None)) return self._past_events(self._contract, 'LogCancel', LogCancel, number_of_past_blocks, event_filter) def create_order(self, pay_token: Address, pay_amount: Wad, buy_token: Address, buy_amount: Wad, expiration: int) -> Order: """Creates a new order. The `maker_fee`, `taker_fee` and `fee_recipient` fields are by default set to zero. Before signing the order and submitting it to the relayer, they may need to be populated using the `calculate_fees()` method of the `ZrxRelayerApi` class. Args: pay_token: Address of the ERC20 token you want to put on sale. pay_amount: Amount of the `pay_token` token you want to put on sale. buy_token: Address of the ERC20 token you want to be paid with. buy_amount: Amount of the `buy_token` you want to receive. expiration: Unix timestamp (in seconds) when the order will expire. Returns: New order as an instance of the :py:class:`pymaker.zrx.Order` class. """ assert(isinstance(pay_token, Address)) assert(isinstance(pay_amount, Wad)) assert(isinstance(buy_token, Address)) assert(isinstance(buy_amount, Wad)) assert(isinstance(expiration, int)) return Order(exchange=self, maker=Address(self.web3.eth.defaultAccount), taker=self._ZERO_ADDRESS, maker_fee=Wad(0), taker_fee=Wad(0), pay_token=pay_token, pay_amount=pay_amount, buy_token=buy_token, buy_amount=buy_amount, salt=self.random_salt(), fee_recipient=self._ZERO_ADDRESS, expiration=expiration, exchange_contract_address=self.address, ec_signature_r=None, ec_signature_s=None, ec_signature_v=None) def get_order_hash(self, order: Order) -> str: """Calculates hash of an order. Args: order: Order you want to calculate the hash of. Returns: Order hash as a hex string starting with `0x`. """ assert(isinstance(order, Order)) # the hash depends on the exchange contract address as well assert(order.exchange_contract_address == self.address) result = self._contract.call().getOrderHash(self._order_addresses(order), self._order_values(order)) return bytes_to_hexstring(array.array('B', [ord(x) for x in result]).tobytes()) def get_unavailable_buy_amount(self, order: Order) -> Wad: """Return the order amount which was either taken or cancelled. Args: order: Order you want to get the unavailable amount of. Returns: The unavailable amount of the order (i.e. the amount which was either taken or cancelled), expressed in terms of the `buy_token` token. """ assert(isinstance(order, Order)) return Wad(self._contract.call().getUnavailableTakerTokenAmount(hexstring_to_bytes(self.get_order_hash(order)))) def sign_order(self, order: Order) -> Order: """Signs an order so it can be submitted to the relayer. Order will be signed by the `web3.eth.defaultAccount` account. Args: order: Order you want to sign. Returns: Signed order. Copy of the order passed as a parameter with the `ec_signature_r`, `ec_signature_s` and `ec_signature_v` fields filled with signature values. """ assert(isinstance(order, Order)) signature = eth_sign(hexstring_to_bytes(self.get_order_hash(order)), self.web3) v, r, s = to_vrs(signature) signed_order = copy.copy(order) signed_order.ec_signature_r = bytes_to_hexstring(r) signed_order.ec_signature_s = bytes_to_hexstring(s) signed_order.ec_signature_v = v return signed_order def fill_order(self, order: Order, fill_buy_amount: Wad) -> Transact: """Fills an order. Args: order: The order to be filled. fill_buy_amount: The amount (in terms of `buy_token` of the original order) to be filled. Returns: A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction. """ assert(isinstance(order, Order)) assert(isinstance(fill_buy_amount, Wad)) return Transact(self, self.web3, self.abi, self.address, self._contract, 'fillOrder', [self._order_addresses(order), self._order_values(order), fill_buy_amount.value, True, order.ec_signature_v, hexstring_to_bytes(order.ec_signature_r), hexstring_to_bytes(order.ec_signature_s)]) def cancel_order(self, order: Order) -> Transact: """Cancels an order. Args: order: Order you want to cancel. Returns: A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction. """ assert(isinstance(order, Order)) return Transact(self, self.web3, self.abi, self.address, self._contract, 'cancelOrder', [self._order_addresses(order), self._order_values(order), order.buy_amount.value]) @staticmethod def _order_values(order): return [order.pay_amount.value, order.buy_amount.value, order.maker_fee.value, order.taker_fee.value, order.expiration, order.salt] @staticmethod def _order_addresses(order): return [order.maker.address, order.taker.address, order.pay_token.address, order.buy_token.address, order.fee_recipient.address] @staticmethod def random_salt() -> int: return random.randint(1, 2**256 - 1) def __repr__(self): return f"ZrxExchange('{self.address}')"
def setup_method(self): self.web3 = Web3(HTTPProvider("http://localhost:8555")) self.web3.eth.defaultAccount = self.web3.eth.accounts[0] self.our_address = Address(self.web3.eth.defaultAccount) self.ds_guard = DSGuard.deploy(self.web3)
def flipper(self, ilk: Ilk) -> Address: assert isinstance(ilk, Ilk) (flip, chop, lump) = self._contract.call().ilks(ilk.toBytes()) return Address(flip)
from web3 import Web3, HTTPProvider from pymaker import Address from pymaker.deployment import DssDeployment from pymaker.keys import register_keys from pymaker.numeric import Wad assert os.environ['ETH_RPC_URL'] web3 = Web3( HTTPProvider(endpoint_uri=os.environ['ETH_RPC_URL'], request_kwargs={"timeout": 10})) our_address = None if len(sys.argv) > 1: web3.eth.defaultAccount = sys.argv[ 1] # ex: 0x0000000000000000000000000000000aBcdef123 our_address = Address(web3.eth.defaultAccount) if len(sys.argv) > 2: register_keys( web3, [sys.argv[2]] ) # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass run_transactions = False else: run_transactions = False mcd = DssDeployment.from_node(web3) # Print a list of collaterals available for this deployment of MCD for collateral in mcd.collaterals.values(): print( f"Found {collateral.ilk.name:>9} - {collateral.gem.name():<20} with {collateral.adapter.dec():>2} decimals" )
def is_eth(self) -> bool: return self.address == Address( '0x0000000000000000000000000000000000000000')
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( "--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( "--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("--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}")) 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)) self.bands_config = ReloadableConfig(self.arguments.config) self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance) self.price_feed = PriceFeedFactory().create_price_feed( self.arguments.price_feed, 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 get_pair_address(self, token_a_address: Address, token_b_address: Address) -> Address: assert (isinstance(token_a_address, Address)) assert (isinstance(token_b_address, Address)) return Address(self._factory_contract.functions.getPair(token_a_address.address, token_b_address.address).call())
def fromBytes(urn: bytes): assert isinstance(urn, bytes) address = Address(Web3.toHex(urn[-20:])) return Urn(address)
create_surplus(mcd, flapper, deployment_address) joy = mcd.vat.dai(mcd.vow.address) assert joy > mcd.vat.sin(mcd.vow.address) + mcd.vow.bump() + mcd.vow.hump() assert (mcd.vat.sin(mcd.vow.address) - mcd.vow.sin()) - mcd.vow.ash() == Rad(0) assert mcd.vow.flap().transact() mint_mkr(mcd.mkr, our_address, Wad.from_number(10)) flapper.approve(mcd.mkr.address, directly(from_address=our_address)) bid = Wad.from_number(0.001) assert mcd.mkr.balance_of(our_address) > bid assert flapper.tend(flapper.kicks(), mcd.vow.bump(), bid).transact(from_address=our_address) nobody = Address("0x0000000000000000000000000000000000000000") class TestShutdownModule: """This test must be run after other MCD tests because it will leave the testchain `cage`d.""" def test_init(self, mcd, deployment_address, our_address): assert mcd.esm is not None assert isinstance(mcd.esm, ShutdownModule) assert isinstance(mcd.esm.address, Address) assert mcd.esm.sum() == Wad(0) assert mcd.esm.min() > Wad(0) assert mcd.end.live() joy = mcd.vat.dai(mcd.vow.address) awe = mcd.vat.sin(mcd.vow.address) # If `test_shutdown.py` is run in isolation, create a flap auction to exercise `yank`
def gem(self) -> DSToken: address = Address(self._contract.call().gem()) return DSToken(self.web3, address)
def from_json(web3: Web3, conf: str): conf = json.loads(conf) pause = DSPause(web3, Address(conf['MCD_PAUSE'])) vat = Vat(web3, Address(conf['MCD_VAT'])) vow = Vow(web3, Address(conf['MCD_VOW'])) jug = Jug(web3, Address(conf['MCD_JUG'])) cat = Cat(web3, Address(conf['MCD_CAT'])) dai = DSToken(web3, Address(conf['MCD_DAI'])) dai_adapter = DaiJoin(web3, Address(conf['MCD_JOIN_DAI'])) flapper = Flapper(web3, Address(conf['MCD_FLAP'])) flopper = Flopper(web3, Address(conf['MCD_FLOP'])) pot = Pot(web3, Address(conf['MCD_POT'])) mkr = DSToken(web3, Address(conf['MCD_GOV'])) spotter = Spotter(web3, Address(conf['MCD_SPOT'])) ds_chief = DSChief(web3, Address(conf['MCD_ADM'])) esm = ShutdownModule(web3, Address(conf['MCD_ESM'])) end = End(web3, Address(conf['MCD_END'])) proxy_registry = ProxyRegistry(web3, Address(conf['PROXY_REGISTRY'])) dss_proxy_actions = DssProxyActionsDsr( web3, Address(conf['PROXY_ACTIONS_DSR'])) cdp_manager = CdpManager(web3, Address(conf['CDP_MANAGER'])) collaterals = {} for name in DssDeployment.Config._infer_collaterals_from_addresses( conf.keys()): ilk = Ilk(name[0].replace('_', '-')) if name[1] == "ETH": gem = DSEthToken(web3, Address(conf[name[1]])) else: gem = DSToken(web3, Address(conf[name[1]])) if name[1] in ['USDC', 'WBTC']: adapter = GemJoin5(web3, Address(conf[f'MCD_JOIN_{name[0]}'])) else: adapter = GemJoin(web3, Address(conf[f'MCD_JOIN_{name[0]}'])) # PIP contract may be a DSValue, OSM, or bogus address. pip_address = Address(conf[f'PIP_{name[1]}']) network = DssDeployment.NETWORKS.get(web3.net.version, "testnet") if network == "testnet": pip = DSValue(web3, pip_address) else: pip = OSM(web3, pip_address) collateral = Collateral( ilk=ilk, gem=gem, adapter=adapter, flipper=Flipper(web3, Address(conf[f'MCD_FLIP_{name[0]}'])), pip=pip) collaterals[ilk.name] = collateral return DssDeployment.Config(pause, vat, vow, jug, cat, flapper, flopper, pot, dai, dai_adapter, mkr, spotter, ds_chief, esm, end, proxy_registry, dss_proxy_actions, cdp_manager, collaterals)
def other_address(web3): return Address(web3.eth.accounts[2])