New demos for Python packages (#1734)
End-to-end demos of constructing and signing an order and submitting it to a Relayer. Docs are generated from the code, and include usage examples that are verified through automated testing.
This commit is contained in:
		
				
					committed by
					
						
						F. Eugene Aumson
					
				
			
			
				
	
			
			
			
						parent
						
							28c4ca73ab
						
					
				
				
					commit
					3099ba71eb
				
			@@ -17,7 +17,6 @@ PACKAGE_DEPENDENCY_LIST = [
 | 
			
		||||
    "sra_client",
 | 
			
		||||
    "order_utils",
 | 
			
		||||
    "middlewares",
 | 
			
		||||
    "contract_demo"
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
for package in PACKAGE_DEPENDENCY_LIST:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "domain": "0x-contract-demo-py",
 | 
			
		||||
    "build_command": "python setup.py build_sphinx",
 | 
			
		||||
    "upload_directory": "build/docs/html",
 | 
			
		||||
    "index_key": "index.html",
 | 
			
		||||
    "error_key": "index.html",
 | 
			
		||||
    "trailing_slashes": true,
 | 
			
		||||
    "cache": 3600,
 | 
			
		||||
    "aws_profile": "default",
 | 
			
		||||
    "aws_region": "us-east-1",
 | 
			
		||||
    "cdn": false,
 | 
			
		||||
    "dns_configured": true
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
## 0x-contract-demo
 | 
			
		||||
 | 
			
		||||
A demonstration of calling 0x smart contracts from Python.
 | 
			
		||||
 | 
			
		||||
Read the [documentation](http://0x-contract-demo-py.s3-website-us-east-1.amazonaws.com/)
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
 | 
			
		||||
 | 
			
		||||
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
 | 
			
		||||
 | 
			
		||||
### Install Code and Dependencies
 | 
			
		||||
 | 
			
		||||
Ensure that you have installed Python >=3.6 and Docker. Then:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
pip install -e .[dev]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Test
 | 
			
		||||
 | 
			
		||||
Tests depend on a running ganache instance with the 0x contracts deployed in it. For convenience, a docker container is provided that has ganache-cli and a snapshot containing the necessary contracts. A shortcut is provided to run that docker container: `./setup.py ganache`. With that running, the tests can be run with `./setup.py test`.
 | 
			
		||||
 | 
			
		||||
### Clean
 | 
			
		||||
 | 
			
		||||
`./setup.py clean --all`
 | 
			
		||||
 | 
			
		||||
### Lint
 | 
			
		||||
 | 
			
		||||
`./setup.py lint`
 | 
			
		||||
 | 
			
		||||
### Build Documentation
 | 
			
		||||
 | 
			
		||||
`./setup.py build_sphinx`
 | 
			
		||||
 | 
			
		||||
### More
 | 
			
		||||
 | 
			
		||||
See `./setup.py --help-commands` for more info.
 | 
			
		||||
@@ -1,146 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
"""setuptools module for 0x-contract-demo package."""
 | 
			
		||||
 | 
			
		||||
import distutils.command.build_py
 | 
			
		||||
from distutils.command.clean import clean
 | 
			
		||||
import subprocess  # nosec
 | 
			
		||||
from shutil import rmtree
 | 
			
		||||
from os import environ, path
 | 
			
		||||
from sys import argv
 | 
			
		||||
 | 
			
		||||
from setuptools import setup
 | 
			
		||||
from setuptools.command.test import test as TestCommand
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCommandExtension(TestCommand):
 | 
			
		||||
    """Run pytest tests."""
 | 
			
		||||
 | 
			
		||||
    def run_tests(self):
 | 
			
		||||
        """Invoke pytest."""
 | 
			
		||||
        import pytest
 | 
			
		||||
 | 
			
		||||
        exit(pytest.main())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LintCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom setuptools command class for running linters."""
 | 
			
		||||
 | 
			
		||||
    description = "Run linters"
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run linter shell commands."""
 | 
			
		||||
        lint_commands = [
 | 
			
		||||
            # formatter:
 | 
			
		||||
            "black --line-length 79 --check --diff test setup.py".split(),
 | 
			
		||||
            # style guide checker (formerly pep8):
 | 
			
		||||
            "pycodestyle test setup.py".split(),
 | 
			
		||||
            # docstring style checker:
 | 
			
		||||
            "pydocstyle test setup.py".split(),
 | 
			
		||||
            # static type checker:
 | 
			
		||||
            "mypy test setup.py".split(),
 | 
			
		||||
            # security issue checker:
 | 
			
		||||
            "bandit -r ./setup.py".split(),
 | 
			
		||||
            # general linter:
 | 
			
		||||
            "pylint test setup.py".split(),
 | 
			
		||||
            # pylint takes relatively long to run, so it runs last, to enable
 | 
			
		||||
            # fast failures.
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        # tell mypy where to find interface stubs for 3rd party libs
 | 
			
		||||
        environ["MYPYPATH"] = path.join(
 | 
			
		||||
            path.dirname(path.realpath(argv[0])), "stubs"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        for lint_command in lint_commands:
 | 
			
		||||
            print(
 | 
			
		||||
                "Running lint command `", " ".join(lint_command).strip(), "`"
 | 
			
		||||
            )
 | 
			
		||||
            subprocess.check_call(lint_command)  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CleanCommandExtension(clean):
 | 
			
		||||
    """Custom command to do custom cleanup."""
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run the regular clean, followed by our custom commands."""
 | 
			
		||||
        super().run()
 | 
			
		||||
        rmtree(".mypy_cache", ignore_errors=True)
 | 
			
		||||
        rmtree(".tox", ignore_errors=True)
 | 
			
		||||
        rmtree(".pytest_cache", ignore_errors=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GanacheCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to publish to pypi.org."""
 | 
			
		||||
 | 
			
		||||
    description = "Run ganache daemon to support tests."
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run ganache."""
 | 
			
		||||
        cmd_line = "docker run -d -p 8545:8545 0xorg/ganache-cli:2.2.2".split()
 | 
			
		||||
        subprocess.call(cmd_line)  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PublishDocsCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to publish docs to S3."""
 | 
			
		||||
 | 
			
		||||
    description = (
 | 
			
		||||
        "Publish docs to "
 | 
			
		||||
        + "http://0x-contract-addresses-py.s3-website-us-east-1.amazonaws.com/"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run npm package `discharge` to build & upload docs."""
 | 
			
		||||
        subprocess.check_call("discharge deploy".split())  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
setup(
 | 
			
		||||
    name="0x-contract-demo",
 | 
			
		||||
    version="1.0.0",
 | 
			
		||||
    description="Demonstration of calling 0x contracts",
 | 
			
		||||
    url=(
 | 
			
		||||
        "https://github.com/0xProject/0x-monorepo/tree/development"
 | 
			
		||||
        + "/python-packages/contract_demo"
 | 
			
		||||
    ),
 | 
			
		||||
    author="F. Eugene Aumson",
 | 
			
		||||
    author_email="feuGeneA@users.noreply.github.com",
 | 
			
		||||
    cmdclass={
 | 
			
		||||
        "clean": CleanCommandExtension,
 | 
			
		||||
        "lint": LintCommand,
 | 
			
		||||
        "test": TestCommandExtension,
 | 
			
		||||
        "ganache": GanacheCommand,
 | 
			
		||||
        "publish_docs": PublishDocsCommand,
 | 
			
		||||
    },
 | 
			
		||||
    install_requires=[
 | 
			
		||||
        "0x-contract-addresses",
 | 
			
		||||
        "0x-contract-artifacts",
 | 
			
		||||
        "0x-order-utils",
 | 
			
		||||
        "0x-web3",  # TEMPORARY! pending resolution of our web3.py PR#1147
 | 
			
		||||
        "mypy_extensions",
 | 
			
		||||
    ],
 | 
			
		||||
    extras_require={
 | 
			
		||||
        "dev": [
 | 
			
		||||
            "bandit",
 | 
			
		||||
            "black",
 | 
			
		||||
            "coverage",
 | 
			
		||||
            "coveralls",
 | 
			
		||||
            "mypy",
 | 
			
		||||
            "mypy_extensions",
 | 
			
		||||
            "pycodestyle",
 | 
			
		||||
            "pydocstyle",
 | 
			
		||||
            "pylint",
 | 
			
		||||
            "pytest",
 | 
			
		||||
            "sphinx",
 | 
			
		||||
            "tox",
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    python_requires=">=3.6, <4",
 | 
			
		||||
    license="Apache 2.0",
 | 
			
		||||
    zip_safe=False,  # required per mypy
 | 
			
		||||
    command_options={
 | 
			
		||||
        "build_sphinx": {
 | 
			
		||||
            "source_dir": ("setup.py", "test"),
 | 
			
		||||
            "build_dir": ("setup.py", "build/docs"),
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
)
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
from distutils.core import Command
 | 
			
		||||
 | 
			
		||||
class clean(Command):
 | 
			
		||||
    def initialize_options(self: clean) -> None: ...
 | 
			
		||||
    def finalize_options(self: clean) -> None: ...
 | 
			
		||||
    def run(self: clean) -> None: ...
 | 
			
		||||
    ...
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
from distutils.core import Command
 | 
			
		||||
 | 
			
		||||
class clean(Command):
 | 
			
		||||
    def initialize_options(self: clean) -> None: ...
 | 
			
		||||
    def finalize_options(self: clean) -> None: ...
 | 
			
		||||
    def run(self: clean) -> None: ...
 | 
			
		||||
    ...
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
from typing import Union
 | 
			
		||||
 | 
			
		||||
def to_checksum_address(value: Union[str, bytes]) -> str:
 | 
			
		||||
    ...
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
from distutils.dist import Distribution
 | 
			
		||||
from typing import Any, List
 | 
			
		||||
 | 
			
		||||
def setup(**attrs: Any) -> Distribution: ...
 | 
			
		||||
 | 
			
		||||
class Command: ...
 | 
			
		||||
 | 
			
		||||
def find_packages(where: str) -> List[str]: ...
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
from setuptools import Command
 | 
			
		||||
 | 
			
		||||
class test(Command): ...
 | 
			
		||||
@@ -1,2 +0,0 @@
 | 
			
		||||
class Web3:
 | 
			
		||||
    ...
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
class Contract:
 | 
			
		||||
    def call(self): ...
 | 
			
		||||
    ...
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
"""Demonstrations of calling 0x smart contracts."""
 | 
			
		||||
@@ -1,117 +0,0 @@
 | 
			
		||||
"""Test calling methods on the Exchange contract."""
 | 
			
		||||
 | 
			
		||||
from eth_utils import to_checksum_address
 | 
			
		||||
from web3 import Web3
 | 
			
		||||
from web3.utils import datatypes
 | 
			
		||||
 | 
			
		||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
 | 
			
		||||
import zero_ex.contract_artifacts
 | 
			
		||||
from zero_ex.json_schemas import assert_valid
 | 
			
		||||
from zero_ex.order_utils import (
 | 
			
		||||
    Order,
 | 
			
		||||
    OrderInfo,
 | 
			
		||||
    order_to_jsdict,
 | 
			
		||||
    generate_order_hash_hex,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_order_info():
 | 
			
		||||
    """Demonstrate Exchange.getOrderInfo()."""
 | 
			
		||||
    order: Order = {
 | 
			
		||||
        "makerAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "takerAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "feeRecipientAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "senderAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "makerAssetAmount": 1000000000000000000,
 | 
			
		||||
        "takerAssetAmount": 1000000000000000000,
 | 
			
		||||
        "makerFee": 0,
 | 
			
		||||
        "takerFee": 0,
 | 
			
		||||
        "expirationTimeSeconds": 12345,
 | 
			
		||||
        "salt": 12345,
 | 
			
		||||
        "makerAssetData": (b"\x00") * 20,
 | 
			
		||||
        "takerAssetData": (b"\x00") * 20,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    web3_instance = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
 | 
			
		||||
 | 
			
		||||
    # false positive from pylint: disable=no-member
 | 
			
		||||
    contract_address = NETWORK_TO_ADDRESSES[
 | 
			
		||||
        NetworkId(int(web3_instance.net.version))
 | 
			
		||||
    ].exchange
 | 
			
		||||
 | 
			
		||||
    assert_valid(
 | 
			
		||||
        order_to_jsdict(order, exchange_address=contract_address),
 | 
			
		||||
        "/orderSchema",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # false positive from pylint: disable=no-member
 | 
			
		||||
    exchange: datatypes.Contract = web3_instance.eth.contract(
 | 
			
		||||
        address=to_checksum_address(contract_address),
 | 
			
		||||
        abi=zero_ex.contract_artifacts.abi_by_name("Exchange"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    order_info = OrderInfo(*exchange.call().getOrderInfo(order))
 | 
			
		||||
 | 
			
		||||
    assert isinstance(order_info.order_status, int)
 | 
			
		||||
    assert order_info.order_status == 4
 | 
			
		||||
 | 
			
		||||
    assert isinstance(order_info.order_hash, bytes)
 | 
			
		||||
    assert order_info.order_hash.hex() == generate_order_hash_hex(
 | 
			
		||||
        order,
 | 
			
		||||
        exchange_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    assert isinstance(order_info.order_taker_asset_filled_amount, int)
 | 
			
		||||
    assert order_info.order_taker_asset_filled_amount == 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_orders_info():
 | 
			
		||||
    """Demonstrate Exchange.getOrderInfo()."""
 | 
			
		||||
    order: Order = {
 | 
			
		||||
        "makerAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "takerAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "feeRecipientAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "senderAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
        "makerAssetAmount": 1000000000000000000,
 | 
			
		||||
        "takerAssetAmount": 1000000000000000000,
 | 
			
		||||
        "makerFee": 0,
 | 
			
		||||
        "takerFee": 0,
 | 
			
		||||
        "expirationTimeSeconds": 12345,
 | 
			
		||||
        "salt": 12345,
 | 
			
		||||
        "makerAssetData": (b"\x00") * 20,
 | 
			
		||||
        "takerAssetData": (b"\x00") * 20,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    web3_instance = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
 | 
			
		||||
 | 
			
		||||
    # false positive from pylint: disable=no-member
 | 
			
		||||
    contract_address = NETWORK_TO_ADDRESSES[
 | 
			
		||||
        NetworkId(int(web3_instance.net.version))
 | 
			
		||||
    ].exchange
 | 
			
		||||
 | 
			
		||||
    assert_valid(
 | 
			
		||||
        order_to_jsdict(order, exchange_address=contract_address),
 | 
			
		||||
        "/orderSchema",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # false positive from pylint: disable=no-member
 | 
			
		||||
    exchange: datatypes.Contract = web3_instance.eth.contract(
 | 
			
		||||
        address=to_checksum_address(contract_address),
 | 
			
		||||
        abi=zero_ex.contract_artifacts.abi_by_name("Exchange"),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    orders_info = exchange.call().getOrdersInfo([order])
 | 
			
		||||
 | 
			
		||||
    for order_info in orders_info:
 | 
			
		||||
        order_info = OrderInfo(*order_info)
 | 
			
		||||
        assert isinstance(order_info.order_status, int)
 | 
			
		||||
        assert order_info.order_status == 4
 | 
			
		||||
 | 
			
		||||
        assert isinstance(order_info.order_hash, bytes)
 | 
			
		||||
        assert order_info.order_hash.hex() == generate_order_hash_hex(
 | 
			
		||||
            order,
 | 
			
		||||
            exchange_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        assert isinstance(order_info.order_taker_asset_filled_amount, int)
 | 
			
		||||
        assert order_info.order_taker_asset_filled_amount == 0
 | 
			
		||||
@@ -1,4 +1,31 @@
 | 
			
		||||
"""JSON schemas and associated utilities."""
 | 
			
		||||
"""JSON schemas and associated utilities.
 | 
			
		||||
 | 
			
		||||
Validating a 0x Order
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
Here is an example on how to validate a 0x order.
 | 
			
		||||
 | 
			
		||||
>>> from zero_ex.json_schemas import assert_valid
 | 
			
		||||
>>> example_order = {
 | 
			
		||||
...     'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
 | 
			
		||||
...     'takerAddress': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
...     'senderAddress': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
...     'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
 | 
			
		||||
...     'feeRecipientAddress':
 | 
			
		||||
...         '0x0000000000000000000000000000000000000000',
 | 
			
		||||
...     'makerAssetData': '0xf47261b0000000000000000000000000'
 | 
			
		||||
...         'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
 | 
			
		||||
...     'takerAssetData': '0xf47261b0000000000000000000000000'
 | 
			
		||||
...         'e41d2489571d322189246dafa5ebde1f4699f498',
 | 
			
		||||
...     'salt': 123456789,
 | 
			
		||||
...     'makerFee': 0,
 | 
			
		||||
...     'takerFee': 0,
 | 
			
		||||
...     'makerAssetAmount': 1000000000000000000,
 | 
			
		||||
...     'takerAssetAmount': 500000000000000000000,
 | 
			
		||||
...     'expirationTimeSeconds': 1553553429
 | 
			
		||||
... }
 | 
			
		||||
>>> assert_valid(example_order, "/orderSchema")
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from os import path
 | 
			
		||||
import json
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,55 @@ For local testing one may construct such a provider pointing at an instance of
 | 
			
		||||
contracts deployed on it.  For convenience, a docker container is provided for
 | 
			
		||||
just this purpose.  To start it:
 | 
			
		||||
`docker run -d -p 8545:8545 0xorg/ganache-cli:2.2.2`:code:.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
Creating a 0x Order
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Here is a short demonstration on how to create a 0x order.
 | 
			
		||||
 | 
			
		||||
>>> import pprint
 | 
			
		||||
>>> from zero_ex.contract_addresses import (
 | 
			
		||||
...     NETWORK_TO_ADDRESSES, NetworkId)
 | 
			
		||||
>>> from zero_ex.order_utils import asset_data_utils, Order
 | 
			
		||||
>>> NULL_ADDRESS = "0x0000000000000000000000000000000000000000"
 | 
			
		||||
>>> my_address = "0x5409ed021d9299bf6814279a6a1411a7e866a631"
 | 
			
		||||
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange
 | 
			
		||||
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token
 | 
			
		||||
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token
 | 
			
		||||
>>> maker_asset_data = (
 | 
			
		||||
...     asset_data_utils.encode_erc20_asset_data(weth_address))
 | 
			
		||||
>>> taker_asset_data = (
 | 
			
		||||
...     asset_data_utils.encode_erc20_asset_data(zrx_address))
 | 
			
		||||
>>> example_order: Order = {
 | 
			
		||||
...     "makerAddress": my_address,
 | 
			
		||||
...     "takerAddress": NULL_ADDRESS,
 | 
			
		||||
...     "exchangeAddress": exchange_address,
 | 
			
		||||
...     "senderAddress": NULL_ADDRESS,
 | 
			
		||||
...     "feeRecipientAddress": NULL_ADDRESS,
 | 
			
		||||
...     "makerAssetData": maker_asset_data,
 | 
			
		||||
...     "takerAssetData": taker_asset_data,
 | 
			
		||||
...     "salt": 123456789,
 | 
			
		||||
...     "makerFee": 0,
 | 
			
		||||
...     "takerFee": 0,
 | 
			
		||||
...     "makerAssetAmount": 1 * 10 ** 18,  # Converting token amount to base unit with 18 decimals
 | 
			
		||||
...     "takerAssetAmount": 500 * 10 ** 18,  # Converting token amount to base unit with 18 decimals
 | 
			
		||||
...     "expirationTimeSeconds": 1553553429,
 | 
			
		||||
... }
 | 
			
		||||
>>> pprint.pprint(example_order)
 | 
			
		||||
{'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
 | 
			
		||||
 'expirationTimeSeconds': 1553553429,
 | 
			
		||||
 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
 | 
			
		||||
 'makerAssetAmount': 1000000000000000000,
 | 
			
		||||
 'makerAssetData': '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
 | 
			
		||||
 'makerFee': 0,
 | 
			
		||||
 'salt': 123456789,
 | 
			
		||||
 'senderAddress': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
 'takerAddress': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
 'takerAssetAmount': 500000000000000000000,
 | 
			
		||||
 'takerAssetData': '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498',
 | 
			
		||||
 'takerFee': 0}
 | 
			
		||||
"""  # noqa E501
 | 
			
		||||
 | 
			
		||||
from copy import copy
 | 
			
		||||
from enum import auto, Enum
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								python-packages/sra_client/.pylintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								python-packages/sra_client/.pylintrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
[MESSAGES CONTROL]
 | 
			
		||||
disable=C0330,line-too-long,fixme,too-few-public-methods,too-many-ancestors
 | 
			
		||||
# C0330 is "bad hanging indent". we use indents per `black`.
 | 
			
		||||
 | 
			
		||||
@@ -1,11 +1,22 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "timestamp": 1553491629,
 | 
			
		||||
        "version": "1.0.1",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Fix regex validation on numeric values"
 | 
			
		||||
                "note": "Fix regex validation on numeric values",
 | 
			
		||||
                "pr": 1731
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        ],
 | 
			
		||||
        "timestamp": 1553491629
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "1.0.1",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Added new devdependencies, and linting commands to `setup.py`. Added sphinx docs to demonstrate how to use sra_client.",
 | 
			
		||||
                "pr": 1734
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "timestamp": 1553183790
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -9,11 +9,11 @@ import pkg_resources
 | 
			
		||||
# pylint: disable=invalid-name
 | 
			
		||||
# because these variables are not named in upper case, as globals should be.
 | 
			
		||||
 | 
			
		||||
project = "0x-contract-demo"
 | 
			
		||||
project = "0x-sra-client"
 | 
			
		||||
# pylint: disable=redefined-builtin
 | 
			
		||||
copyright = "2018, ZeroEx, Intl."
 | 
			
		||||
author = "F. Eugene Aumson"
 | 
			
		||||
version = pkg_resources.get_distribution("0x-contract-demo").version
 | 
			
		||||
version = pkg_resources.get_distribution("0x-sra-client").version
 | 
			
		||||
release = ""  # The full version, including alpha/beta/rc tags
 | 
			
		||||
 | 
			
		||||
extensions = [
 | 
			
		||||
@@ -22,6 +22,7 @@ extensions = [
 | 
			
		||||
    "sphinx.ext.intersphinx",
 | 
			
		||||
    "sphinx.ext.coverage",
 | 
			
		||||
    "sphinx.ext.viewcode",
 | 
			
		||||
    "sphinx_autodoc_typehints",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
templates_path = ["doc_templates"]
 | 
			
		||||
@@ -46,7 +47,7 @@ html_static_path = ["doc_static"]
 | 
			
		||||
# so a file named "default.css" will overwrite the builtin "default.css".
 | 
			
		||||
 | 
			
		||||
# Output file base name for HTML help builder.
 | 
			
		||||
htmlhelp_basename = "contract_demopydoc"
 | 
			
		||||
htmlhelp_basename = "sraclientpydoc"
 | 
			
		||||
 | 
			
		||||
# -- Extension configuration:
 | 
			
		||||
 | 
			
		||||
@@ -1,13 +1,20 @@
 | 
			
		||||
.. source for the sphinx-generated build/docs/web/index.html
 | 
			
		||||
 | 
			
		||||
Python demo of 0x Smart Contracts
 | 
			
		||||
=================================
 | 
			
		||||
Python zero_ex.sra_client.api_client
 | 
			
		||||
====================================
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
   :caption: Contents:
 | 
			
		||||
 | 
			
		||||
.. automodule:: test.test_exchange
 | 
			
		||||
.. automodule:: sra_client
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
API
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
.. automodule:: sra_client.api.default_api
 | 
			
		||||
   :members:
 | 
			
		||||
 | 
			
		||||
Indices and tables
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# coding: utf-8
 | 
			
		||||
 | 
			
		||||
"""setuptools module for sra_client package."""
 | 
			
		||||
 | 
			
		||||
import subprocess
 | 
			
		||||
import subprocess  # nosec
 | 
			
		||||
import distutils.command.build_py
 | 
			
		||||
 | 
			
		||||
from setuptools import setup, find_packages  # noqa: H301
 | 
			
		||||
from setuptools.command.test import test as TestCommand
 | 
			
		||||
 | 
			
		||||
NAME = "0x-sra-client"
 | 
			
		||||
VERSION = "1.0.0"
 | 
			
		||||
VERSION = "1.0.1"
 | 
			
		||||
# To install the library, run the following
 | 
			
		||||
#
 | 
			
		||||
# python setup.py install
 | 
			
		||||
@@ -21,6 +23,17 @@ with open("README.md", "r") as file_handle:
 | 
			
		||||
 | 
			
		||||
REQUIRES = ["urllib3 >= 1.15", "six >= 1.10", "certifi", "python-dateutil"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestCommandExtension(TestCommand):
 | 
			
		||||
    """Run pytest tests."""
 | 
			
		||||
 | 
			
		||||
    def run_tests(self):
 | 
			
		||||
        """Invoke pytest."""
 | 
			
		||||
        import pytest
 | 
			
		||||
 | 
			
		||||
        exit(pytest.main(["--doctest-modules"]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestPublishCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to publish to test.pypi.org."""
 | 
			
		||||
 | 
			
		||||
@@ -38,6 +51,59 @@ class TestPublishCommand(distutils.command.build_py.build_py):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GanacheCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to publish to pypi.org."""
 | 
			
		||||
 | 
			
		||||
    description = "Run ganache daemon to support tests."
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run ganache."""
 | 
			
		||||
        cmd_line = (
 | 
			
		||||
            "docker run -d -p 8545:8545 0xorg/ganache-cli:2.2.2"
 | 
			
		||||
        ).split()
 | 
			
		||||
        subprocess.call(cmd_line)  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LaunchKitCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to boot up a local 0x-launch-kit in docker."""
 | 
			
		||||
 | 
			
		||||
    description = "Run launch-kit daemon to support sra_client demos."
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run 0x-launch-kit."""
 | 
			
		||||
        cmd_line = ("docker run -d -p 3000:3000 0xorg/launch-kit-ci").split()
 | 
			
		||||
        subprocess.call(cmd_line)  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LintCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom setuptools command class for running linters."""
 | 
			
		||||
 | 
			
		||||
    description = "Run linters"
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """Run linter shell commands."""
 | 
			
		||||
        lint_commands = [
 | 
			
		||||
            # formatter:
 | 
			
		||||
            "black --line-length 79 --check --diff test sra_client/__init__.py setup.py".split(),  # noqa: E501 (line too long)
 | 
			
		||||
            # style guide checker (formerly pep8):
 | 
			
		||||
            "pycodestyle test sra_client/__init__.py setup.py".split(),
 | 
			
		||||
            # docstring style checker:
 | 
			
		||||
            "pydocstyle src test sra_client/__init__.py setup.py".split(),
 | 
			
		||||
            # static type checker:
 | 
			
		||||
            "bandit -r test sra_client/__init__.py setup.py".split(),
 | 
			
		||||
            # general linter:
 | 
			
		||||
            "pylint test sra_client/__init__.py setup.py".split(),
 | 
			
		||||
            # pylint takes relatively long to run, so it runs last, to enable
 | 
			
		||||
            # fast failures.
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        for lint_command in lint_commands:
 | 
			
		||||
            print(
 | 
			
		||||
                "Running lint command `", " ".join(lint_command).strip(), "`"
 | 
			
		||||
            )
 | 
			
		||||
            subprocess.check_call(lint_command)  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PublishCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to publish to pypi.org."""
 | 
			
		||||
 | 
			
		||||
@@ -48,13 +114,17 @@ class PublishCommand(distutils.command.build_py.build_py):
 | 
			
		||||
        subprocess.check_call("twine upload dist/*".split())  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LintCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """No-op lint command to support top-level lint script."""
 | 
			
		||||
class PublishDocsCommand(distutils.command.build_py.build_py):
 | 
			
		||||
    """Custom command to publish docs to S3."""
 | 
			
		||||
 | 
			
		||||
    description = "No-op"
 | 
			
		||||
    description = (
 | 
			
		||||
        "Publish docs to "
 | 
			
		||||
        + "http://0x-sra-demos-py.s3-website-us-east-1.amazonaws.com/"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        pass
 | 
			
		||||
        """Run npm package `discharge` to build & upload docs."""
 | 
			
		||||
        subprocess.check_call("discharge deploy".split())  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
setup(
 | 
			
		||||
@@ -72,6 +142,33 @@ setup(
 | 
			
		||||
    cmdclass={
 | 
			
		||||
        "test_publish": TestPublishCommand,
 | 
			
		||||
        "publish": PublishCommand,
 | 
			
		||||
        "launch_kit": LaunchKitCommand,
 | 
			
		||||
        "lint": LintCommand,
 | 
			
		||||
        "publish_docs": PublishDocsCommand,
 | 
			
		||||
        "test": TestCommandExtension,
 | 
			
		||||
        "ganache": GanacheCommand,
 | 
			
		||||
    },
 | 
			
		||||
    extras_require={
 | 
			
		||||
        "dev": [
 | 
			
		||||
            "0x-contract-addresses",
 | 
			
		||||
            "0x-order-utils",
 | 
			
		||||
            "0x-web3",
 | 
			
		||||
            "bandit",
 | 
			
		||||
            "black",
 | 
			
		||||
            "coverage",
 | 
			
		||||
            "coveralls",
 | 
			
		||||
            "pycodestyle",
 | 
			
		||||
            "pydocstyle",
 | 
			
		||||
            "pylint",
 | 
			
		||||
            "pytest",
 | 
			
		||||
            "sphinx",
 | 
			
		||||
            "sphinx-autodoc-typehints",
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    command_options={
 | 
			
		||||
        "build_sphinx": {
 | 
			
		||||
            "source_dir": ("setup.py", "."),
 | 
			
		||||
            "build_dir": ("setup.py", "build/docs"),
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,167 @@
 | 
			
		||||
 | 
			
		||||
# flake8: noqa
 | 
			
		||||
 | 
			
		||||
"""Python api client to interact with SRA compatible 0x relayers.
 | 
			
		||||
 | 
			
		||||
0x Protocol is an open standard.  Many relayers opt-in to implementing a set of
 | 
			
		||||
`standard relayer API endpoints <http://sra-spec.s3-website-us-east-1.amazonaws.com/>`_
 | 
			
		||||
to make it easier for anyone to source liquidity that conforms to the 0x order format.
 | 
			
		||||
Here, we will show you how you can use our `sra_client
 | 
			
		||||
<https://github.com/0xProject/0x-monorepo/tree/development/python-packages/sra_client#0x-sra-client>`_
 | 
			
		||||
module to interact with 0x relayers that implements the Standard Relayer API.
 | 
			
		||||
 | 
			
		||||
Setup
 | 
			
		||||
=====
 | 
			
		||||
Install the sra-client package with pip:
 | 
			
		||||
 | 
			
		||||
`pip install 0x-sra-client`:code:
 | 
			
		||||
 | 
			
		||||
To interact with a 0x Relayer, you need the HTTP endpoint of the Relayer you'd like to
 | 
			
		||||
connect to (i.e. https://api.radarrelay.com/0x/v2).
 | 
			
		||||
 | 
			
		||||
For local testing one can use the `0x-launch-kit
 | 
			
		||||
<https://github.com/0xProject/0x-launch-kit#table-of-contents/>`_
 | 
			
		||||
to host orders locally. For convenience, a docker container is provided
 | 
			
		||||
for just this purpose. To start it:
 | 
			
		||||
 | 
			
		||||
`docker run -d -p 3000:3000 0xorg/launch-kit-ci`:code:
 | 
			
		||||
 | 
			
		||||
and then connect to the http server running at http://localhost:3000.
 | 
			
		||||
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
Configure and create an API client instance
 | 
			
		||||
--------------------------------------------
 | 
			
		||||
 | 
			
		||||
>>> from sra_client import ApiClient, Configuration
 | 
			
		||||
>>> from sra_client.api import DefaultApi
 | 
			
		||||
>>> config = Configuration()
 | 
			
		||||
>>> config.host = "http://localhost:3000"
 | 
			
		||||
>>> relayer_api = DefaultApi(ApiClient(config))
 | 
			
		||||
 | 
			
		||||
Post Order
 | 
			
		||||
-----------
 | 
			
		||||
Post an order to an SRA-compliant Relayer.
 | 
			
		||||
 | 
			
		||||
>>> from web3 import HTTPProvider, Web3
 | 
			
		||||
>>> from zero_ex.contract_addresses import (
 | 
			
		||||
...     NETWORK_TO_ADDRESSES, NetworkId)
 | 
			
		||||
>>> from zero_ex.order_utils import (
 | 
			
		||||
...     asset_data_utils,
 | 
			
		||||
...     generate_order_hash_hex,
 | 
			
		||||
...     jsdict_order_to_struct,
 | 
			
		||||
...     sign_hash)
 | 
			
		||||
>>> provider = HTTPProvider("http://localhost:8545")
 | 
			
		||||
>>> maker_address = "0x5409ed021d9299bf6814279a6a1411a7e866a631"
 | 
			
		||||
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.KOVAN].exchange
 | 
			
		||||
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.KOVAN].ether_token
 | 
			
		||||
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.KOVAN].zrx_token
 | 
			
		||||
>>> weth_asset_data = asset_data_utils.encode_erc20_asset_data(weth_address)
 | 
			
		||||
>>> zrx_asset_data = asset_data_utils.encode_erc20_asset_data(zrx_address)
 | 
			
		||||
>>> example_order = {
 | 
			
		||||
...     "makerAddress": maker_address,
 | 
			
		||||
...     "takerAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
...     "senderAddress": "0x0000000000000000000000000000000000000000",
 | 
			
		||||
...     "exchangeAddress": exchange_address,
 | 
			
		||||
...     "feeRecipientAddress":
 | 
			
		||||
...         "0x0000000000000000000000000000000000000000",
 | 
			
		||||
...     "makerAssetData": weth_asset_data,
 | 
			
		||||
...     "takerAssetData": zrx_asset_data,
 | 
			
		||||
...     "salt": "2362734632784682376287462",
 | 
			
		||||
...     "makerFee": "0",
 | 
			
		||||
...     "takerFee": "0",
 | 
			
		||||
...     "makerAssetAmount": "1000000000000000000",
 | 
			
		||||
...     "takerAssetAmount": "500000000000000000000",
 | 
			
		||||
...     "expirationTimeSeconds": "999999999999999999999"}
 | 
			
		||||
>>> order_hash = generate_order_hash_hex(
 | 
			
		||||
...     jsdict_order_to_struct(example_order), exchange_address)
 | 
			
		||||
>>> example_order["signature"] = sign_hash(
 | 
			
		||||
...     provider, Web3.toChecksumAddress(maker_address), order_hash)
 | 
			
		||||
>>> relayer_api.post_order_with_http_info(
 | 
			
		||||
...     network_id=42, signed_order_schema=example_order)[1]
 | 
			
		||||
200
 | 
			
		||||
 | 
			
		||||
Get Orders
 | 
			
		||||
-----------
 | 
			
		||||
Get orders from an SRA-compliant Relayer.
 | 
			
		||||
 | 
			
		||||
>>> relayer_api.get_orders()
 | 
			
		||||
{'records': [{'meta_data': {},
 | 
			
		||||
              'order': {'exchange_address': '0x35dd2932454449b14cee11a94d3674a936d5d7b2',
 | 
			
		||||
                        'expiration_time_seconds': '1000000000000000000000',
 | 
			
		||||
                        'fee_recipient_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
                        'maker_address': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
 | 
			
		||||
                        'maker_asset_amount': '1000000000000000000',
 | 
			
		||||
                        'maker_asset_data': '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
 | 
			
		||||
                        'maker_fee': '0',
 | 
			
		||||
                        'salt': '2362734632784682376287462',
 | 
			
		||||
                        'sender_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
                        'taker_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
                        'taker_asset_amount': '500000000000000000000',
 | 
			
		||||
                        'taker_asset_data': '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa',
 | 
			
		||||
                        'taker_fee': '0'}}]}
 | 
			
		||||
 | 
			
		||||
Get Order
 | 
			
		||||
---------
 | 
			
		||||
Get an order by hash from an SRA-compliant Relayer.
 | 
			
		||||
 | 
			
		||||
>>> relayer_api.get_order(order_hash)  # doctest: +SKIP
 | 
			
		||||
{'meta_data': {},
 | 
			
		||||
'order': {'exchange_address': '0x35dd2932454449b14cee11a94d3674a936d5d7b2',
 | 
			
		||||
            'expiration_time_seconds': '1000000000000000000000',
 | 
			
		||||
            'fee_recipient_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
            'maker_address': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
 | 
			
		||||
            'maker_asset_amount': '1000000000000000000',
 | 
			
		||||
            'maker_asset_data': '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
 | 
			
		||||
            'maker_fee': '0',
 | 
			
		||||
            'salt': '2362734632784682376287462',
 | 
			
		||||
            'sender_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
            'taker_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
            'taker_asset_amount': '500000000000000000000',
 | 
			
		||||
            'taker_asset_data': '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa',
 | 
			
		||||
            'taker_fee': '0'}},
 | 
			
		||||
 | 
			
		||||
Get Asset Pair
 | 
			
		||||
---------------
 | 
			
		||||
Get available asset pairs from an SRA-compliant Relayer.
 | 
			
		||||
 | 
			
		||||
>>> relayer_api.get_asset_pairs()
 | 
			
		||||
{'records': [{'assetDataA': {'assetData': '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
 | 
			
		||||
                             'maxAmount': '115792089237316195423570985008687907853269984665640564039457584007913129639936',
 | 
			
		||||
                             'minAmount': '0',
 | 
			
		||||
                             'precision': 18},
 | 
			
		||||
              'assetDataB': {'assetData': '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa',
 | 
			
		||||
                             'maxAmount': '115792089237316195423570985008687907853269984665640564039457584007913129639936',
 | 
			
		||||
                             'minAmount': '0',
 | 
			
		||||
                             'precision': 18}}]}
 | 
			
		||||
 | 
			
		||||
Get Orderbook
 | 
			
		||||
-------------
 | 
			
		||||
Get the orderbook for the WETH/ZRX asset pair from an SRA-compliant Relayer.
 | 
			
		||||
 | 
			
		||||
>>> relayer_api.get_orderbook(
 | 
			
		||||
...     base_asset_data=weth_asset_data,
 | 
			
		||||
...     quote_asset_data=zrx_asset_data)
 | 
			
		||||
{'asks': {'records': [{'meta_data': {},
 | 
			
		||||
                       'order': {'exchange_address': '0x35dd2932454449b14cee11a94d3674a936d5d7b2',
 | 
			
		||||
                                 'expiration_time_seconds': '1000000000000000000000',
 | 
			
		||||
                                 'fee_recipient_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
                                 'maker_address': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
 | 
			
		||||
                                 'maker_asset_amount': '1000000000000000000',
 | 
			
		||||
                                 'maker_asset_data': '0xf47261b0000000000000000000000000d0a1e359811322d97991e03f863a0c30c2cf029c',
 | 
			
		||||
                                 'maker_fee': '0',
 | 
			
		||||
                                 'salt': '2362734632784682376287462',
 | 
			
		||||
                                 'sender_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
                                 'taker_address': '0x0000000000000000000000000000000000000000',
 | 
			
		||||
                                 'taker_asset_amount': '500000000000000000000',
 | 
			
		||||
                                 'taker_asset_data': '0xf47261b00000000000000000000000002002d3812f58e35f0ea1ffbf80a75a38c32175fa',
 | 
			
		||||
                                 'taker_fee': '0'}}]},
 | 
			
		||||
 'bids': {'records': []}}
 | 
			
		||||
"""  # noqa: E501 (line too long)
 | 
			
		||||
 | 
			
		||||
# NOTE: Bug in get_order method.
 | 
			
		||||
# Sra_client not deserialzing order from server properly, need fix!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
@@ -28,7 +189,7 @@ from sra_client.models.relayer_api_asset_data_trade_info_schema import (
 | 
			
		||||
from sra_client.models.relayer_api_error_response_schema import (
 | 
			
		||||
    RelayerApiErrorResponseSchema,
 | 
			
		||||
)
 | 
			
		||||
from sra_client.models.relayer_api_error_response_schema_validation_errors import (
 | 
			
		||||
from sra_client.models.relayer_api_error_response_schema_validation_errors import (  # noqa: E501 (line too long)
 | 
			
		||||
    RelayerApiErrorResponseSchemaValidationErrors,
 | 
			
		||||
)
 | 
			
		||||
from sra_client.models.relayer_api_fee_recipients_response_schema import (
 | 
			
		||||
@@ -44,7 +205,7 @@ from sra_client.models.relayer_api_order_schema import RelayerApiOrderSchema
 | 
			
		||||
from sra_client.models.relayer_api_orderbook_response_schema import (
 | 
			
		||||
    RelayerApiOrderbookResponseSchema,
 | 
			
		||||
)
 | 
			
		||||
from sra_client.models.relayer_api_orders_channel_subscribe_payload_schema import (
 | 
			
		||||
from sra_client.models.relayer_api_orders_channel_subscribe_payload_schema import (  # noqa: E501 (line too long)
 | 
			
		||||
    RelayerApiOrdersChannelSubscribePayloadSchema,
 | 
			
		||||
)
 | 
			
		||||
from sra_client.models.relayer_api_orders_channel_subscribe_schema import (
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -207,8 +207,7 @@ class ApiClient(object):
 | 
			
		||||
 | 
			
		||||
        If obj is None, return None.
 | 
			
		||||
        If obj is str, int, long, float, bool, return directly.
 | 
			
		||||
        If obj is datetime.datetime, datetime.date
 | 
			
		||||
            convert to string in iso8601 format.
 | 
			
		||||
        If obj is datetime.datetime, datetime.date convert to string in iso8601 format.
 | 
			
		||||
        If obj is list, sanitize each element in the list.
 | 
			
		||||
        If obj is dict, return the dict.
 | 
			
		||||
        If obj is OpenAPI model, return the properties dict.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1 @@
 | 
			
		||||
"""Test for sra_client."""
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
"""Test the default api client"""
 | 
			
		||||
# coding: utf-8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -5,30 +6,92 @@ from __future__ import absolute_import
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import sra_client
 | 
			
		||||
from sra_client.api.default_api import DefaultApi  # noqa: E501
 | 
			
		||||
from sra_client.models.relayer_api_asset_data_pairs_response_schema import (
 | 
			
		||||
    RelayerApiAssetDataPairsResponseSchema
 | 
			
		||||
)
 | 
			
		||||
from sra_client.rest import ApiException
 | 
			
		||||
from sra_client import ApiClient, Configuration
 | 
			
		||||
from sra_client.api import DefaultApi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDefaultApi(unittest.TestCase):
 | 
			
		||||
    """DefaultApi unit test stubs"""
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.api = sra_client.api.default_api.DefaultApi()  # noqa: E501
 | 
			
		||||
        config = Configuration()
 | 
			
		||||
        config.host = "http://localhost:3000"
 | 
			
		||||
        self.api = DefaultApi(ApiClient(config))
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=too-many-locals
 | 
			
		||||
    def test_get_asset_pairs(self):
 | 
			
		||||
        """Test case for get_asset_pairs
 | 
			
		||||
 | 
			
		||||
        """
 | 
			
		||||
        expected = RelayerApiAssetDataPairsResponseSchema([])
 | 
			
		||||
        expected = {
 | 
			
		||||
            "records": [
 | 
			
		||||
                {
 | 
			
		||||
                    "assetDataA": {
 | 
			
		||||
                        "assetData": "0xf47261b0000000000000000000000000"
 | 
			
		||||
                        "d0a1e359811322d97991e03f863a0c30c2cf029c",
 | 
			
		||||
                        "maxAmount": "115792089237316195423570985008687907853"
 | 
			
		||||
                        "269984665640564039457584007913129639936",
 | 
			
		||||
                        "minAmount": "0",
 | 
			
		||||
                        "precision": 18,
 | 
			
		||||
                    },
 | 
			
		||||
                    "assetDataB": {
 | 
			
		||||
                        "assetData": "0xf47261b0000000000000000000000000"
 | 
			
		||||
                        "2002d3812f58e35f0ea1ffbf80a75a38c32175fa",
 | 
			
		||||
                        "maxAmount": "115792089237316195423570985008687907853"
 | 
			
		||||
                        "269984665640564039457584007913129639936",
 | 
			
		||||
                        "minAmount": "0",
 | 
			
		||||
                        "precision": 18,
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actual = self.api.get_asset_pairs()
 | 
			
		||||
        self.assertEqual(actual, expected)
 | 
			
		||||
 | 
			
		||||
        acutal_asset_data_a = actual.records[0]["assetDataA"]["assetData"]
 | 
			
		||||
        expected_asset_data_a = expected["records"][0]["assetDataA"][
 | 
			
		||||
            "assetData"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_asset_data_a, expected_asset_data_a)
 | 
			
		||||
        acutal_max_amount_a = actual.records[0]["assetDataA"]["maxAmount"]
 | 
			
		||||
        expected_max_amount_a = expected["records"][0]["assetDataA"][
 | 
			
		||||
            "maxAmount"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_max_amount_a, expected_max_amount_a)
 | 
			
		||||
        acutal_min_amount_a = actual.records[0]["assetDataA"]["minAmount"]
 | 
			
		||||
        expected_min_amount_a = expected["records"][0]["assetDataA"][
 | 
			
		||||
            "minAmount"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_min_amount_a, expected_min_amount_a)
 | 
			
		||||
        acutal_precision_a = actual.records[0]["assetDataA"]["precision"]
 | 
			
		||||
        expected_precision_a = expected["records"][0]["assetDataA"][
 | 
			
		||||
            "precision"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_precision_a, expected_precision_a)
 | 
			
		||||
 | 
			
		||||
        acutal_asset_data_b = actual.records[0]["assetDataB"]["assetData"]
 | 
			
		||||
        expected_asset_data_b = expected["records"][0]["assetDataB"][
 | 
			
		||||
            "assetData"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_asset_data_b, expected_asset_data_b)
 | 
			
		||||
        acutal_max_amount_b = actual.records[0]["assetDataB"]["maxAmount"]
 | 
			
		||||
        expected_max_amount_b = expected["records"][0]["assetDataB"][
 | 
			
		||||
            "maxAmount"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_max_amount_b, expected_max_amount_b)
 | 
			
		||||
        acutal_min_amount_b = actual.records[0]["assetDataB"]["minAmount"]
 | 
			
		||||
        expected_min_amount_b = expected["records"][0]["assetDataB"][
 | 
			
		||||
            "minAmount"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_min_amount_b, expected_min_amount_b)
 | 
			
		||||
        acutal_precision_b = actual.records[0]["assetDataB"]["precision"]
 | 
			
		||||
        expected_precision_b = expected["records"][0]["assetDataB"][
 | 
			
		||||
            "precision"
 | 
			
		||||
        ]
 | 
			
		||||
        self.assertEqual(acutal_precision_b, expected_precision_b)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user