Ejemplo n.º 1
0
    def cancel_order(
        self,
        order: Order,
        tx_params: Optional[TxParams] = None,
        view_only: bool = False,
    ) -> Union[HexBytes, bytes]:
        """Cancel an order.

        See the specification docs for `cancelOrder
        <https://github.com/0xProject/0x-protocol-specification/blob/master
        /v2/v2-specification.md#cancelorder>`_.

        :param order: instance of :class:`zero_ex.order_utils.Order`
        :param tx_params: default None, :class:`TxParams` transaction params
        :param view_only: default False, boolean of whether to transact or
            view only

        :returns: transaction hash
        """
        assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
        maker_address = self._validate_and_checksum_address(
            order["makerAddress"])

        if tx_params and tx_params.from_:
            self._raise_if_maker_not_canceller(
                maker_address,
                self._validate_and_checksum_address(tx_params.from_),
            )
        elif self._web3_eth.defaultAccount:
            self._raise_if_maker_not_canceller(maker_address,
                                               self._web3_eth.defaultAccount)
        func = self._exchange.functions.cancelOrder(order)
        return self._invoke_function_call(func=func,
                                          tx_params=tx_params,
                                          view_only=view_only)
Ejemplo n.º 2
0
def test_query_orders(test_client, pydex_client, asset_infos):
    """Test whether the app can return a valid list of orders,
    filtered by query params
    """
    orders_params = pydex_client.make_orders_query(
        maker_asset_data=asset_infos.VETH_ASSET_DATA,
        taker_asset_data=asset_infos.LONG_ASSET_DATA,
        include_maybe_fillables=True)
    res = test_client.get(pydex_client.get_orders_url,
                          query_string=orders_params)
    assert res.status_code == 200
    res = res.get_json()
    assert_valid(res, "/relayerApiOrdersResponseSchema")
    assert res["records"][0]["order"][
        "makerAssetData"] == asset_infos.VETH_ASSET_DATA
    assert res["records"][0]["order"][
        "takerAssetData"] == asset_infos.LONG_ASSET_DATA
    expected_maker_asset_proxy_id = ERC20_PROXY_ID
    orders_params = pydex_client.make_orders_query(
        maker_asset_proxy_id=expected_maker_asset_proxy_id,
        include_maybe_fillables=True)
    res = test_client.get(pydex_client.get_orders_url,
                          query_string=orders_params)
    assert res.status_code == 200
    res = res.get_json()
    assert_valid(res, "/relayerApiOrdersResponseSchema")
    assert res["records"][0]["order"][
        "makerAssetData"][:10] == expected_maker_asset_proxy_id
Ejemplo n.º 3
0
    def fill_or_kill_order(
        self,
        order: Order,
        taker_amount: int,
        signature: str,
        tx_params: Optional[TxParams] = None,
        view_only: bool = False,
    ) -> Union[HexBytes, bytes]:
        """Attemp to `fillOrder`, revert if fill is not exact amount.

        :param order: instance of :class:`zero_ex.order_utils.Order`
        :param taker_amount: integer taker amount in Wei (1 Wei is 10e-18 ETH)
        :param signature: str or hexstr or bytes of order hash signature
        :param tx_params: default None, :class:`TxParams` transaction params
        :param view_only: default False, boolean of whether to transact or
            view only

        :returns: transaction hash
        """
        assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
        is_valid_signature(
            self._provider,
            generate_order_hash_hex(order, self.address),
            signature,
            order["makerAddress"],
        )
        # safeguard against fractional inputs
        taker_fill_amount = int(taker_amount)
        normalized_signature = bytes.fromhex(remove_0x_prefix(signature))
        func = self._exchange.functions.fillOrKillOrder(
            order, taker_fill_amount, normalized_signature)
        return self._invoke_function_call(func=func,
                                          tx_params=tx_params,
                                          view_only=view_only)
Ejemplo n.º 4
0
def get_asset_pairs():
    """GET AssetPairs endpoint retrieves a list of available asset pairs and the
    information required to trade them.
    http://sra-spec.s3-website-us-east-1.amazonaws.com/#operation/getAssetPairs
    """
    current_app.logger.info("############ GETTING ASSET PAIRS")
    network_id = NetworkId(int(request.args.get("networkId"))).value
    assert network_id == current_app.config["PYDEX_NETWORK_ID"], \
        f"networkId={network_id} not supported"
    page = int(request.args.get("page", current_app.config["OB_DEFAULT_PAGE"]))
    per_page = int(
        request.args.get("per_page",
                         current_app.config["OB_DEFAULT_PER_PAGE"]))
    asset_data_a = request.args.get("assetDataA")
    asset_data_b = request.args.get("assetDataB")
    include_maybe_fillables = bool(request.args.get("include_maybe_fillables"))
    asset_pairs, asset_pairs_count = Orderbook.get_asset_pairs(
        asset_data_a=asset_data_a,
        asset_data_b=asset_data_b,
        page=page,
        per_page=per_page,
        include_maybe_fillables=include_maybe_fillables)
    res = {
        "total": asset_pairs_count,
        "perPage": per_page,
        "page": page,
        "records": asset_pairs
    }
    assert_valid(res, "/relayerApiAssetDataPairsResponseSchema")
    return current_app.response_class(response=json.dumps(res),
                                      status=200,
                                      mimetype='application/json')
