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:
parent
28c4ca73ab
commit
3099ba71eb
@ -17,7 +17,6 @@ PACKAGE_DEPENDENCY_LIST = [
|
|||||||
"sra_client",
|
"sra_client",
|
||||||
"order_utils",
|
"order_utils",
|
||||||
"middlewares",
|
"middlewares",
|
||||||
"contract_demo"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for package in PACKAGE_DEPENDENCY_LIST:
|
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
|
from os import path
|
||||||
import json
|
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
|
contracts deployed on it. For convenience, a docker container is provided for
|
||||||
just this purpose. To start it:
|
just this purpose. To start it:
|
||||||
`docker run -d -p 8545:8545 0xorg/ganache-cli:2.2.2`:code:.
|
`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 copy import copy
|
||||||
from enum import auto, Enum
|
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",
|
"version": "1.0.1",
|
||||||
"changes": [
|
"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
|
# pylint: disable=invalid-name
|
||||||
# because these variables are not named in upper case, as globals should be.
|
# 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
|
# pylint: disable=redefined-builtin
|
||||||
copyright = "2018, ZeroEx, Intl."
|
copyright = "2018, ZeroEx, Intl."
|
||||||
author = "F. Eugene Aumson"
|
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
|
release = "" # The full version, including alpha/beta/rc tags
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
@ -22,6 +22,7 @@ extensions = [
|
|||||||
"sphinx.ext.intersphinx",
|
"sphinx.ext.intersphinx",
|
||||||
"sphinx.ext.coverage",
|
"sphinx.ext.coverage",
|
||||||
"sphinx.ext.viewcode",
|
"sphinx.ext.viewcode",
|
||||||
|
"sphinx_autodoc_typehints",
|
||||||
]
|
]
|
||||||
|
|
||||||
templates_path = ["doc_templates"]
|
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".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "contract_demopydoc"
|
htmlhelp_basename = "sraclientpydoc"
|
||||||
|
|
||||||
# -- Extension configuration:
|
# -- Extension configuration:
|
||||||
|
|
@ -1,13 +1,20 @@
|
|||||||
.. source for the sphinx-generated build/docs/web/index.html
|
.. source for the sphinx-generated build/docs/web/index.html
|
||||||
|
|
||||||
Python demo of 0x Smart Contracts
|
Python zero_ex.sra_client.api_client
|
||||||
=================================
|
====================================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
.. automodule:: test.test_exchange
|
.. automodule:: sra_client
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
|
||||||
|
.. automodule:: sra_client.api.default_api
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
@ -1,14 +1,16 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""setuptools module for sra_client package."""
|
||||||
|
|
||||||
import subprocess
|
import subprocess # nosec
|
||||||
import distutils.command.build_py
|
import distutils.command.build_py
|
||||||
|
|
||||||
from setuptools import setup, find_packages # noqa: H301
|
from setuptools import setup, find_packages # noqa: H301
|
||||||
|
from setuptools.command.test import test as TestCommand
|
||||||
|
|
||||||
NAME = "0x-sra-client"
|
NAME = "0x-sra-client"
|
||||||
VERSION = "1.0.0"
|
VERSION = "1.0.1"
|
||||||
# To install the library, run the following
|
# To install the library, run the following
|
||||||
#
|
#
|
||||||
# python setup.py install
|
# 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"]
|
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):
|
class TestPublishCommand(distutils.command.build_py.build_py):
|
||||||
"""Custom command to publish to test.pypi.org."""
|
"""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):
|
class PublishCommand(distutils.command.build_py.build_py):
|
||||||
"""Custom command to publish to pypi.org."""
|
"""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
|
subprocess.check_call("twine upload dist/*".split()) # nosec
|
||||||
|
|
||||||
|
|
||||||
class LintCommand(distutils.command.build_py.build_py):
|
class PublishDocsCommand(distutils.command.build_py.build_py):
|
||||||
"""No-op lint command to support top-level lint script."""
|
"""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):
|
def run(self):
|
||||||
pass
|
"""Run npm package `discharge` to build & upload docs."""
|
||||||
|
subprocess.check_call("discharge deploy".split()) # nosec
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@ -72,6 +142,33 @@ setup(
|
|||||||
cmdclass={
|
cmdclass={
|
||||||
"test_publish": TestPublishCommand,
|
"test_publish": TestPublishCommand,
|
||||||
"publish": PublishCommand,
|
"publish": PublishCommand,
|
||||||
|
"launch_kit": LaunchKitCommand,
|
||||||
"lint": LintCommand,
|
"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
|
# 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
|
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 (
|
from sra_client.models.relayer_api_error_response_schema import (
|
||||||
RelayerApiErrorResponseSchema,
|
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,
|
RelayerApiErrorResponseSchemaValidationErrors,
|
||||||
)
|
)
|
||||||
from sra_client.models.relayer_api_fee_recipients_response_schema import (
|
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 (
|
from sra_client.models.relayer_api_orderbook_response_schema import (
|
||||||
RelayerApiOrderbookResponseSchema,
|
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,
|
RelayerApiOrdersChannelSubscribePayloadSchema,
|
||||||
)
|
)
|
||||||
from sra_client.models.relayer_api_orders_channel_subscribe_schema import (
|
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 None, return None.
|
||||||
If obj is str, int, long, float, bool, return directly.
|
If obj is str, int, long, float, bool, return directly.
|
||||||
If obj is datetime.datetime, datetime.date
|
If obj is datetime.datetime, datetime.date convert to string in iso8601 format.
|
||||||
convert to string in iso8601 format.
|
|
||||||
If obj is list, sanitize each element in the list.
|
If obj is list, sanitize each element in the list.
|
||||||
If obj is dict, return the dict.
|
If obj is dict, return the dict.
|
||||||
If obj is OpenAPI model, return the properties 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
|
# coding: utf-8
|
||||||
|
|
||||||
|
|
||||||
@ -5,30 +6,92 @@ from __future__ import absolute_import
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import sra_client
|
from sra_client import ApiClient, Configuration
|
||||||
from sra_client.api.default_api import DefaultApi # noqa: E501
|
from sra_client.api import DefaultApi
|
||||||
from sra_client.models.relayer_api_asset_data_pairs_response_schema import (
|
|
||||||
RelayerApiAssetDataPairsResponseSchema
|
|
||||||
)
|
|
||||||
from sra_client.rest import ApiException
|
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultApi(unittest.TestCase):
|
class TestDefaultApi(unittest.TestCase):
|
||||||
"""DefaultApi unit test stubs"""
|
"""DefaultApi unit test stubs"""
|
||||||
|
|
||||||
def setUp(self):
|
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):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# pylint: disable=too-many-locals
|
||||||
def test_get_asset_pairs(self):
|
def test_get_asset_pairs(self):
|
||||||
"""Test case for get_asset_pairs
|
"""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()
|
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__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user