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:
Michael Huang 2019-03-26 18:07:04 -05:00 committed by F. Eugene Aumson
parent 28c4ca73ab
commit 3099ba71eb
33 changed files with 872 additions and 697 deletions

View File

@ -17,7 +17,6 @@ PACKAGE_DEPENDENCY_LIST = [
"sra_client",
"order_utils",
"middlewares",
"contract_demo"
]
for package in PACKAGE_DEPENDENCY_LIST:

View File

@ -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
}

View File

@ -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.

View File

@ -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"),
}
},
)

View File

@ -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: ...
...

View File

@ -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: ...
...

View File

@ -1,4 +0,0 @@
from typing import Union
def to_checksum_address(value: Union[str, bytes]) -> str:
...

View File

@ -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]: ...

View File

@ -1,3 +0,0 @@
from setuptools import Command
class test(Command): ...

View File

@ -1,2 +0,0 @@
class Web3:
...

View File

@ -1,3 +0,0 @@
class Contract:
def call(self): ...
...

View File

@ -1 +0,0 @@
"""Demonstrations of calling 0x smart contracts."""

View File

@ -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

View File

@ -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

View File

@ -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

View 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`.

View File

@ -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
}
]

View File

@ -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:

View File

@ -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

View File

@ -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"),
}
},
)

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1 @@
"""Test for sra_client."""

View File

@ -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__":