Ejemplo n.º 5
0
def test_exchange_wrapper__fill_order(
    accounts,
    exchange_wrapper,  # pylint: disable=redefined-outer-name
    ganache_provider,
    weth_asset_data,
):
    """Test filling an order."""
    taker = accounts[0]
    maker = accounts[1]
    exchange_address = exchange_wrapper.contract_address
    order = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data)
    order_hash = generate_order_hash_hex(order=order,
                                         exchange_address=exchange_address)
    order_signature = sign_hash_to_bytes(ganache_provider, maker, order_hash)

    tx_hash = exchange_wrapper.fill_order.send_transaction(
        order=order,
        taker_asset_fill_amount=order["takerAssetAmount"],
        signature=order_signature,
        tx_params=TxParams(from_=taker),
    )
    assert_valid(tx_hash.hex(), "/hexSchema")

    fill_event = exchange_wrapper.get_fill_event(tx_hash)
    assert_fill_log(fill_event[0].args, maker, taker, order, order_hash)
Ejemplo n.º 6
0
def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
    """Calculate the hash of the given order as a hexadecimal string.

    :param order: The order to be hashed.  Must conform to `the 0x order JSON schema <https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schema.json>`_.
    :param exchange_address: The address to which the 0x Exchange smart
        contract has been deployed.
    :returns: A string, of ASCII hex digits, representing the order hash.

    >>> generate_order_hash_hex(
    ...     {
    ...         'makerAddress': "0x0000000000000000000000000000000000000000",
    ...         'takerAddress': "0x0000000000000000000000000000000000000000",
    ...         'feeRecipientAddress': "0x0000000000000000000000000000000000000000",
    ...         'senderAddress': "0x0000000000000000000000000000000000000000",
    ...         'makerAssetAmount': "1000000000000000000",
    ...         'takerAssetAmount': "1000000000000000000",
    ...         'makerFee': "0",
    ...         'takerFee': "0",
    ...         'expirationTimeSeconds': "12345",
    ...         'salt': "12345",
    ...         'makerAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...         'takerAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...     },
    ...     exchange_address="0x0000000000000000000000000000000000000000",
    ... )
    '55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692'
    """  # noqa: E501 (line too long)
    assert_is_address(exchange_address, "exchange_address")
    assert_valid(order_to_jsdict(order, exchange_address), "/orderSchema")

    def pad_20_bytes_to_32(twenty_bytes: bytes):
        return bytes(12) + twenty_bytes

    def int_to_32_big_endian_bytes(i: int):
        return i.to_bytes(32, byteorder="big")

    eip712_domain_struct_hash = keccak(
        _Constants.eip712_domain_struct_header +
        pad_20_bytes_to_32(to_bytes(hexstr=exchange_address)))

    eip712_order_struct_hash = keccak(
        _Constants.eip712_order_schema_hash +
        pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"])) +
        pad_20_bytes_to_32(to_bytes(hexstr=order["takerAddress"])) +
        pad_20_bytes_to_32(to_bytes(hexstr=order["feeRecipientAddress"])) +
        pad_20_bytes_to_32(to_bytes(hexstr=order["senderAddress"])) +
        int_to_32_big_endian_bytes(int(order["makerAssetAmount"])) +
        int_to_32_big_endian_bytes(int(order["takerAssetAmount"])) +
        int_to_32_big_endian_bytes(int(order["makerFee"])) +
        int_to_32_big_endian_bytes(int(order["takerFee"])) +
        int_to_32_big_endian_bytes(int(order["expirationTimeSeconds"])) +
        int_to_32_big_endian_bytes(int(order["salt"])) +
        keccak(to_bytes(hexstr=order["makerAssetData"].hex())) +
        keccak(to_bytes(hexstr=order["takerAssetData"].hex())))

    return keccak(_Constants.eip191_header + eip712_domain_struct_hash +
                  eip712_order_struct_hash).hex()
Ejemplo n.º 7
0
def test_query_orderbook(test_client, pydex_client, asset_infos):
    """Test whether the app can return a valid orderbook"""
    orderbook_params = pydex_client.make_orderbook_query(
        base_asset_data=asset_infos.VETH_ASSET_DATA,
        quote_asset_data=asset_infos.LONG_ASSET_DATA)
    res = test_client.get(pydex_client.orderbook_url,
                          query_string=orderbook_params)
    assert res.status_code == 200
    res = res.get_json()
    assert_valid(res, "/relayerApiOrderbookResponseSchema")
