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()))
def write(self, code: str): assert (isinstance(code, str)) if code.startswith('0x'): b32_code = hexstring_to_bytes(code) else: b32_code = hexstring_to_bytes('0x' + code) return Transact(self, self.web3, self.abi, self.address, self._contract, 'write', [b32_code])
def execute(self, code: str, calldata: Calldata) -> Transact: assert (isinstance(code, str)) assert (isinstance(calldata, Calldata)) if code.startswith('0x'): b32_code = hexstring_to_bytes(code) else: b32_code = hexstring_to_bytes('0x' + code) return Transact(self, self.web3, self.abi, self.address, self._contract, 'execute(bytes,bytes)', [b32_code, calldata.as_bytes()])
def read(self, code: str) -> Optional[Address]: assert (isinstance(code, str)) if code.startswith('0x'): b32_code = hexstring_to_bytes(code) else: b32_code = hexstring_to_bytes('0x' + code) address = Address(self._contract.call().read(b32_code)) if address == Address('0x0000000000000000000000000000000000000000'): return None else: return address
def from_json(ether_delta, data: dict): assert (isinstance(data, dict)) return Order(ether_delta=ether_delta, maker=Address(data['user']), pay_token=Address(data['tokenGive']), pay_amount=Wad(int(data['amountGive'])), buy_token=Address(data['tokenGet']), buy_amount=Wad(int(data['amountGet'])), expires=int(data['expires']), nonce=int(data['nonce']), v=int(data['v']), r=hexstring_to_bytes(data['r']), s=hexstring_to_bytes(data['s']))
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)) # TODO duplicate code below signed_hash = eth_sign(self.web3, hexstring_to_bytes(self.get_order_hash(order)))[2:] r = bytes.fromhex(signed_hash[0:64]) s = bytes.fromhex(signed_hash[64:128]) v = ord(bytes.fromhex(signed_hash[128:130])) 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 place_order(self, pair: str, is_sell: bool, price: Wad, amount: Wad) -> str: assert (isinstance(pair, str)) assert (isinstance(is_sell, bool)) assert (isinstance(price, Wad)) assert (isinstance(amount, Wad)) self.logger.info( f"Placing order ({'SELL' if is_sell else 'BUY'}, amount {amount} of {pair}," f" price {price})...") # build order order = self._build_order(amount, price, is_sell, pair) result = self._http_post_signed(f"/{self.version}/orders/build", order) order_id = result['data']['order']['id'] unsignedOrder = result['data']['order']['json'] fee = self._get_fee_rate(result) # sign order signature = eth_sign(hexstring_to_bytes(order_id), self.web3) result = self._http_post_signed(f"/{self.version}/orders", { "orderId": order_id, "signature": signature }) self.logger.info( f"Placed order ({'SELL' if is_sell else 'BUY'}, amount {amount} of {pair}," f" price {price}, fee {float(fee)*100:.4f}%) as #{order_id}") return order_id
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.keccak( 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 write(self, code: str): assert (isinstance(code, str)) b32_code = hexstring_to_bytes(code) return Transact(self, self.web3, self.abi, self.address, self._contract, 'write', [b32_code])
def read(self, code: str) -> Address: assert (isinstance(code, str)) b32_code = hexstring_to_bytes(code) address = self._contract.call().read(b32_code) return Address(address)
def place_order(self, pair: str, is_sell: bool, price: Wad, amount: Wad) -> str: assert(isinstance(pair, str)) assert(isinstance(is_sell, bool)) assert(isinstance(price, Wad)) assert(isinstance(amount, Wad)) self.logger.info(f"Placing order ({'SELL' if is_sell else 'BUY'}, amount {amount} of {pair}," f" price {price})...") # build order order = { "amount": str(amount), "price": str(price), "side": 'sell' if is_sell else 'buy', "marketId": pair, } result = self._http_post_signed("/v2/orders/build", order) order_id = result['data']['order']['id'] unsignedOrder = result['data']['order']['json'] fee = result['data']['order']['feeAmount'] # sign order signature = eth_sign(hexstring_to_bytes(order_id), self.web3) result = self._http_post_signed("/v2/orders", {"orderId": order_id, "signature": signature}) self.logger.info(f"Placed order ({'SELL' if is_sell else 'BUY'}, amount {amount} of {pair}," f" price {price}, fee {float(fee)*100:.4f}%) as #{order_id}") return order_id
def cancel_order(self, order: Order) -> bool: assert(isinstance(order, Order)) nonce = self.next_nonce() signed_data = keccak_256(encode_bytes(hexstring_to_bytes(order.order_hash)) + encode_uint256(nonce)).digest() signature = eth_sign(signed_data, self.idex.web3) v, r, s = to_vrs(signature) data = { 'orderHash': order.order_hash, 'nonce': str(nonce), 'address': self._our_address(), 'v': v, 'r': bytes_to_hexstring(r), 's': bytes_to_hexstring(s) } self.logger.info(f"Cancelling order #{order.order_id}...") result = self._http_post("/cancel", data) success = result['success'] == 1 if success: self.logger.info(f"Cancelled order #{order.order_id}") else: self.logger.info(f"Failed to cancel order #{order.order_id}") return success
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 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 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: assert (isinstance(order, Order)) # TODO duplicate code below signed_hash = eth_sign(self.web3, hexstring_to_bytes( self.get_order_hash(order)))[2:] r = bytes.fromhex(signed_hash[0:64]) s = bytes.fromhex(signed_hash[64:128]) v = ord(bytes.fromhex(signed_hash[128:130])) 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 test_permit_specific_addresses_and_sig(self): # when self.ds_guard.permit(src=Address('0x1111111111222222222211111111112222222222'), dst=Address('0x3333333333444444444433333333334444444444'), sig=hexstring_to_bytes('0xab121fd7')).transact() # then assert self.can_call(src='0x1111111111222222222211111111112222222222', dst='0x3333333333444444444433333333334444444444', sig='0xab121fd7') # and assert not self.can_call(src='0x3333333333444444444433333333334444444444', dst='0x1111111111222222222211111111112222222222', sig='0xab121fd7') # different addresses assert not self.can_call(src='0x1111111111222222222211111111112222222222', dst='0x3333333333444444444433333333334444444444', sig='0xab121fd8') # different sig
def eth_sign_with_keyfile(message: bytes, raw: bool, keyfile: str, password: str): assert(isinstance(message, bytes)) assert(isinstance(raw, bool)) assert(isinstance(keyfile, str)) assert(isinstance(password, str)) if not raw: message = hexstring_to_bytes(Eth._recoveryMessageHash(data=message)) key = eth_keyfile.decode_keyfile_json(eth_keyfile.load_keyfile(keyfile), bytes(password, 'utf-8')) pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(message, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) signature_hex = signature.hex()[0:128] + int_to_bytes(ord(bytes.fromhex(signature.hex()[128:130]))+27).hex() return '0x' + signature_hex
def eth_sign(message: bytes, web3: Web3): assert(isinstance(message, bytes)) assert(isinstance(web3, Web3)) # as `EthereumTesterProvider` does not support `eth_sign`, we implement it ourselves if str(web3.providers[0]) == 'EthereumTesterProvider': key = k0 msg = hexstring_to_bytes(Eth._recoveryMessageHash(data=message)) pk = PrivateKey(key, raw=True) signature = pk.ecdsa_recoverable_serialize( pk.ecdsa_sign_recoverable(msg, raw=True) ) signature = signature[0] + utils.bytearray_to_bytestr([signature[1]]) signature_hex = signature.hex()[0:128] + int_to_bytes(ord(bytes.fromhex(signature.hex()[128:130]))+27).hex() return '0x' + signature_hex return web3.manager.request_blocking( "eth_sign", [web3.eth.defaultAccount, encode_hex(message)], )
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 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 can_call(self, src: str, dst: str, sig: str) -> bool: return self.ds_guard._contract.call().canCall(src, dst, hexstring_to_bytes(sig))
def get_unavailable_buy_amount(self, order: Order) -> Wad: assert (isinstance(order, Order)) return Wad(self._contract.call().getUnavailableTakerTokenAmount( hexstring_to_bytes(self.get_order_hash(order))))
def test_hexstring_to_bytes(): assert hexstring_to_bytes('0x00') == bytes([0x00]) assert hexstring_to_bytes('0x010203') == bytes([0x01, 0x02, 0x03]) assert hexstring_to_bytes('0xffff') == bytes([0xff, 0xff])