def test_exchange_wrapper__fill_order(
    accounts,
    exchange_wrapper,  # pylint: disable=redefined-outer-name
    ganache_provider,
    weth_asset_data,
    zrx_asset_data,
):
    """Test filling an order."""
    taker = accounts[0]
    maker = accounts[1]
    exchange_address = exchange_wrapper.contract_address
    order = create_test_order(maker, 1, weth_asset_data, 1, zrx_asset_data)
    order_hash = generate_order_hash_hex(order=order,
                                         exchange_address=exchange_address,
                                         chain_id=1337)
    order_signature = sign_hash(ganache_provider, maker, order_hash)

    web3 = Web3(ganache_provider)
    web3.eth.setGasPriceStrategy(  # pylint: disable=no-member
        rpc_gas_price_strategy)

    fill_results = exchange_wrapper.fill_order.call(
        order=order,
        taker_asset_fill_amount=order["takerAssetAmount"],
        signature=order_signature,
        tx_params=TxParams(
            from_=taker,
            value=web3.eth.generateGasPrice()  # pylint: disable=no-member
            * 150000,
        ),
    )
    assert fill_results["makerAssetFilledAmount"] == 1
    assert fill_results["takerAssetFilledAmount"] == 1
    assert fill_results["makerFeePaid"] == 0
    assert fill_results["takerFeePaid"] == 0
    assert (fill_results["protocolFeePaid"] == web3.eth.generateGasPrice() *
            150000  # pylint: disable=no-member
            )

    tx_hash = exchange_wrapper.fill_order.send_transaction(
        order=order,
        taker_asset_fill_amount=order["takerAssetAmount"],
        signature=order_signature,
        tx_params=TxParams(
            from_=taker,
            value=web3.eth.generateGasPrice()  # pylint: disable=no-member
            * 150000,
        ),
    )
    assert_valid(tx_hash.hex(), "/hexSchema")

    fill_event = exchange_wrapper.get_fill_event(tx_hash)
    assert_fill_log(fill_event[0].args, maker, taker, order, order_hash)
    def post_signed_order(self, order):
        """Validate and post a signed order to PyDEX app

        Keyword Arguments:
        order -- SignedOrder object to post
        """
        order_json = order.update().to_json()
        assert_valid(order_json, "/signedOrderSchema")
        res = requests.post("{}{}".format(self._pydex_api_url,
                                          self.post_order_url),
                            json=order_json)
        return res
Ejemplo n.º 10
0
def jsdict_order_to_struct(jsdict: dict) -> Order:
    r"""Convert a JSON-schema-compatible dict order to a Web3-compatible struct.

    More specifically, do explicit encoding of the `bytes`:code: fields.

    >>> import pprint
    >>> pprint.pprint(jsdict_order_to_struct(
    ...     {
    ...         'makerAddress': "0x0000000000000000000000000000000000000000",
    ...         'takerAddress': "0x0000000000000000000000000000000000000000",
    ...         'feeRecipientAddress': "0x0000000000000000000000000000000000000000",
    ...         'senderAddress': "0x0000000000000000000000000000000000000000",
    ...         'makerAssetAmount': 1000000000000000000,
    ...         'takerAssetAmount': 1000000000000000000,
    ...         'makerFee': 0,
    ...         'takerFee': 0,
    ...         'expirationTimeSeconds': 12345,
    ...         'salt': 12345,
    ...         'makerAssetData': "0x0000000000000000000000000000000000000000",
    ...         'takerAssetData': "0x0000000000000000000000000000000000000000",
    ...         'exchangeAddress': "0x0000000000000000000000000000000000000000",
    ...     },
    ... ))
    {'expirationTimeSeconds': 12345,
     'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
     'makerAddress': '0x0000000000000000000000000000000000000000',
     'makerAssetAmount': 1000000000000000000,
     'makerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                       b'\x00\x00\x00\x00\x00\x00\x00\x00',
     'makerFee': 0,
     'salt': 12345,
     'senderAddress': '0x0000000000000000000000000000000000000000',
     'takerAddress': '0x0000000000000000000000000000000000000000',
     'takerAssetAmount': 1000000000000000000,
     'takerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
                       b'\x00\x00\x00\x00\x00\x00\x00\x00',
     'takerFee': 0}
    """  # noqa: E501 (line too long)
    assert_valid(jsdict, "/orderSchema")

    order = cast(Order, copy(jsdict))

    order["makerAssetData"] = bytes.fromhex(
        remove_0x_prefix(jsdict["makerAssetData"]))
    order["takerAssetData"] = bytes.fromhex(
        remove_0x_prefix(jsdict["takerAssetData"]))

    del order["exchangeAddress"]  # type: ignore
    # silence mypy pending release of
    # https://github.com/python/mypy/issues/3550

    return order
Ejemplo n.º 11
0
def order_to_jsdict(
        order: Order,
        exchange_address="0x0000000000000000000000000000000000000000") -> dict:
    """Convert a Web3-compatible order struct to a JSON-schema-compatible dict.

    More specifically, do explicit decoding for the `bytes`:code: fields.

    >>> import pprint
    >>> pprint.pprint(order_to_jsdict(
    ...     {
    ...         'makerAddress': "0x0000000000000000000000000000000000000000",
    ...         'takerAddress': "0x0000000000000000000000000000000000000000",
    ...         'feeRecipientAddress':
    ...             "0x0000000000000000000000000000000000000000",
    ...         'senderAddress': "0x0000000000000000000000000000000000000000",
    ...         'makerAssetAmount': 1,
    ...         'takerAssetAmount': 1,
    ...         'makerFee': 0,
    ...         'takerFee': 0,
    ...         'expirationTimeSeconds': 1,
    ...         'salt': 1,
    ...         'makerAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...         'takerAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...     },
    ... ))
    {'exchangeAddress': '0x0000000000000000000000000000000000000000',
     'expirationTimeSeconds': 1,
     'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
     'makerAddress': '0x0000000000000000000000000000000000000000',
     'makerAssetAmount': 1,
     'makerAssetData': '0x0000000000000000000000000000000000000000',
     'makerFee': 0,
     'salt': 1,
     'senderAddress': '0x0000000000000000000000000000000000000000',
     'takerAddress': '0x0000000000000000000000000000000000000000',
     'takerAssetAmount': 1,
     'takerAssetData': '0x0000000000000000000000000000000000000000',
     'takerFee': 0}
    """
    jsdict = cast(Dict, copy(order))

    # encode bytes fields
    jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex()
    jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex()

    jsdict["exchangeAddress"] = exchange_address

    assert_valid(jsdict, "/orderSchema")

    return jsdict
Ejemplo n.º 12
0
def get_order_by_hash(order_hash):
    """GET Order endpoint retrieves the order by order hash.
    http://sra-spec.s3-website-us-east-1.amazonaws.com/#operation/getOrder
    """
    current_app.logger.info("############ GETTING ORDER BY HASH")
    assert_valid(order_hash, "/orderHashSchema")
    network_id = request.args.get("networkId",
                                  current_app.config["PYDEX_NETWORK_ID"])
    assert network_id == current_app.config[
        "PYDEX_NETWORK_ID"], f"networkId={network_id} not supported"
    res = Orderbook.get_order_by_hash(order_hash=order_hash)
    assert_valid(res, "/relayerApiOrderSchema")
    return current_app.response_class(response=json.dumps(res),
                                      status=200,
                                      mimetype='application/json')
Ejemplo n.º 13
0
def post_order():
    """POST Order endpoint submits an order to the Relayer.
    http://sra-spec.s3-website-us-east-1.amazonaws.com/#operation/postOrder
    """
    current_app.logger.info("############ POSTING ORDER")
    network_id = request.args.get("networkId",
                                  current_app.config["PYDEX_NETWORK_ID"])
    assert network_id == current_app.config[
        "PYDEX_NETWORK_ID"], f"networkId={network_id} not supported"
    current_app.logger.info(request.json)
    assert_valid(request.json, "/signedOrderSchema")
    Orderbook.add_order(order_json=request.json)
    return current_app.response_class(response={'success': True},
                                      status=200,
                                      mimetype='application/json')
Ejemplo n.º 14
0
def test_assert_valid_caches_resources():
    """Test that the JSON ref resolver in `assert_valid()` caches resources

    In order to test the cache we much access the private class of
    `json_schemas` and reset the LRU cache on `_LocalRefResolver`.
    For this to happen, we need to disable errror `W0212`
    on _LOCAL_RESOLVER
    """
    _LOCAL_RESOLVER._remote_cache.cache_clear()  # pylint: disable=W0212

    assert_valid(EMPTY_ORDER, "/orderSchema")
    cache_info = (
        _LOCAL_RESOLVER._remote_cache.cache_info()  # pylint: disable=W0212
    )
    assert cache_info.currsize == 4
    assert cache_info.hits > 0
def test_exchange_wrapper__batch_fill_orders(
    accounts,
    exchange_wrapper,  # pylint: disable=redefined-outer-name
    ganache_provider,
    weth_asset_data,
):
    """Test filling a batch of orders."""
    taker = accounts[0]
    maker = accounts[1]
    exchange_address = exchange_wrapper.contract_address
    orders = []
    order_1 = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data)
    order_2 = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data)
    orders.append(order_1)
    orders.append(order_2)
    order_hashes = [
        generate_order_hash_hex(order=order,
                                exchange_address=exchange_address,
                                chain_id=1337) for order in orders
    ]
    order_signatures = [
        sign_hash(ganache_provider, maker, order_hash)
        for order_hash in order_hashes
    ]
    taker_amounts = [order["takerAssetAmount"] for order in orders]

    web3 = Web3(ganache_provider)
    web3.eth.setGasPriceStrategy(  # pylint: disable=no-member
        rpc_gas_price_strategy)

    tx_hash = exchange_wrapper.batch_fill_orders.send_transaction(
        orders=orders,
        taker_asset_fill_amounts=taker_amounts,
        signatures=order_signatures,
        tx_params=TxParams(
            from_=taker,
            value=2 * web3.eth.generateGasPrice()  # pylint: disable=no-member
            * 150000,
        ),
    )
    assert_valid(tx_hash.hex(), "/hexSchema")

    fill_events = exchange_wrapper.get_fill_event(tx_hash)
    for index, order in enumerate(orders):
        assert_fill_log(fill_events[index].args, maker, taker, order,
                        order_hashes[index])
Ejemplo n.º 16
0
    def from_json(
        cls,
        order_json,
        check_validity=False,
        include_signature=True,
    ):
        """Given a json representation of a signed order, return a SignedOrder object

        Args:
            order_json (dict): a dict conforming to "/signedOrderSchema"
                or "/orderSchema" (dependign on whether `include_signature`
                is set to True or False) schemas can be found
                `here <https://github.com/0xProject/0x-monorepo/tree/development/
                packages/json-schemas/schemas>`__
            check_validity (bool): whether we should do an explicit check
                to make sure the passed in dict adheres to the required
                schema (default: True)
            include_signature (bool): whether the object is expected to have
                the signature on it or not. This will affect whether
                "/signedOrderSchema" or "/orderSchema" is used for validation
                (default: True)
        """
        order = cls()
        if check_validity:
            if include_signature:
                assert_valid(order_json, "/signedOrderSchema")
            else:
                assert_valid(order_json, "/orderSchema")
        order.maker_address = order_json["makerAddress"]
        order.taker_address = order_json["takerAddress"]
        order.maker_fee = order_json["makerFee"]
        order.taker_fee = order_json["takerFee"]
        order.sender_address = order_json["senderAddress"]
        order.maker_asset_amount = order_json["makerAssetAmount"]
        order.taker_asset_amount = order_json["takerAssetAmount"]
        order.maker_asset_data = order_json["makerAssetData"]
        order.taker_asset_data = order_json["takerAssetData"]
        order.salt = order_json["salt"]
        order.exchange_address = order_json["exchangeAddress"]
        order.fee_recipient_address = order_json["feeRecipientAddress"]
        order.expiration_time_seconds = order_json["expirationTimeSeconds"]
        if include_signature:
            order.signature = order_json["signature"]
        order.update()
        return order
Ejemplo n.º 17
0
    def fill_order(
        self,
        order: Order,
        taker_amount: int,
        signature: str,
        tx_params: Optional[TxParams] = None,
        view_only: bool = False,
    ) -> Union[HexBytes, bytes]:
        """Fill a signed order with given amount of taker asset.

        This is the most basic way to fill an order. All of the other methods
        call fillOrder under the hood with additional logic. This function
        will attempt to fill the amount specified by the caller. However, if
        the remaining fillable amount is less than the amount specified, the
        remaining amount will be filled. Partial fills are allowed when
        filling orders.

        See the specification docs for `fillOrder
        <https://github.com/0xProject/0x-protocol-specification/blob/master
        /v2/v2-specification.md#fillorder>`_.

        :param order: instance of :class:`zero_ex.order_utils.Order`
        :param taker_amount: integer taker amount in Wei (1 Wei is 10e-18 ETH)
        :param signature: str or hexstr or bytes of order hash signature
        :param tx_params: default None, :class:`TxParams` transaction params
        :param view_only: default False, boolean of whether to transact or
            view only

        :returns: transaction hash
        """
        assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
        is_valid_signature(
            self._provider,
            generate_order_hash_hex(order, self.address),
            signature,
            order["makerAddress"],
        )
        # safeguard against fractional inputs
        taker_fill_amount = int(taker_amount)
        normalized_signature = bytes.fromhex(remove_0x_prefix(signature))
        func = self._exchange.functions.fillOrder(order, taker_fill_amount,
                                                  normalized_signature)
        return self._invoke_function_call(func=func,
                                          tx_params=tx_params,
                                          view_only=view_only)
Ejemplo n.º 18
0
def test_post_order(test_client, pydex_client, make_veth_signed_order):
    """Make sure posting order returns success and order exists
    in database."""
    order = make_veth_signed_order(
        asset_type="LONG",
        qty=0.0001,
        price=0.5,
        side="BUY",
    )
    res = test_client.post(pydex_client.post_order_url, json=order.to_json())
    assert res.status_code == 200
    # Retrieve order via get order endpoint
    res = test_client.get("{}{}".format(pydex_client.get_order_url,
                                        order.hash))
    assert res.status_code == 200
    res = res.get_json()
    assert_valid(res, "/relayerApiOrderSchema")
    assert res["order"] == order.to_json()
Ejemplo n.º 19
0
def get_orders():
    """GET Orders endpoint retrieves a list of orders given query parameters.
    http://sra-spec.s3-website-us-east-1.amazonaws.com/#operation/getOrders
    """
    current_app.logger.info("############ GETTING ORDERS")
    network_id = NetworkId(int(request.args.get("networkId"))).value
    assert network_id == current_app.config["PYDEX_NETWORK_ID"], \
        f"networkId={network_id} not supported"
    page = int(request.args.get("page", current_app.config["OB_DEFAULT_PAGE"]))
    per_page = int(
        request.args.get("per_page",
                         current_app.config["OB_DEFAULT_PER_PAGE"]))
    orders, orders_count = Orderbook.get_orders(
        maker_asset_proxy_id=request.args.get("makerAssetProxyId"),
        taker_asset_proxy_id=request.args.get("takerAssetProxyId"),
        maker_asset_address=request.args.get("makerAssetAddress"),
        taker_asset_address=request.args.get("takerAssetAddress"),
        exchange_address=request.args.get("exchangeAddress"),
        sender_address=request.args.get("takerAssetAddress"),
        maker_asset_data=request.args.get("makerAssetData")
        or request.args.get("traderAssetData"),
        taker_asset_data=request.args.get("takerAssetData")
        or request.args.get("traderAssetData"),
        maker_address=request.args.get("makerAddress")
        or request.args.get("traderAddress"),
        taker_address=request.args.get("takerAddress")
        or request.args.get("traderAddress"),
        fee_recipient_address=request.args.get("feeRecipient"),
        page=page,
        per_page=per_page,
        include_maybe_fillables=bool(
            request.args.get("include_maybe_fillables")))
    res = {
        "total": orders_count,
        "perPage": per_page,
        "page": page,
        "records": orders
    }
    assert_valid(res, "/relayerApiOrdersResponseSchema")
    return current_app.response_class(response=json.dumps(res),
                                      status=200,
                                      mimetype='application/json')
Ejemplo n.º 20
0
    def assert_valid(self, method_name: str, parameter_name: str,
                     argument_value: Any) -> None:
        """Raise an exception if method input is not valid.

        :param method_name: Name of the method whose input is to be validated.
        :param parameter_name: Name of the parameter whose input is to be
            validated.
        :param argument_value: Value of argument to parameter to be validated.
        """
        if parameter_name == "order":
            json_schemas.assert_valid(
                order_to_jsdict(argument_value, self.contract_address),
                "/orderSchema",
            )

        if parameter_name == "orders":
            for order in argument_value:
                json_schemas.assert_valid(
                    order_to_jsdict(order, self.contract_address),
                    "/orderSchema",
                )
Ejemplo n.º 21
0
def post_order_config():
    """POST Order config endpoint retrives the values for order fields that the
    relayer requires.
    http://sra-spec.s3-website-us-east-1.amazonaws.com/#operation/getOrderConfig
    """
    current_app.logger.info("############ GETTING ORDER CONFIG")
    network_id = request.args.get("networkId",
                                  current_app.config["PYDEX_NETWORK_ID"])
    assert network_id == current_app.config[
        "PYDEX_NETWORK_ID"], f"networkId={network_id} not supported"
    order = request.json
    assert_valid(order, "/orderConfigRequestSchema")
    res = {
        "senderAddress": NULL_ADDRESS,
        "feeRecipientAddress": current_app.config["PYDEX_ZX_FEE_RECIPIENT"],
        "makerFee": current_app.config["PYDEX_ZX_MAKER_FEE"],
        "takerFee": current_app.config["PYDEX_ZX_TAKER_FEE"],
    }
    # assert_valid(res, "/relayerApiOrderConfigResponseSchema")
    return current_app.response_class(response=json.dumps(res),
                                      status=200,
                                      mimetype='application/json')
Ejemplo n.º 22
0
def get_post_recipients():
    """GET FeeRecepients endpoint retrieves a collection of all fee recipient
    addresses for a relayer.
    http://sra-spec.s3-website-us-east-1.amazonaws.com/v2/fee_recipients
    """
    current_app.logger.info("############ GETTING FEE RECIPIENTS")
    page = int(request.args.get("page", current_app.config["OB_DEFAULT_PAGE"]))
    per_page = int(
        request.args.get("per_page",
                         current_app.config["OB_DEFAULT_PER_PAGE"]))
    normalized_fee_recipient = current_app.config[
        "PYDEX_ZX_FEE_RECIPIENT"].lower()
    fee_recipients = [normalized_fee_recipient]
    res = {
        "total": len(fee_recipients),
        "perPage": per_page,
        "page": page,
        "records": fee_recipients
    }
    assert_valid(res, "/relayerApiFeeRecipientsResponseSchema")
    return current_app.response_class(response=json.dumps(res),
                                      status=200,
                                      mimetype='application/json')
Ejemplo n.º 23
0
def test_exchange_wrapper__batch_fill_orders(
    accounts,
    exchange_wrapper,  # pylint: disable=redefined-outer-name
    ganache_provider,
    weth_asset_data,
):
    """Test filling a batch of orders."""
    taker = accounts[0]
    maker = accounts[1]
    exchange_address = exchange_wrapper.contract_address
    orders = []
    order_1 = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data)
    order_2 = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data)
    orders.append(order_1)
    orders.append(order_2)
    order_hashes = [
        generate_order_hash_hex(order=order, exchange_address=exchange_address)
        for order in orders
    ]
    order_signatures = [
        sign_hash_to_bytes(ganache_provider, maker, order_hash)
        for order_hash in order_hashes
    ]
    taker_amounts = [order["takerAssetAmount"] for order in orders]
    tx_hash = exchange_wrapper.batch_fill_orders.send_transaction(
        orders=orders,
        taker_asset_fill_amounts=taker_amounts,
        signatures=order_signatures,
        tx_params=TxParams(from_=taker),
    )
    assert_valid(tx_hash.hex(), "/hexSchema")

    fill_events = exchange_wrapper.get_fill_event(tx_hash)
    for index, order in enumerate(orders):
        assert_fill_log(fill_events[index].args, maker, taker, order,
                        order_hashes[index])
    def _make_veth_signed_order(  # pylint: disable=too-many-locals
            asset_type,
            qty,
            price,
            side,
            maker_address=pydex_client.account_address,
            expiration_time_seconds=600,
            maker_fee="0",
            taker_fee="0",
            salt="1234567890",
            taker_address=NULL_ADDRESS,
            fee_recipient_address=NULL_ADDRESS,
            sender_address=NULL_ADDRESS,
            exchange_address=exchange_address,  # pylint: disable=redefined-outer-name
            pydex_client=pydex_client,  # pylint: disable=redefined-outer-name
    ):
        """Convenience function for making valid orders to buy or sell
        SHORT or LONG assets against VETH.

        Keyword arguments:
        asset_type - - str from {'LONG', 'SHORT'} to index into `full_asset_set_data`
        qty - - how much of the ticker asset you want to buy against VETH
        price - - Always in units of LONG or SHORT asset per VETH
        side - - str from {'BUY', 'SELL'}
        maker_address - - your address(defaults to MY_ADDRESS)
        """
        asset_data = asset_infos.FULL_SET_ASSET_DATA[asset_type]
        if side == 'BUY':
            maker_asset_data = asset_infos.VETH_ASSET_DATA
            taker_asset_data = asset_data
            maker_amount = to_base_unit_amount(qty * price)
            taker_amount = to_base_unit_amount(qty)
        elif side == 'SELL':
            maker_asset_data = asset_data
            taker_asset_data = asset_infos.VETH_ASSET_DATA
            maker_amount = to_base_unit_amount(qty)
            taker_amount = to_base_unit_amount(qty * price)
        else:
            raise Exception("side must be one of {'BUY', 'SELL'}")

        if salt is None:
            salt = "{:.0f}".format(
                Decimal(random.uniform(0, 9223372036854775807)))
        if not isinstance(maker_fee, str):
            maker_fee = to_base_unit_amount(maker_fee)
        if not isinstance(taker_fee, str):
            taker_fee = to_base_unit_amount(taker_fee)
        expiration_time_seconds = int(time.time() + expiration_time_seconds)

        order = SignedOrder()
        order.maker_address = maker_address
        order.taker_address = taker_address
        order.fee_recipient_address = fee_recipient_address
        order.sender_address = sender_address
        order.maker_asset_amount = maker_amount
        order.taker_asset_amount = taker_amount
        order.maker_fee = "{:.0f}".format(Decimal(maker_fee))
        order.taker_fee = "{:.0f}".format(Decimal(taker_fee))
        order.expiration_time_seconds = expiration_time_seconds
        order.salt = salt
        order.maker_asset_data = maker_asset_data
        order.taker_asset_data = taker_asset_data
        order.exchange_address = exchange_address
        # sign the hash
        order.signature = pydex_client.sign_hash_zx_compat(order.update().hash)
        # make sure the signed_order is valid
        assert_valid(order.to_json(), "/signedOrderSchema")
        return order
Ejemplo n.º 25
0
def generate_order_hash_hex(order: Order, exchange_address: str,
                            chain_id: int) -> str:
    """Calculate the hash of the given order as a hexadecimal string.

    :param order: The order to be hashed.  Must conform to `the 0x order JSON schema <https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schema.json>`_.
    :param exchange_address: The address to which the 0x Exchange smart
        contract has been deployed.
    :returns: A string, of ASCII hex digits, representing the order hash.

    Inputs and expected result below were copied from
    @0x/order-utils/test/order_hash_test.ts

    >>> generate_order_hash_hex(
    ...     Order(
    ...         makerAddress="0x0000000000000000000000000000000000000000",
    ...         takerAddress="0x0000000000000000000000000000000000000000",
    ...         feeRecipientAddress="0x0000000000000000000000000000000000000000",
    ...         senderAddress="0x0000000000000000000000000000000000000000",
    ...         makerAssetAmount="0",
    ...         takerAssetAmount="0",
    ...         makerFee="0",
    ...         takerFee="0",
    ...         expirationTimeSeconds="0",
    ...         salt="0",
    ...         makerAssetData=((0).to_bytes(1, byteorder='big') * 20),
    ...         takerAssetData=((0).to_bytes(1, byteorder='big') * 20),
    ...         makerFeeAssetData=((0).to_bytes(1, byteorder='big') * 20),
    ...         takerFeeAssetData=((0).to_bytes(1, byteorder='big') * 20),
    ...     ),
    ...     exchange_address="0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
    ...     chain_id=1337
    ... )
    'cb36e4fedb36508fb707e2c05e21bffc7a72766ccae93f8ff096693fff7f1714'
    """  # noqa: E501 (line too long)
    assert_is_address(exchange_address, "exchange_address")
    assert_valid(order_to_jsdict(order, chain_id, exchange_address),
                 "/orderSchema")

    def pad_20_bytes_to_32(twenty_bytes: bytes):
        return bytes(12) + twenty_bytes

    def int_to_32_big_endian_bytes(i: int):
        return i.to_bytes(32, byteorder="big")

    eip712_domain_struct_hash = keccak(
        _Constants.eip712_domain_struct_header +
        int_to_32_big_endian_bytes(int(chain_id)) +
        pad_20_bytes_to_32(to_bytes(hexstr=exchange_address)))

    def ensure_bytes(str_or_bytes: Union[str, bytes]) -> bytes:
        return (to_bytes(hexstr=cast(bytes, str_or_bytes)) if isinstance(
            str_or_bytes, str) else str_or_bytes)

    eip712_order_struct_hash = keccak(
        _Constants.eip712_order_schema_hash +
        pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"])) +
        pad_20_bytes_to_32(to_bytes(hexstr=order["takerAddress"])) +
        pad_20_bytes_to_32(to_bytes(hexstr=order["feeRecipientAddress"])) +
        pad_20_bytes_to_32(to_bytes(hexstr=order["senderAddress"])) +
        int_to_32_big_endian_bytes(int(order["makerAssetAmount"])) +
        int_to_32_big_endian_bytes(int(order["takerAssetAmount"])) +
        int_to_32_big_endian_bytes(int(order["makerFee"])) +
        int_to_32_big_endian_bytes(int(order["takerFee"])) +
        int_to_32_big_endian_bytes(int(order["expirationTimeSeconds"])) +
        int_to_32_big_endian_bytes(int(order["salt"])) +
        keccak(ensure_bytes(order["makerAssetData"])) +
        keccak(ensure_bytes(order["takerAssetData"])) +
        keccak(ensure_bytes(order["makerFeeAssetData"])) +
        keccak(ensure_bytes(order["takerFeeAssetData"])))

    return keccak(_Constants.eip191_header + eip712_domain_struct_hash +
                  eip712_order_struct_hash).hex()
Ejemplo n.º 26
0
def order_to_jsdict(
    order: Order,
    chain_id: int,
    exchange_address="0x0000000000000000000000000000000000000000",
    signature: str = None,
) -> dict:
    """Convert a Web3-compatible order struct to a JSON-schema-compatible dict.

    More specifically, do explicit decoding for the `bytes`:code: fields, and
    convert numerics to strings.

    >>> import pprint
    >>> pprint.pprint(order_to_jsdict(
    ...     {
    ...         'makerAddress': "0x0000000000000000000000000000000000000000",
    ...         'takerAddress': "0x0000000000000000000000000000000000000000",
    ...         'feeRecipientAddress':
    ...             "0x0000000000000000000000000000000000000000",
    ...         'senderAddress': "0x0000000000000000000000000000000000000000",
    ...         'makerAssetAmount': 1,
    ...         'takerAssetAmount': 1,
    ...         'makerFee': 0,
    ...         'takerFee': 0,
    ...         'expirationTimeSeconds': 1,
    ...         'salt': 1,
    ...         'makerAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...         'takerAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...         'makerFeeAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...         'takerFeeAssetData': (0).to_bytes(1, byteorder='big') * 20,
    ...     },
    ...     chain_id=50
    ... ))
    {'chainId': 50,
     'exchangeAddress': '0x0000000000000000000000000000000000000000',
     'expirationTimeSeconds': '1',
     'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
     'makerAddress': '0x0000000000000000000000000000000000000000',
     'makerAssetAmount': '1',
     'makerAssetData': '0x0000000000000000000000000000000000000000',
     'makerFee': '0',
     'makerFeeAssetData': '0x0000000000000000000000000000000000000000',
     'salt': '1',
     'senderAddress': '0x0000000000000000000000000000000000000000',
     'takerAddress': '0x0000000000000000000000000000000000000000',
     'takerAssetAmount': '1',
     'takerAssetData': '0x0000000000000000000000000000000000000000',
     'takerFee': '0',
     'takerFeeAssetData': '0x0000000000000000000000000000000000000000'}
    """
    jsdict = cast(Dict, copy(order))

    def encode_bytes(bytes_or_str: Union[bytes, str]) -> bytes:
        def ensure_hex_prefix(hex_str: str):
            if hex_str[0:2] != "0x":
                hex_str = "0x" + hex_str
            return hex_str

        return ensure_hex_prefix(
            cast(bytes, bytes_or_str).hex() if isinstance(bytes_or_str, bytes
                                                          ) else bytes_or_str)

    jsdict["makerAssetData"] = encode_bytes(order["makerAssetData"])
    jsdict["takerAssetData"] = encode_bytes(order["takerAssetData"])
    jsdict["makerFeeAssetData"] = encode_bytes(order["makerFeeAssetData"])
    jsdict["takerFeeAssetData"] = encode_bytes(order["takerFeeAssetData"])

    jsdict["exchangeAddress"] = exchange_address

    jsdict["expirationTimeSeconds"] = str(order["expirationTimeSeconds"])

    jsdict["makerAssetAmount"] = str(order["makerAssetAmount"])
    jsdict["takerAssetAmount"] = str(order["takerAssetAmount"])

    jsdict["makerFee"] = str(order["makerFee"])
    jsdict["takerFee"] = str(order["takerFee"])

    jsdict["salt"] = str(order["salt"])

    jsdict["chainId"] = chain_id

    if signature is not None:
        jsdict["signature"] = signature

    assert_valid(jsdict, "/orderSchema")

    return jsdict