Python doc polish (#1757)
* Exercise doctests as a test not as a linter * Add a contract artifact doctest, and exercise it * Clean up linter issues * Change asset data decoding output type Previously, it was a TypedDict, but that was causing problems. Sphinx seems to be broken, such that none of the fields of the class were being rendered into the doc. Thinking on it further, I decided that a NamedTuple makes more sense here anyways, since tuples are immutable and this output value isn't something someone should ever build or modify. And, NamedTuple is getting its fields properly rendered by Sphinx. * Add type annotations to JSON schemas docs * Add doc publish metadata file for middlewares pkg * Improve documentation Note that none of the changes to .py files impact functionality in any way, because the changes are restricted to "docstrings", which to the Python interpreter are simply no-op statements. However, one caveat to that is that much of these docstring changes DO affect the functionality of automated test runs, because all of the code examples (blocks beginning with `>>> `) are "doctests", which are exercised via the test framework. The index.rst files are the top-level templates for generating the documentation, and the "automodule"/"autoclass"/etc statements pull in the docstrings from the source code. * correct package name in doc URL * Move sra_client module into zero_ex namespace * Add functions to encode asset data to bytes * Fix: SRA client was deserializing orders weirdly The generated code was transforming the order structure, from the camel case field name format in the spec, into the snake case field name format expected by Python convention. With this problem in place, the only way to take an order from a relayer and send it to a contract (for fill, cancel, etc) was to manually transform the field names, one by one, into a new structure. * Fix problem with Web3/JSON order conversion utils * doctest: maker, trade ZRX for WETH, not vice versa * Remove redundant test * Construct order in native Python, not JSON Then convert it to JSON before sending it to the relayer. * doctest: simplify asset units * Add doctests for filling and cancelling * Minor doctetst copy edits; whitespace * Rename function, and add optional parameter * Tweak docstrings on JSON conversion functions. * Demo asset data decoding to view asset pairs * Demo selecting an order from the order book And have taker take it. * Rename variable * Abstract ganache from examples Doing that exposed excessive use of the verbose NETWORK_TO_ADDRESSES[NetworkId.Ganache] construct, so simplified that, which ripped into eliminating other temporary variables that had been used to hold specific contract addresses. Also cleaned up some misplaced import statements. * Add missing SRA client doc publication metadata * Ran prettier on new SRA client doc pub metadata * Remove local env customizations in doc metadata * Eliminate temporary variable * Rename variable * Show `pip install` in every package's doc * Doc NetorkID & pagination params as int, not float * Clean up unmatched parenthesis in docs
This commit is contained in:
parent
b896f82282
commit
0564ac1530
@ -10,6 +10,7 @@ from sys import argv
|
|||||||
from distutils.command.clean import clean
|
from distutils.command.clean import clean
|
||||||
import distutils.command.build_py
|
import distutils.command.build_py
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
from setuptools.command.test import test as TestCommand
|
||||||
|
|
||||||
|
|
||||||
class LintCommand(distutils.command.build_py.build_py):
|
class LintCommand(distutils.command.build_py.build_py):
|
||||||
@ -30,8 +31,6 @@ class LintCommand(distutils.command.build_py.build_py):
|
|||||||
"mypy src setup.py".split(),
|
"mypy src setup.py".split(),
|
||||||
# security issue checker:
|
# security issue checker:
|
||||||
"bandit -r src ./setup.py".split(),
|
"bandit -r src ./setup.py".split(),
|
||||||
# run doctests:
|
|
||||||
"pytest --doctest-modules".split(),
|
|
||||||
# general linter:
|
# general linter:
|
||||||
"pylint src setup.py".split(),
|
"pylint src setup.py".split(),
|
||||||
# pylint takes relatively long to run, so it runs last, to enable
|
# pylint takes relatively long to run, so it runs last, to enable
|
||||||
@ -103,6 +102,16 @@ class PublishDocsCommand(distutils.command.build_py.build_py):
|
|||||||
subprocess.check_call("discharge deploy".split()) # nosec
|
subprocess.check_call("discharge deploy".split()) # nosec
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandExtension(TestCommand):
|
||||||
|
"""Run pytest tests."""
|
||||||
|
|
||||||
|
def run_tests(self):
|
||||||
|
"""Invoke pytest."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
exit(pytest.main(["--doctest-modules"]))
|
||||||
|
|
||||||
|
|
||||||
with open("README.md", "r") as file_handle:
|
with open("README.md", "r") as file_handle:
|
||||||
README_MD = file_handle.read()
|
README_MD = file_handle.read()
|
||||||
|
|
||||||
@ -122,6 +131,7 @@ setup(
|
|||||||
cmdclass={
|
cmdclass={
|
||||||
"clean": CleanCommandExtension,
|
"clean": CleanCommandExtension,
|
||||||
"lint": LintCommand,
|
"lint": LintCommand,
|
||||||
|
"test": TestCommandExtension,
|
||||||
"test_publish": TestPublishCommand,
|
"test_publish": TestPublishCommand,
|
||||||
"publish": PublishCommand,
|
"publish": PublishCommand,
|
||||||
"publish_docs": PublishDocsCommand,
|
"publish_docs": PublishDocsCommand,
|
||||||
|
@ -7,15 +7,20 @@ Python zero_ex.contract_addresses
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
.. autoclass:: zero_ex.contract_addresses.NetworkId
|
.. automodule:: zero_ex.contract_addresses
|
||||||
|
:no-members:
|
||||||
|
|
||||||
See source for enum members.
|
.. autoclass:: zero_ex.contract_addresses.NetworkId
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
.. autoclass:: zero_ex.contract_addresses.ContractAddresses
|
.. autoclass:: zero_ex.contract_addresses.ContractAddresses
|
||||||
:members:
|
:members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
.. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES
|
.. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES
|
||||||
:annotation:
|
:annotation: : Dict[NetworkId, ContractAddresses]
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
@ -1,22 +1,50 @@
|
|||||||
"""Addresses at which the 0x smart contracts have been deployed."""
|
"""Addresses at which the 0x smart contracts have been deployed.
|
||||||
|
|
||||||
|
Setup
|
||||||
|
-----
|
||||||
|
|
||||||
|
Install the package with pip::
|
||||||
|
|
||||||
|
pip install 0x-contract-addresses
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, NamedTuple
|
from typing import Dict, NamedTuple
|
||||||
|
|
||||||
|
|
||||||
class ContractAddresses(NamedTuple): # noqa
|
class ContractAddresses(NamedTuple):
|
||||||
"""An abstract record listing all the contracts that have addresses."""
|
"""An abstract record listing all the contracts that have addresses."""
|
||||||
|
|
||||||
erc20_proxy: str
|
erc20_proxy: str
|
||||||
|
"""Address of the ERC20Proxy contract."""
|
||||||
|
|
||||||
erc721_proxy: str
|
erc721_proxy: str
|
||||||
|
"""Address of the ERC20Proxy contract."""
|
||||||
|
|
||||||
zrx_token: str
|
zrx_token: str
|
||||||
|
"""Address of the ZRX token contract."""
|
||||||
|
|
||||||
ether_token: str
|
ether_token: str
|
||||||
|
"""Address of the WETH token contract."""
|
||||||
|
|
||||||
exchange: str
|
exchange: str
|
||||||
|
"""Address of the Exchange contract."""
|
||||||
|
|
||||||
asset_proxy_owner: str
|
asset_proxy_owner: str
|
||||||
|
"""Address of the AssetProxyOwner contract."""
|
||||||
|
|
||||||
forwarder: str
|
forwarder: str
|
||||||
|
"""Address of the Forwarder contract."""
|
||||||
|
|
||||||
order_validator: str
|
order_validator: str
|
||||||
|
"""Address of the OrderValidator contract."""
|
||||||
|
|
||||||
coordinator_registry: str
|
coordinator_registry: str
|
||||||
|
"""Address of the CoordinatorRegistry contract."""
|
||||||
|
|
||||||
coordinator: str
|
coordinator: str
|
||||||
|
"""Address of the Coordinator contract."""
|
||||||
|
|
||||||
|
|
||||||
class NetworkId(Enum):
|
class NetworkId(Enum):
|
||||||
@ -24,6 +52,9 @@ class NetworkId(Enum):
|
|||||||
|
|
||||||
>>> NetworkId.MAINNET
|
>>> NetworkId.MAINNET
|
||||||
<NetworkId.MAINNET: 1>
|
<NetworkId.MAINNET: 1>
|
||||||
|
|
||||||
|
>>> NetworkId.MAINNET.value
|
||||||
|
1
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MAINNET = 1
|
MAINNET = 1
|
||||||
@ -98,7 +129,7 @@ NETWORK_TO_ADDRESSES: Dict[NetworkId, ContractAddresses] = {
|
|||||||
"""A mapping from instances of NetworkId to instances of ContractAddresses.
|
"""A mapping from instances of NetworkId to instances of ContractAddresses.
|
||||||
|
|
||||||
Addresses under NetworkId.Ganache are from our Ganache snapshot generated from
|
Addresses under NetworkId.Ganache are from our Ganache snapshot generated from
|
||||||
migrations.
|
npm package @0x/migrations.
|
||||||
|
|
||||||
>>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange
|
>>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange
|
||||||
0x4f833a24e1f95d70f028921e27040ca56e09ab0b
|
0x4f833a24e1f95d70f028921e27040ca56e09ab0b
|
||||||
|
@ -10,6 +10,7 @@ from sys import argv
|
|||||||
from distutils.command.clean import clean
|
from distutils.command.clean import clean
|
||||||
import distutils.command.build_py
|
import distutils.command.build_py
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
from setuptools.command.test import test as TestCommand
|
||||||
|
|
||||||
|
|
||||||
class LintCommand(distutils.command.build_py.build_py):
|
class LintCommand(distutils.command.build_py.build_py):
|
||||||
@ -110,6 +111,16 @@ class PublishDocsCommand(distutils.command.build_py.build_py):
|
|||||||
subprocess.check_call("discharge deploy".split()) # nosec
|
subprocess.check_call("discharge deploy".split()) # nosec
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandExtension(TestCommand):
|
||||||
|
"""Run pytest tests."""
|
||||||
|
|
||||||
|
def run_tests(self):
|
||||||
|
"""Invoke pytest."""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
exit(pytest.main(["--doctest-modules"]))
|
||||||
|
|
||||||
|
|
||||||
with open("README.md", "r") as file_handle:
|
with open("README.md", "r") as file_handle:
|
||||||
README_MD = file_handle.read()
|
README_MD = file_handle.read()
|
||||||
|
|
||||||
@ -129,6 +140,7 @@ setup(
|
|||||||
cmdclass={
|
cmdclass={
|
||||||
"clean": CleanCommandExtension,
|
"clean": CleanCommandExtension,
|
||||||
"lint": LintCommand,
|
"lint": LintCommand,
|
||||||
|
"test": TestCommandExtension,
|
||||||
"test_publish": TestPublishCommand,
|
"test_publish": TestPublishCommand,
|
||||||
"publish": PublishCommand,
|
"publish": PublishCommand,
|
||||||
"publish_docs": PublishDocsCommand,
|
"publish_docs": PublishDocsCommand,
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
"""0x smart contract compilation artifacts."""
|
"""0x smart contract compilation artifacts.
|
||||||
|
|
||||||
|
Setup
|
||||||
|
-----
|
||||||
|
|
||||||
|
Install the package with pip::
|
||||||
|
|
||||||
|
pip install 0x-contract-artifacts
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
@ -31,5 +39,21 @@ class _ArtifactCache:
|
|||||||
|
|
||||||
|
|
||||||
def abi_by_name(contract_name: str) -> Dict:
|
def abi_by_name(contract_name: str) -> Dict:
|
||||||
"""Return the ABI for the named contract."""
|
"""Return the ABI for the named contract.
|
||||||
|
|
||||||
|
Contract names must correspond to files in the package's `artifacts`:code:
|
||||||
|
directory, without the `.json`:code: suffix.
|
||||||
|
|
||||||
|
>>> from pprint import pprint
|
||||||
|
>>> pprint(abi_by_name("IValidator"))
|
||||||
|
[{'constant': True,
|
||||||
|
'inputs': [{'name': 'hash', 'type': 'bytes32'},
|
||||||
|
{'name': 'signerAddress', 'type': 'address'},
|
||||||
|
{'name': 'signature', 'type': 'bytes'}],
|
||||||
|
'name': 'isValidSignature',
|
||||||
|
'outputs': [{'name': 'isValid', 'type': 'bool'}],
|
||||||
|
'payable': False,
|
||||||
|
'stateMutability': 'view',
|
||||||
|
'type': 'function'}]
|
||||||
|
"""
|
||||||
return _ArtifactCache.contract_name_to_abi(contract_name)
|
return _ArtifactCache.contract_name_to_abi(contract_name)
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
disable=C0330,line-too-long,fixme,too-few-public-methods,too-many-ancestors,too-many-arguments
|
disable=C0330,line-too-long,fixme,too-few-public-methods,too-many-ancestors
|
||||||
# C0330 is "bad hanging indent". we use indents per `black`.
|
# C0330 is "bad hanging indent". we use indents per `black`.
|
||||||
min-similarity-lines=10
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
min-similarity-lines=6
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Python zero_ex.contract_wrappers
|
Python zero_ex.contract_wrappers
|
||||||
================================================
|
================================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
@ -7,27 +7,26 @@ Python zero_ex.contract_wrappers
|
|||||||
|
|
||||||
.. automodule:: zero_ex.contract_wrappers
|
.. automodule:: zero_ex.contract_wrappers
|
||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
|
||||||
|
|
||||||
|
|
||||||
zero_ex.contract_wrappers.Exchange
|
zero_ex.contract_wrappers.Exchange
|
||||||
----------------------------------
|
==================================
|
||||||
|
|
||||||
.. autoclass:: zero_ex.contract_wrappers.Exchange
|
.. autoclass:: zero_ex.contract_wrappers.Exchange
|
||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:special-members:
|
||||||
|
|
||||||
|
|
||||||
zero_ex.contract_wrappers.ERC20Token
|
zero_ex.contract_wrappers.ERC20Token
|
||||||
-------------------------------------
|
====================================
|
||||||
|
|
||||||
.. autoclass:: zero_ex.contract_wrappers.ERC20Token
|
.. autoclass:: zero_ex.contract_wrappers.ERC20Token
|
||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:special-members:
|
||||||
|
|
||||||
|
|
||||||
zero_ex.contract_wrappers.TxParams
|
zero_ex.contract_wrappers.TxParams
|
||||||
----------------------------------
|
==================================
|
||||||
|
|
||||||
.. autoclass:: zero_ex.contract_wrappers.TxParams
|
.. autoclass:: zero_ex.contract_wrappers.TxParams
|
||||||
:members:
|
:members:
|
||||||
|
@ -1,350 +1,290 @@
|
|||||||
"""Python wrappers for interacting with 0x smart contracts.
|
"""Python wrappers for interacting with 0x smart contracts.
|
||||||
|
|
||||||
The smart contract wrappers have simplified interfaces,
|
The smart contract wrappers have simplified interfaces, performing client-side
|
||||||
and perform client-side validation on transactions and throw
|
validation on transactions, and throwing helpful error messages.
|
||||||
helpful error messages.
|
|
||||||
|
|
||||||
Installing
|
Setup
|
||||||
==========
|
-----
|
||||||
Install the 0x-contract-wrappers with pip:
|
|
||||||
|
|
||||||
``pip install 0x-contract-wrappers``
|
Install the 0x-contract-wrappers with pip::
|
||||||
|
|
||||||
Demo
|
pip install 0x-contract-wrappers
|
||||||
====
|
|
||||||
We will demonstrate some basic steps to help you get started trading on 0x.
|
|
||||||
|
|
||||||
Importing packages
|
We need a Web3 provider to allow us to talk to the blockchain. You can
|
||||||
------------------
|
read `more about providers in the Web3.py documentation
|
||||||
|
<https://web3py.readthedocs.io/en/stable/providers.htm>`_. The examples below
|
||||||
|
assume there's a local instance of Ganache listening on port 8545:
|
||||||
|
|
||||||
The first step to interact with the 0x smart contract is to import
|
>>> from web3 import HTTPProvider
|
||||||
the following relevant packages:
|
>>> ganache = HTTPProvider("http://localhost:8545")
|
||||||
|
|
||||||
>>> import random
|
To replicate these examples, one can use the `0xorg/ganache-cli`:code: docker
|
||||||
>>> from eth_utils import to_checksum_address
|
image, which comes with the 0x contracts pre-deployed. To start it::
|
||||||
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
|
||||||
>>> from zero_ex.contract_wrappers import (
|
|
||||||
... ERC20Token, Exchange, TxParams
|
|
||||||
... )
|
|
||||||
>>> from zero_ex.order_utils import(
|
|
||||||
... sign_hash, generate_order_hash_hex)
|
|
||||||
|
|
||||||
Provider
|
docker run docker run -d -p 8545:8545 0xorg/ganache-cli
|
||||||
|
|
||||||
|
Accounts
|
||||||
--------
|
--------
|
||||||
|
|
||||||
We need a web3 provider to allow us to talk to the blockchain. You can
|
In the examples below, we will use the accounts provided by Ganache, which are
|
||||||
read more about providers
|
accessible through the Web3 instance. The first account will be the maker, and
|
||||||
`here <https://web3py.readthedocs.io/en/stable/providers.htm>`__. In our
|
the second account will be the taker.
|
||||||
case, we are using our local node (ganache), we will connect to our provider
|
|
||||||
at http://localhost:8545.
|
|
||||||
|
|
||||||
>>> from web3 import HTTPProvider, Web3
|
>>> from web3 import Web3
|
||||||
>>> provider = HTTPProvider("http://localhost:8545")
|
>>> accounts = Web3(ganache).eth.accounts
|
||||||
>>> # Create a web3 instance from the provider
|
>>> maker_address = accounts[0].lower()
|
||||||
>>> web3_instance = Web3(provider)
|
>>> taker_address = accounts[1].lower()
|
||||||
|
|
||||||
Declaring Decimals and Addresses
|
In the examples below, we'll use the optional `tx_params`:code: parameter to
|
||||||
---------------------------------
|
the contract calls, in order to specify which account each transaction is to
|
||||||
|
originate from. Under normal circumstances, your provider will have a default
|
||||||
|
account which will be used if you decline to specify an originating address.
|
||||||
|
For convenience, a `TxParams`:code: class is provided:
|
||||||
|
|
||||||
Since we are dealing with a few contracts, we will specify them now to
|
>>> from zero_ex.contract_wrappers import TxParams
|
||||||
reduce the syntax load. Fortunately for us, the 0x python packages comes
|
|
||||||
with a couple of contract addresses that can be useful to have on hand.
|
|
||||||
One thing that is important to remember is that there are no decimals in
|
|
||||||
the Ethereum virtual machine (EVM), which means you always need to keep
|
|
||||||
track of how many "decimals" each token possesses. Since we will sell some
|
|
||||||
ZRX for some ETH and since they both have 18 decimals, we can use a shared
|
|
||||||
constant. Let us first get the addresses of the WETH and ZRX tokens on
|
|
||||||
the test network Ganache:
|
|
||||||
|
|
||||||
|
Contract Addresses
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The `0x-contract-addresses`:code: package (which is used by
|
||||||
|
`0x-contract-wrappers`:code: and thus gets installed along with it) provides
|
||||||
|
the addresses of the 0x contracts on each network, including those that come
|
||||||
|
pre-deployed deployed in the `0xorg/ganache-cli`:code: docker image. Let's
|
||||||
|
capture the addresses we'll use throughout the examples below:
|
||||||
|
|
||||||
|
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||||
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
||||||
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token
|
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token
|
||||||
|
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange
|
||||||
|
|
||||||
Approvals and WETH Balance
|
Wrapping ETH
|
||||||
--------------------------
|
------------
|
||||||
|
|
||||||
To trade on 0x, the participants (maker and taker) require a small
|
The examples below demonstrate constructing an order with the maker providing
|
||||||
amount of initial set up. They need to approve the 0x smart contracts
|
ZRX in exchange for the taker providing some WETH. For the order to be valid,
|
||||||
to move funds on their behalf. In order to give 0x protocol smart contract
|
our Taker first needs to wrap some ether as WETH.
|
||||||
access to funds, we need to set allowances (you can read about allowances
|
|
||||||
`here <https://tokenallowance.io/>`__).
|
|
||||||
In our demo the taker asset is WETH (or Wrapped ETH, you can read about WETH
|
|
||||||
`here <https://weth.io/>`__).,
|
|
||||||
as ETH is not an ERC20 token it must first be converted into WETH to be
|
|
||||||
used by 0x. Concretely, "converting" ETH to WETH means that we will deposit
|
|
||||||
some ETH in a smart contract acting as a ERC20 wrapper. In exchange of
|
|
||||||
depositing ETH, we will get some ERC20 compliant tokens called WETH at a
|
|
||||||
1:1 conversion rate. For example, depositing 10 ETH will give us back 10 WETH
|
|
||||||
and we can revert the process at any time.
|
|
||||||
|
|
||||||
In this demo, we will use test accounts on Ganache, which are accessible
|
First get an instance of the WETH contract on the network:
|
||||||
through the Web3 instance. The first account will be the maker, and the second
|
|
||||||
account will be the taker.
|
|
||||||
|
|
||||||
>>> import pprint
|
|
||||||
>>> # Instantiate an instance of the erc20_wrapper with the provider
|
|
||||||
>>> erc20_wrapper = ERC20Token(provider)
|
|
||||||
>>> # Get accounts from the web 3 instance
|
|
||||||
>>> accounts = web3_instance.eth.accounts
|
|
||||||
>>> pprint.pprint(accounts)
|
|
||||||
['0x5409ED021D9299bf6814279A6A1411A7e866A631',
|
|
||||||
'0x6Ecbe1DB9EF729CBe972C83Fb886247691Fb6beb',
|
|
||||||
'0xE36Ea790bc9d7AB70C55260C66D52b1eca985f84',
|
|
||||||
'0xE834EC434DABA538cd1b9Fe1582052B880BD7e63',
|
|
||||||
'0x78dc5D2D739606d31509C31d654056A45185ECb6',
|
|
||||||
'0xA8dDa8d7F5310E4A9E24F8eBA77E091Ac264f872',
|
|
||||||
'0x06cEf8E666768cC40Cc78CF93d9611019dDcB628',
|
|
||||||
'0x4404ac8bd8F9618D27Ad2f1485AA1B2cFD82482D',
|
|
||||||
'0x7457d5E02197480Db681D3fdF256c7acA21bDc12',
|
|
||||||
'0x91c987bf62D25945dB517BDAa840A6c661374402']
|
|
||||||
|
|
||||||
>>> maker = accounts[0]
|
|
||||||
>>> taker = accounts[1]
|
|
||||||
|
|
||||||
Now we need to allow the 0x ERC20 Proxy to move WETH on behalf of our
|
|
||||||
maker and taker accounts. Let's let our maker and taker here approve
|
|
||||||
the 0x ERC20 Proxy an allowance of 100 WETH.
|
|
||||||
|
|
||||||
>>> # Multiplying by 10 ** 18 to account for decimals
|
|
||||||
>>> ALLOWANCE = (100) * 10 ** 18
|
|
||||||
>>> erc20_proxy = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy
|
|
||||||
|
|
||||||
>>> # Set allowance to the erc20_proxy from maker account
|
|
||||||
>>> tx = erc20_wrapper.approve(
|
|
||||||
... weth_address,
|
|
||||||
... erc20_proxy,
|
|
||||||
... ALLOWANCE,
|
|
||||||
... tx_params=TxParams(from_=maker),
|
|
||||||
... )
|
|
||||||
>>> # Check the allowance given to the 0x ERC20 Proxy
|
|
||||||
>>> maker_allowance = erc20_wrapper.allowance(
|
|
||||||
... weth_address,
|
|
||||||
... maker,
|
|
||||||
... erc20_proxy,
|
|
||||||
... )
|
|
||||||
>>> (maker_allowance) // 10 ** 18
|
|
||||||
100
|
|
||||||
|
|
||||||
>>> # Set allowance to the erc20_proxy from taker account
|
|
||||||
>>> tx = erc20_wrapper.approve(
|
|
||||||
... weth_address,
|
|
||||||
... erc20_proxy,
|
|
||||||
... ALLOWANCE,
|
|
||||||
... tx_params=TxParams(from_=taker),
|
|
||||||
... )
|
|
||||||
>>> # Check the allowance given to the 0x ERC20 Proxy
|
|
||||||
>>> taker_allowance = erc20_wrapper.allowance(
|
|
||||||
... weth_address,
|
|
||||||
... taker,
|
|
||||||
... erc20_proxy,
|
|
||||||
... )
|
|
||||||
>>> (taker_allowance) // 10 ** 18
|
|
||||||
100
|
|
||||||
|
|
||||||
To give our accounts some initial WETH balance, we'll need
|
|
||||||
to *wrap* some ETH to get WETH. The WETH token contract
|
|
||||||
contains two extra methods, not included in the ERC20 token
|
|
||||||
standard, so we will grab the ABI for the WETH Token contract
|
|
||||||
and call the deposit method to wrap our ETH. Here is how we do so.
|
|
||||||
|
|
||||||
>>> from zero_ex.contract_artifacts import abi_by_name
|
>>> from zero_ex.contract_artifacts import abi_by_name
|
||||||
>>> # Converting 0.5 ETH to base unit wei
|
>>> weth_instance = Web3(ganache).eth.contract(
|
||||||
>>> deposit_amount = int(0.5 * 10 ** 18)
|
... address=Web3.toChecksumAddress(weth_address),
|
||||||
|
... abi=abi_by_name("WETH9")
|
||||||
|
... )
|
||||||
|
|
||||||
>>> # Let's have our maker wrap 1 ETH for 1 WETH
|
Then have the Taker deposit some ETH into that contract, which will result in
|
||||||
>>> tx = erc20_wrapper.execute_method(
|
it receiving WETH:
|
||||||
... address=weth_address,
|
|
||||||
... abi=abi_by_name("WETH9"),
|
|
||||||
... method="deposit",
|
|
||||||
... tx_params=TxParams(from_=maker, value=deposit_amount))
|
|
||||||
>>> # Checking our maker's WETH balance
|
|
||||||
>>> maker_balance = erc20_wrapper.balance_of(
|
|
||||||
... token_address=weth_address, owner_address=maker)
|
|
||||||
>>> (maker_balance) / 10 ** 18 # doctest: +SKIP
|
|
||||||
0.5
|
|
||||||
|
|
||||||
>>> # Let's have our taker wrap 0.5 ETH as well
|
>>> from eth_utils import to_wei
|
||||||
>>> tx = erc20_wrapper.execute_method(
|
>>> weth_instance.functions.deposit().transact(
|
||||||
... address=weth_address,
|
... {"from": Web3.toChecksumAddress(taker_address),
|
||||||
... abi=abi_by_name("WETH9"),
|
... "value": to_wei(1, 'ether')}
|
||||||
... method="deposit",
|
... )
|
||||||
... tx_params=TxParams(from_=taker, value=deposit_amount))
|
HexBytes('0x...')
|
||||||
>>> # Checking our taker's WETH balance
|
|
||||||
>>> taker_balance = erc20_wrapper.balance_of(
|
|
||||||
... token_address=weth_address, owner_address=taker)
|
|
||||||
>>> (taker_balance) / 10 ** 18 # doctest: +SKIP
|
|
||||||
0.5
|
|
||||||
|
|
||||||
Now we can trade our WETH tokens on 0x!
|
Approvals
|
||||||
|
---------
|
||||||
|
|
||||||
Signing an order
|
In order to trade on 0x, one must approve the 0x smart contracts to transfer
|
||||||
----------------
|
their tokens. Because the order constructed below has the maker giving WETH,
|
||||||
|
we need to tell the WETH token contract to let the 0x contracts transfer our
|
||||||
|
balance:
|
||||||
|
|
||||||
Here is an example of a JSON order previously generated by our maker
|
>>> from zero_ex.contract_wrappers import ERC20Token
|
||||||
to sell 0.1 WETH. To confirm his intent to sell and recieve the described
|
>>> erc20_wrapper = ERC20Token(ganache)
|
||||||
token amounts in this order, our maker must first sign the order by
|
|
||||||
creating a signature with the given order data.
|
|
||||||
|
|
||||||
>>> maker
|
>>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy
|
||||||
'0x5409ED021D9299bf6814279A6A1411A7e866A631'
|
|
||||||
|
|
||||||
>>> example_order = {
|
>>> tx = erc20_wrapper.approve(
|
||||||
... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
... zrx_address,
|
||||||
... 'takerAddress': '0x0000000000000000000000000000000000000000',
|
... erc20_proxy_addr,
|
||||||
... 'senderAddress': '0x0000000000000000000000000000000000000000',
|
... to_wei(100, 'ether'),
|
||||||
... 'exchangeAddress': '0x48bacb9266a570d521063ef5dd96e61686dbe788',
|
... tx_params=TxParams(from_=maker_address),
|
||||||
... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
... )
|
||||||
... 'makerAssetData': bytes.fromhex(
|
|
||||||
... 'f47261b0000000000000000000000000'
|
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
|
||||||
... 'takerAssetData': bytes.fromhex(
|
|
||||||
... 'f47261b0000000000000000000000000'
|
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
|
||||||
... 'salt': random.randint(1, 100000000000000000),
|
|
||||||
... 'makerFee': 0,
|
|
||||||
... 'takerFee': 0,
|
|
||||||
... 'makerAssetAmount': 100000000000000000,
|
|
||||||
... 'takerAssetAmount': 100000000000000000,
|
|
||||||
... 'expirationTimeSeconds': 999999999999999999999}
|
|
||||||
|
|
||||||
Please checkout our demo `here
|
>>> tx = erc20_wrapper.approve(
|
||||||
<http://0x-demos-py.s3-website-us-east-1.amazonaws.com/>`__
|
... weth_address,
|
||||||
if you would like to see how you can create an 0x order
|
... erc20_proxy_addr,
|
||||||
with our python packages.
|
... to_wei(100, 'ether'),
|
||||||
|
... tx_params=TxParams(from_=taker_address),
|
||||||
|
... )
|
||||||
|
|
||||||
To sign this order, we first need to generate the order hash.
|
Constructing an order
|
||||||
|
---------------------
|
||||||
|
|
||||||
>>> order_hash = generate_order_hash_hex(
|
>>> from zero_ex.order_utils import asset_data_utils, Order
|
||||||
... example_order, example_order["exchangeAddress"])
|
>>> from eth_utils import remove_0x_prefix
|
||||||
|
>>> from datetime import datetime, timedelta
|
||||||
|
>>> import random
|
||||||
|
>>> order = Order(
|
||||||
|
... makerAddress=maker_address,
|
||||||
|
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... makerAssetData=asset_data_utils.encode_erc20(zrx_address),
|
||||||
|
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||||
|
... salt=random.randint(1, 100000000000000000),
|
||||||
|
... makerFee=0,
|
||||||
|
... takerFee=0,
|
||||||
|
... makerAssetAmount=to_wei(0.1, 'ether'),
|
||||||
|
... takerAssetAmount=to_wei(0.1, 'ether'),
|
||||||
|
... expirationTimeSeconds=round(
|
||||||
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
|
... )
|
||||||
|
... )
|
||||||
|
|
||||||
Now our maker can sign this order hash with our web3 provider and
|
For this order to be valid, our Maker must sign a hash of it:
|
||||||
the `sign_hash` function from the order utils package.
|
|
||||||
|
|
||||||
|
>>> from zero_ex.order_utils import generate_order_hash_hex
|
||||||
|
>>> order_hash_hex = generate_order_hash_hex(order, exchange_address)
|
||||||
|
|
||||||
|
>>> from zero_ex.order_utils import sign_hash
|
||||||
>>> maker_signature = sign_hash(
|
>>> maker_signature = sign_hash(
|
||||||
... provider, to_checksum_address(maker), order_hash)
|
... ganache, Web3.toChecksumAddress(maker_address), order_hash_hex
|
||||||
|
... )
|
||||||
|
|
||||||
Now our maker can either deliver his signature and example order
|
Now our Maker can either deliver this order, along with his signature, directly
|
||||||
directly to the taker, or he can choose to broadcast the order
|
to the taker, or he can choose to broadcast the order to a 0x Relayer. For
|
||||||
with his signature to a 0x-relayer.
|
more information on working with Relayers, see `the documentation for
|
||||||
|
0x-sra-client <http://0x-sra-client-py.s3-website-us-east-1.amazonaws.com/>`_.
|
||||||
|
|
||||||
Filling an order
|
Filling an order
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
We finally have a valid order! We can now have our taker try
|
Now our Taker will fill the order. The `takerAssetAmount`:code: parameter
|
||||||
to fill the example order. The *takerAssetAmount* is simply the
|
specifies the amount of tokens (in this case WETH) that the taker wants to
|
||||||
amount of tokens (in our case WETH) the taker wants to fill.
|
fill. This example fills the order completely, but partial fills are possible
|
||||||
For this demonstration, we will be completely filling the order.
|
too.
|
||||||
Orders may also be partially filled.
|
|
||||||
|
|
||||||
Now let's fill the example order:
|
>>> from zero_ex.contract_wrappers import Exchange
|
||||||
|
>>> exchange_contract = Exchange(ganache)
|
||||||
>>> # Instantiate an instance of the exchange_wrapper with
|
>>> tx_hash = exchange_contract.fill_order(
|
||||||
>>> # the provider
|
... order=order,
|
||||||
>>> zero_ex_exchange = Exchange(provider)
|
... taker_amount=order["takerAssetAmount"],
|
||||||
>>> tx_hash = zero_ex_exchange.fill_order(
|
|
||||||
... order=example_order,
|
|
||||||
... taker_amount=example_order["takerAssetAmount"],
|
|
||||||
... signature=maker_signature,
|
... signature=maker_signature,
|
||||||
... tx_params=TxParams(from_=taker))
|
... tx_params=TxParams(from_=taker_address)
|
||||||
|
... )
|
||||||
|
|
||||||
Once the transaction is mined, we can get the details of
|
Once the transaction is mined, we can get the details of our exchange through
|
||||||
our exchange through the exchange wrapper.
|
the exchange wrapper:
|
||||||
|
|
||||||
>>> fill_event = zero_ex_exchange.get_fill_event(tx_hash)
|
>>> exchange_contract.get_fill_event(tx_hash)
|
||||||
>>> taker_filled_amount = fill_event[0].args.takerAssetFilledAmount
|
(AttributeDict({'args': ...({'makerAddress': ...}), 'event': 'Fill', ...}),)
|
||||||
>>> taker_filled_amount / 10 ** 18
|
>>> from pprint import pprint
|
||||||
0.1
|
>>> pprint(exchange_contract.get_fill_event(tx_hash)[0].args.__dict__)
|
||||||
|
{'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'makerAddress': '0x...',
|
||||||
|
'makerAssetData': b...,
|
||||||
|
'makerAssetFilledAmount': 100000000000000000,
|
||||||
|
'makerFeePaid': 0,
|
||||||
|
'orderHash': b...,
|
||||||
|
'senderAddress': '0x...',
|
||||||
|
'takerAddress': '0x...',
|
||||||
|
'takerAssetData': b...,
|
||||||
|
'takerAssetFilledAmount': 100000000000000000,
|
||||||
|
'takerFeePaid': 0}
|
||||||
|
>>> exchange_contract.get_fill_event(tx_hash)[0].args.takerAssetFilledAmount
|
||||||
|
100000000000000000
|
||||||
|
|
||||||
Cancelling an order
|
Cancelling an order
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Now we will show how to cancel an order if the maker no
|
A Maker can cancel an order that has yet to be filled.
|
||||||
long wishes to exchange his WETH tokens. We will use a second example
|
|
||||||
order to demonstrate.
|
|
||||||
|
|
||||||
>>> example_order_2 = {
|
>>> order = Order(
|
||||||
... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
... makerAddress=maker_address,
|
||||||
... 'takerAddress': '0x0000000000000000000000000000000000000000',
|
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
|
... exchangeAddress=exchange_address,
|
||||||
... 'senderAddress': '0x0000000000000000000000000000000000000000',
|
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'makerAssetData': bytes.fromhex(
|
... makerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||||
... 'f47261b0000000000000000000000000'
|
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
... salt=random.randint(1, 100000000000000000),
|
||||||
... 'takerAssetData': bytes.fromhex(
|
... makerFee=0,
|
||||||
... 'f47261b0000000000000000000000000'
|
... takerFee=0,
|
||||||
... 'e41d2489571d322189246dafa5ebde1f4699f498'),
|
... makerAssetAmount=1000000000000000000,
|
||||||
... 'salt': random.randint(1, 100000000000000000),
|
... takerAssetAmount=500000000000000000000,
|
||||||
... 'makerFee': 0,
|
... expirationTimeSeconds=round(
|
||||||
... 'takerFee': 0,
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
... 'makerAssetAmount': 1000000000000000000,
|
... )
|
||||||
... 'takerAssetAmount': 500000000000000000000,
|
... )
|
||||||
... 'expirationTimeSeconds': 999999999999999999999}
|
|
||||||
>>> tx_hash = zero_ex_exchange.cancel_order(
|
|
||||||
... order=example_order_2, tx_params=TxParams(from_=maker))
|
|
||||||
|
|
||||||
Once the transaction is mined, we can get the details of
|
>>> tx_hash = exchange_contract.cancel_order(
|
||||||
our cancellation through the exchange wrapper.
|
... order=order, tx_params=TxParams(from_=maker_address)
|
||||||
|
... )
|
||||||
|
|
||||||
>>> cancel_event = zero_ex_exchange.get_cancel_event(tx_hash);
|
Once the transaction is mined, we can get the details of the cancellation
|
||||||
>>> cancelled_order_hash = cancel_event[0].args.orderHash.hex()
|
through the Exchange wrapper:
|
||||||
|
|
||||||
|
>>> exchange_contract.get_cancel_event(tx_hash)
|
||||||
|
(AttributeDict({'args': ...({'makerAddress': ...}), 'event': 'Cancel', ...}),)
|
||||||
|
>>> pprint(exchange_contract.get_cancel_event(tx_hash)[0].args.__dict__)
|
||||||
|
{'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'makerAddress': '0x...',
|
||||||
|
'makerAssetData': b...,
|
||||||
|
'orderHash': b...,
|
||||||
|
'senderAddress': '0x...',
|
||||||
|
'takerAssetData': b...}
|
||||||
|
>>> exchange_contract.get_cancel_event(tx_hash)[0].args.feeRecipientAddress
|
||||||
|
'0x0000000000000000000000000000000000000000'
|
||||||
|
|
||||||
Batching orders
|
Batching orders
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The 0x exchange contract can also process multiple orders at
|
The Exchange contract can also process multiple orders at the same time. Here
|
||||||
the same time. Here is an example where the taker fills
|
is an example where the taker fills two orders in one transaction:
|
||||||
two orders in one transaction.
|
|
||||||
|
|
||||||
>>> order_1 = {
|
>>> order_1 = Order(
|
||||||
... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
... makerAddress=maker_address,
|
||||||
... 'takerAddress': '0x0000000000000000000000000000000000000000',
|
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'senderAddress': '0x0000000000000000000000000000000000000000',
|
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'makerAssetData': bytes.fromhex(
|
... makerAssetData=asset_data_utils.encode_erc20(zrx_address),
|
||||||
... 'f47261b0000000000000000000000000'
|
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
... salt=random.randint(1, 100000000000000000),
|
||||||
... 'takerAssetData': bytes.fromhex(
|
... makerFee=0,
|
||||||
... 'f47261b0000000000000000000000000'
|
... takerFee=0,
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
... makerAssetAmount=100,
|
||||||
... 'salt': random.randint(1, 100000000000000000),
|
... takerAssetAmount=100,
|
||||||
... 'makerFee': 0,
|
... expirationTimeSeconds=round(
|
||||||
... 'takerFee': 0,
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
... 'makerAssetAmount': 100,
|
... )
|
||||||
... 'takerAssetAmount': 100,
|
... )
|
||||||
... 'expirationTimeSeconds': 1000000000000000000}
|
|
||||||
>>> order_hash_1 = generate_order_hash_hex(
|
|
||||||
... order_1, zero_ex_exchange.address)
|
|
||||||
>>> signature_1 = sign_hash(
|
>>> signature_1 = sign_hash(
|
||||||
... provider, to_checksum_address(maker), order_hash_1)
|
... ganache,
|
||||||
>>> order_2 = {
|
... Web3.toChecksumAddress(maker_address),
|
||||||
... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
... generate_order_hash_hex(order_1, exchange_contract.address)
|
||||||
... 'takerAddress': '0x0000000000000000000000000000000000000000',
|
... )
|
||||||
... 'senderAddress': '0x0000000000000000000000000000000000000000',
|
>>> order_2 = Order(
|
||||||
... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
... makerAddress=maker_address,
|
||||||
... 'makerAssetData': bytes.fromhex(
|
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'f47261b0000000000000000000000000'
|
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||||
... 'takerAssetData': bytes.fromhex(
|
... makerAssetData=asset_data_utils.encode_erc20(zrx_address),
|
||||||
... 'f47261b0000000000000000000000000'
|
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||||
... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
... salt=random.randint(1, 100000000000000000),
|
||||||
... 'salt': random.randint(1, 100000000000000000),
|
... makerFee=0,
|
||||||
... 'makerFee': 0,
|
... takerFee=0,
|
||||||
... 'takerFee': 0,
|
... makerAssetAmount=200,
|
||||||
... 'makerAssetAmount': 200,
|
... takerAssetAmount=200,
|
||||||
... 'takerAssetAmount': 200,
|
... expirationTimeSeconds=round(
|
||||||
... 'expirationTimeSeconds': 2000000000000000000}
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
>>> order_hash_2 = generate_order_hash_hex(
|
... )
|
||||||
... order_2, zero_ex_exchange.address)
|
... )
|
||||||
>>> signature_2 = sign_hash(
|
>>> signature_2 = sign_hash(
|
||||||
... provider, to_checksum_address(maker), order_hash_2)
|
... ganache,
|
||||||
|
... Web3.toChecksumAddress(maker_address),
|
||||||
|
... generate_order_hash_hex(order_2, exchange_contract.address)
|
||||||
|
... )
|
||||||
|
|
||||||
Fill order_1 and order_2 together:
|
Fill order_1 and order_2 together:
|
||||||
|
|
||||||
>>> tx_hash = zero_ex_exchange.batch_fill_orders(
|
>>> exchange_contract.batch_fill_orders(
|
||||||
... orders=[order_1, order_2],
|
... orders=[order_1, order_2],
|
||||||
... taker_amounts=[1, 2],
|
... taker_amounts=[1, 2],
|
||||||
... signatures=[signature_1, signature_2],
|
... signatures=[signature_1, signature_2],
|
||||||
... tx_params=TxParams(from_=taker))
|
... tx_params=TxParams(from_=taker_address))
|
||||||
|
HexBytes('0x...')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .tx_params import TxParams
|
from .tx_params import TxParams
|
||||||
|
@ -95,6 +95,7 @@ class BaseContractWrapper:
|
|||||||
)
|
)
|
||||||
return self._web3_eth.sendRawTransaction(signed_tx.rawTransaction)
|
return self._web3_eth.sendRawTransaction(signed_tx.rawTransaction)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def execute_method(
|
def execute_method(
|
||||||
self,
|
self,
|
||||||
address: str,
|
address: str,
|
||||||
|
@ -13,12 +13,7 @@ from .tx_params import TxParams
|
|||||||
|
|
||||||
|
|
||||||
class ERC20Token(BaseContractWrapper):
|
class ERC20Token(BaseContractWrapper):
|
||||||
"""Wrapper class for Ethereum ERC20 smart contract.
|
"""Wrapper class for Ethereum ERC20 smart contract."""
|
||||||
|
|
||||||
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
|
||||||
:param account_address: default None, str of account address
|
|
||||||
:param private_key: default None, str of private_key
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -26,7 +21,10 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
account_address: str = None,
|
account_address: str = None,
|
||||||
private_key: str = None,
|
private_key: str = None,
|
||||||
):
|
):
|
||||||
"""Get an instance of wrapper for ERC20 smart contract."""
|
"""Get an instance of wrapper for ERC20 smart contract.
|
||||||
|
|
||||||
|
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
||||||
|
"""
|
||||||
super(ERC20Token, self).__init__(
|
super(ERC20Token, self).__init__(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
account_address=account_address,
|
account_address=account_address,
|
||||||
@ -36,14 +34,13 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
def _erc20(self, token_address):
|
def _erc20(self, token_address):
|
||||||
"""Get an instance of the ERC20 smart contract at a specific address.
|
"""Get an instance of the ERC20 smart contract at a specific address.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
|
||||||
|
|
||||||
:returns: ERC20 contract object
|
:returns: ERC20 contract object
|
||||||
"""
|
"""
|
||||||
return self._contract_instance(
|
return self._contract_instance(
|
||||||
address=token_address, abi=abi_by_name("ERC20Token")
|
address=token_address, abi=abi_by_name("ERC20Token")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def transfer(
|
def transfer(
|
||||||
self,
|
self,
|
||||||
token_address: str,
|
token_address: str,
|
||||||
@ -54,12 +51,9 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
) -> Union[HexBytes, bytes]:
|
) -> Union[HexBytes, bytes]:
|
||||||
"""Transfer the balance from owner's account to another account.
|
"""Transfer the balance from owner's account to another account.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:param value: integer amount to send
|
||||||
:param to_address: string address of receiver
|
:param tx_params: transaction parameters
|
||||||
:param value: integer amount to send in Wei base unit
|
:param view_only: whether to use transact() or call()
|
||||||
:param tx_params: default None, dict of transaction options
|
|
||||||
:param view_only: default False, boolean of whether to transact or
|
|
||||||
view only
|
|
||||||
|
|
||||||
:returns: transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
@ -72,6 +66,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
func=func, tx_params=tx_params, view_only=view_only
|
func=func, tx_params=tx_params, view_only=view_only
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def approve(
|
def approve(
|
||||||
self,
|
self,
|
||||||
token_address: str,
|
token_address: str,
|
||||||
@ -80,14 +75,11 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
tx_params: Optional[TxParams] = None,
|
tx_params: Optional[TxParams] = None,
|
||||||
view_only: bool = False,
|
view_only: bool = False,
|
||||||
) -> Union[HexBytes, bytes]:
|
) -> Union[HexBytes, bytes]:
|
||||||
"""Approve a `spender_address` to spend up to `value` your account.
|
"""Approve an address to spend up to `value`:code: of your tokens.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:param value: amount of allowance
|
||||||
:param spender_address: string address of receiver
|
:param tx_params: transaction options
|
||||||
:param value: integer amount of allowance in Wei base unit
|
:param view_only: whether to use transact() or call()
|
||||||
:param tx_params: default None, dict of transaction options
|
|
||||||
:param view_only: default False, boolean of whether to transact or
|
|
||||||
view only
|
|
||||||
|
|
||||||
:returns: transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
@ -102,6 +94,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
func=func, tx_params=tx_params, view_only=view_only
|
func=func, tx_params=tx_params, view_only=view_only
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def transfer_from(
|
def transfer_from(
|
||||||
self,
|
self,
|
||||||
token_address: str,
|
token_address: str,
|
||||||
@ -111,19 +104,16 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
tx_params: Optional[TxParams] = None,
|
tx_params: Optional[TxParams] = None,
|
||||||
view_only: bool = False,
|
view_only: bool = False,
|
||||||
) -> Union[HexBytes, bytes]:
|
) -> Union[HexBytes, bytes]:
|
||||||
"""Transfer tokens from `authorized_address` to another address.
|
"""Transfer tokens from `authorized_address`:code: to another address.
|
||||||
|
|
||||||
Note that the `authorized_address` must have called with `approve`
|
Note that the `authorized_address`:code: must have already called
|
||||||
with your address as the `spender_address`.
|
`approve`:code: for the `spender_address`:code:.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:param authorized_address: address you have been authorized to transfer
|
||||||
:param authorized_address: string address you have been authorized to
|
tokens from
|
||||||
to transfer tokens from
|
:param value: amount to send
|
||||||
:param to_address: string address of receiver
|
:param tx_params: transaction parameters
|
||||||
:param value: integer amount to send in Wei base unit
|
:param view_only: whether to use transact() or call()
|
||||||
:param tx_params: default None, dict of transaction options
|
|
||||||
:param view_only: default False, boolean of whether to transact or
|
|
||||||
view only
|
|
||||||
|
|
||||||
:returns: transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
@ -144,9 +134,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
def total_supply(self, token_address: str) -> int:
|
def total_supply(self, token_address: str) -> int:
|
||||||
"""Get total supply of a given ERC20 Token.
|
"""Get total supply of a given ERC20 Token.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:returns: amount of tokens
|
||||||
|
|
||||||
:returns: integer amount of tokens in Wei
|
|
||||||
"""
|
"""
|
||||||
token_address = self._validate_and_checksum_address(token_address)
|
token_address = self._validate_and_checksum_address(token_address)
|
||||||
func = self._erc20(token_address).functions.totalSupply()
|
func = self._erc20(token_address).functions.totalSupply()
|
||||||
@ -157,10 +145,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
def balance_of(self, token_address: str, owner_address: str) -> int:
|
def balance_of(self, token_address: str, owner_address: str) -> int:
|
||||||
"""Get token balance of a given owner address.
|
"""Get token balance of a given owner address.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:returns: amount of tokens
|
||||||
:param owner_address: string address of owner to check balance for
|
|
||||||
|
|
||||||
:returns: integer amount of tokens in Wei the owner has
|
|
||||||
"""
|
"""
|
||||||
token_address = self._validate_and_checksum_address(token_address)
|
token_address = self._validate_and_checksum_address(token_address)
|
||||||
owner_address = self._validate_and_checksum_address(owner_address)
|
owner_address = self._validate_and_checksum_address(owner_address)
|
||||||
@ -174,12 +159,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
) -> Union[HexBytes, bytes]:
|
) -> Union[HexBytes, bytes]:
|
||||||
"""Get the amount of tokens approved for a spender.
|
"""Get the amount of tokens approved for a spender.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:returns: amount of tokens
|
||||||
:param owner_address: string address of owner of the tokens
|
|
||||||
:param spender_address: string address of spender to be checked
|
|
||||||
|
|
||||||
:returns: integer amount of tokens in Wei spender is authorized
|
|
||||||
to spend
|
|
||||||
"""
|
"""
|
||||||
token_address = self._validate_and_checksum_address(token_address)
|
token_address = self._validate_and_checksum_address(token_address)
|
||||||
owner_address = self._validate_and_checksum_address(owner_address)
|
owner_address = self._validate_and_checksum_address(owner_address)
|
||||||
@ -196,8 +176,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
) -> Tuple[AttributeDict]:
|
) -> Tuple[AttributeDict]:
|
||||||
"""Get the result of a transfer from its transaction hash.
|
"""Get the result of a transfer from its transaction hash.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:param tx_hash: hash of transfer transaction
|
||||||
:param tx_hash: `HexBytes` hash of transfer transaction
|
|
||||||
"""
|
"""
|
||||||
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
|
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
|
||||||
token_address = self._validate_and_checksum_address(token_address)
|
token_address = self._validate_and_checksum_address(token_address)
|
||||||
@ -212,8 +191,7 @@ class ERC20Token(BaseContractWrapper):
|
|||||||
) -> Tuple[AttributeDict]:
|
) -> Tuple[AttributeDict]:
|
||||||
"""Get the result of an approval event from its transaction hash.
|
"""Get the result of an approval event from its transaction hash.
|
||||||
|
|
||||||
:param token_address: string address of token smart contract
|
:param tx_hash: hash of approval transaction
|
||||||
:param tx_hash: `HexBytes` hash of approval transaction
|
|
||||||
"""
|
"""
|
||||||
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
|
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
|
||||||
token_address = self._validate_and_checksum_address(token_address)
|
token_address = self._validate_and_checksum_address(token_address)
|
||||||
|
@ -27,12 +27,7 @@ class CancelDisallowedError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class Exchange(BaseContractWrapper):
|
class Exchange(BaseContractWrapper):
|
||||||
"""Wrapper class for 0x Exchange smart contract.
|
"""Wrapper class for 0x Exchange smart contract."""
|
||||||
|
|
||||||
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
|
||||||
:param account_address: default None, str of account address
|
|
||||||
:param private_key: default None, str of private_key
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -40,7 +35,12 @@ class Exchange(BaseContractWrapper):
|
|||||||
account_address: str = None,
|
account_address: str = None,
|
||||||
private_key: str = None,
|
private_key: str = None,
|
||||||
):
|
):
|
||||||
"""Get an instance of the 0x Exchange smart contract wrapper."""
|
"""Get an instance of the 0x Exchange smart contract wrapper.
|
||||||
|
|
||||||
|
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
||||||
|
:param account_address: str of account address
|
||||||
|
:param private_key: str of private_key
|
||||||
|
"""
|
||||||
super(Exchange, self).__init__(
|
super(Exchange, self).__init__(
|
||||||
provider=provider,
|
provider=provider,
|
||||||
account_address=account_address,
|
account_address=account_address,
|
||||||
@ -54,6 +54,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
address=self.address, abi=abi_by_name("Exchange")
|
address=self.address, abi=abi_by_name("Exchange")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def fill_order(
|
def fill_order(
|
||||||
self,
|
self,
|
||||||
order: Order,
|
order: Order,
|
||||||
@ -82,7 +83,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
:param view_only: default False, boolean of whether to transact or
|
:param view_only: default False, boolean of whether to transact or
|
||||||
view only
|
view only
|
||||||
|
|
||||||
:returns: `HexBytes` transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
|
assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
|
||||||
is_valid_signature(
|
is_valid_signature(
|
||||||
@ -101,6 +102,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
func=func, tx_params=tx_params, view_only=view_only
|
func=func, tx_params=tx_params, view_only=view_only
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def batch_fill_orders(
|
def batch_fill_orders(
|
||||||
self,
|
self,
|
||||||
orders: List[Order],
|
orders: List[Order],
|
||||||
@ -118,7 +120,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
:param view_only: default False, boolean of whether to transact or
|
:param view_only: default False, boolean of whether to transact or
|
||||||
view only
|
view only
|
||||||
|
|
||||||
:returns: `HexBytes` transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
order_jsdicts = [
|
order_jsdicts = [
|
||||||
order_to_jsdict(order, self.address) for order in orders
|
order_to_jsdict(order, self.address) for order in orders
|
||||||
@ -139,6 +141,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
func=func, tx_params=tx_params, view_only=view_only
|
func=func, tx_params=tx_params, view_only=view_only
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def fill_or_kill_order(
|
def fill_or_kill_order(
|
||||||
self,
|
self,
|
||||||
order: Order,
|
order: Order,
|
||||||
@ -156,7 +159,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
:param view_only: default False, boolean of whether to transact or
|
:param view_only: default False, boolean of whether to transact or
|
||||||
view only
|
view only
|
||||||
|
|
||||||
:returns: `HexBytes` transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
|
assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
|
||||||
is_valid_signature(
|
is_valid_signature(
|
||||||
@ -175,6 +178,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
func=func, tx_params=tx_params, view_only=view_only
|
func=func, tx_params=tx_params, view_only=view_only
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# pylint: disable=too-many-arguments
|
||||||
def batch_fill_or_kill_orders(
|
def batch_fill_or_kill_orders(
|
||||||
self,
|
self,
|
||||||
orders: List[Order],
|
orders: List[Order],
|
||||||
@ -192,7 +196,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
:param view_only: default False, boolean of whether to transact or
|
:param view_only: default False, boolean of whether to transact or
|
||||||
view only
|
view only
|
||||||
|
|
||||||
:returns: `HexBytes` transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
order_jsdicts = [
|
order_jsdicts = [
|
||||||
order_to_jsdict(order, self.address) for order in orders
|
order_to_jsdict(order, self.address) for order in orders
|
||||||
@ -230,7 +234,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
:param view_only: default False, boolean of whether to transact or
|
:param view_only: default False, boolean of whether to transact or
|
||||||
view only
|
view only
|
||||||
|
|
||||||
:returns: `HexBytes` transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
|
assert_valid(order_to_jsdict(order, self.address), "/orderSchema")
|
||||||
maker_address = self._validate_and_checksum_address(
|
maker_address = self._validate_and_checksum_address(
|
||||||
@ -264,7 +268,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
:param view_only: default False, boolean of whether to transact or
|
:param view_only: default False, boolean of whether to transact or
|
||||||
view only
|
view only
|
||||||
|
|
||||||
:returns: `HexBytes` transaction hash
|
:returns: transaction hash
|
||||||
"""
|
"""
|
||||||
order_jsdicts = [
|
order_jsdicts = [
|
||||||
order_to_jsdict(order, self.address) for order in orders
|
order_to_jsdict(order, self.address) for order in orders
|
||||||
@ -298,7 +302,7 @@ class Exchange(BaseContractWrapper):
|
|||||||
|
|
||||||
:param tx_hash: `HexBytes` hash of fill transaction
|
:param tx_hash: `HexBytes` hash of fill transaction
|
||||||
|
|
||||||
:returns: tuple of `FillResults`.
|
:returns: fill event
|
||||||
"""
|
"""
|
||||||
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
|
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
|
||||||
return self._exchange.events.Fill().processReceipt(tx_receipt)
|
return self._exchange.events.Fill().processReceipt(tx_receipt)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Fixtures for pytest."""
|
"""Fixtures for pytest."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from eth_utils import remove_0x_prefix, to_checksum_address
|
from eth_utils import to_checksum_address
|
||||||
from web3 import Web3
|
from web3 import Web3
|
||||||
|
|
||||||
from zero_ex.order_utils import asset_data_utils
|
from zero_ex.order_utils import asset_data_utils
|
||||||
@ -48,11 +48,7 @@ def weth_address():
|
|||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def weth_asset_data(weth_address): # pylint: disable=redefined-outer-name
|
def weth_asset_data(weth_address): # pylint: disable=redefined-outer-name
|
||||||
"""Get 0x asset data for Wrapped Ether (WETH) token."""
|
"""Get 0x asset data for Wrapped Ether (WETH) token."""
|
||||||
return bytes.fromhex(
|
return asset_data_utils.encode_erc20(weth_address)
|
||||||
remove_0x_prefix(
|
|
||||||
asset_data_utils.encode_erc20_asset_data(weth_address)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
@ -74,6 +70,4 @@ def zrx_address():
|
|||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def zrx_asset_data(zrx_address): # pylint: disable=redefined-outer-name
|
def zrx_asset_data(zrx_address): # pylint: disable=redefined-outer-name
|
||||||
"""Get 0x asset data for ZRX token."""
|
"""Get 0x asset data for ZRX token."""
|
||||||
return bytes.fromhex(
|
return asset_data_utils.encode_erc20(zrx_address)
|
||||||
remove_0x_prefix(asset_data_utils.encode_erc20_asset_data(zrx_address))
|
|
||||||
)
|
|
||||||
|
@ -159,6 +159,7 @@ setup(
|
|||||||
"pylint",
|
"pylint",
|
||||||
"pytest",
|
"pytest",
|
||||||
"sphinx",
|
"sphinx",
|
||||||
|
"sphinx-autodoc-typehints",
|
||||||
"tox",
|
"tox",
|
||||||
"twine",
|
"twine",
|
||||||
]
|
]
|
||||||
|
@ -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"]
|
||||||
|
@ -1,30 +1,12 @@
|
|||||||
"""JSON schemas and associated utilities.
|
"""0x JSON schemas and associated utilities.
|
||||||
|
|
||||||
Validating a 0x Order
|
Setup
|
||||||
---------------------
|
-----
|
||||||
|
|
||||||
Here is an example on how to validate a 0x order.
|
Install the package with pip::
|
||||||
|
|
||||||
|
pip install 0x-json-schemas
|
||||||
|
|
||||||
>>> 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
|
||||||
@ -77,13 +59,37 @@ def assert_valid(data: Mapping, schema_id: str) -> None:
|
|||||||
|
|
||||||
Raises an exception if validation fails.
|
Raises an exception if validation fails.
|
||||||
|
|
||||||
>>> assert_valid(
|
>>> from zero_ex.json_schemas import assert_valid
|
||||||
... {'v': 27, 'r': '0x'+'f'*64, 's': '0x'+'f'*64},
|
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||||
... '/ecSignatureSchema',
|
>>> from zero_ex.order_utils import (
|
||||||
|
... asset_data_utils, Order, order_to_jsdict
|
||||||
... )
|
... )
|
||||||
|
>>> from eth_utils import remove_0x_prefix
|
||||||
|
>>> import random
|
||||||
|
>>> from datetime import datetime, timedelta
|
||||||
|
>>> example_order = Order(
|
||||||
|
... makerAddress='0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
||||||
|
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... exchangeAddress='0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
|
||||||
|
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... makerAssetData=asset_data_utils.encode_erc20(
|
||||||
|
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token
|
||||||
|
... ),
|
||||||
|
... takerAssetData=asset_data_utils.encode_erc20(
|
||||||
|
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token
|
||||||
|
... ),
|
||||||
|
... salt=random.randint(1, 100000000000000000),
|
||||||
|
... makerFee=0,
|
||||||
|
... takerFee=0,
|
||||||
|
... makerAssetAmount=1000000000000000000,
|
||||||
|
... takerAssetAmount=500000000000000000000,
|
||||||
|
... expirationTimeSeconds=round(
|
||||||
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
|
... )
|
||||||
|
... )
|
||||||
|
>>> assert_valid(order_to_jsdict(example_order), "/orderSchema")
|
||||||
"""
|
"""
|
||||||
# noqa
|
|
||||||
|
|
||||||
_, schema = _LOCAL_RESOLVER.resolve(schema_id)
|
_, schema = _LOCAL_RESOLVER.resolve(schema_id)
|
||||||
jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER)
|
jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER)
|
||||||
|
|
||||||
@ -99,7 +105,7 @@ def assert_valid_json(data: str, schema_id: str) -> None:
|
|||||||
Raises an exception if validation fails.
|
Raises an exception if validation fails.
|
||||||
|
|
||||||
>>> assert_valid_json(
|
>>> assert_valid_json(
|
||||||
... r'''{
|
... '''{
|
||||||
... "v": 27,
|
... "v": 27,
|
||||||
... "r": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
... "r": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
... "s": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
... "s": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
13
python-packages/middlewares/.discharge.json
Normal file
13
python-packages/middlewares/.discharge.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"domain": "0x-middlewares-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 +1,10 @@
|
|||||||
"""Web3 middlewares for 0x applications."""
|
"""Web3 middlewares for 0x applications.
|
||||||
|
|
||||||
|
Setup
|
||||||
|
-----
|
||||||
|
|
||||||
|
Install the package with pip::
|
||||||
|
|
||||||
|
pip install 0x-middlewares
|
||||||
|
|
||||||
|
"""
|
||||||
|
@ -60,22 +60,20 @@ def construct_local_message_signer(
|
|||||||
list, or set of private keys. Keys can be any of the following
|
list, or set of private keys. Keys can be any of the following
|
||||||
formats:
|
formats:
|
||||||
|
|
||||||
- An `eth_account.LocalAccount` object
|
- An `eth_account.LocalAccount`:code: object
|
||||||
- An `eth_keys.PrivateKey` object
|
- An `eth_keys.PrivateKey`:code: object
|
||||||
- A raw private key as a hex `string` or `bytes`
|
- A raw private key as a hex `string`:code: or as `bytes`:code:
|
||||||
:returns: callable local_message_signer_middleware
|
:returns: callable local_message_signer_middleware
|
||||||
|
|
||||||
:Example:
|
>>> private_key=(
|
||||||
|
... "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d"
|
||||||
>>> from web3 import Web3
|
... )
|
||||||
>>> from zero_ex.middlewares.local_message_signer import (
|
>>> from web3 import Web3, HTTPProvider
|
||||||
... construct_local_message_signer)
|
>>> Web3(
|
||||||
>>> WEB3_RPC_URL="https://mainnet.infura.io/v3/INFURA_API_KEY"
|
... HTTPProvider("https://mainnet.infura.io/v3/API_KEY")
|
||||||
>>> PRIVATE_KEY=(
|
... ).middleware_stack.add(
|
||||||
... "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d")
|
... construct_local_message_signer(private_key)
|
||||||
>>> web3_instance = Web3.HTTPProvider(WEB3_RPC_URL)
|
... )
|
||||||
>>> web3_instance.middlewares.add(
|
|
||||||
... construct_local_message_signer(PRIVATE_KEY))
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(private_key_or_account, (list, tuple, set)):
|
if not isinstance(private_key_or_account, (list, tuple, set)):
|
||||||
|
@ -4,5 +4,7 @@ from web3.utils import datatypes
|
|||||||
from web3.providers.base import BaseProvider
|
from web3.providers.base import BaseProvider
|
||||||
|
|
||||||
class Web3:
|
class Web3:
|
||||||
class HTTPProvider(BaseProvider): ...
|
|
||||||
def __init__(self, provider: BaseProvider) -> None: ...
|
def __init__(self, provider: BaseProvider) -> None: ...
|
||||||
|
|
||||||
|
class HTTPProvider(BaseProvider):
|
||||||
|
...
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""Tests of 0x.middlewares.local_message_signer."""
|
"""Tests of 0x.middlewares.local_message_signer."""
|
||||||
|
|
||||||
from eth_utils import to_checksum_address
|
from eth_utils import to_checksum_address
|
||||||
from web3 import Web3
|
from web3 import Web3, HTTPProvider
|
||||||
|
|
||||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||||
from zero_ex.middlewares.local_message_signer import (
|
from zero_ex.middlewares.local_message_signer import (
|
||||||
construct_local_message_signer,
|
construct_local_message_signer,
|
||||||
@ -25,16 +26,14 @@ def test_local_message_signer__sign_order():
|
|||||||
private_key = (
|
private_key = (
|
||||||
"f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d"
|
"f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d"
|
||||||
)
|
)
|
||||||
web3_rpc_url = "http://127.0.0.1:8545"
|
ganache = HTTPProvider("http://127.0.0.1:8545")
|
||||||
web3_instance = Web3.HTTPProvider(web3_rpc_url)
|
web3_instance = Web3(ganache)
|
||||||
web3_instance.middlewares.add(construct_local_message_signer(private_key))
|
web3_instance.middleware_stack.add(
|
||||||
|
construct_local_message_signer(private_key)
|
||||||
|
)
|
||||||
order = make_empty_order()
|
order = make_empty_order()
|
||||||
order_hash = generate_order_hash_hex(order, exchange)
|
order_hash = generate_order_hash_hex(order, exchange)
|
||||||
signature = sign_hash(
|
signature = sign_hash(ganache, to_checksum_address(address), order_hash)
|
||||||
web3_instance, to_checksum_address(address), order_hash
|
|
||||||
)
|
|
||||||
assert signature == expected_signature
|
assert signature == expected_signature
|
||||||
is_valid = is_valid_signature(
|
is_valid = is_valid_signature(ganache, order_hash, signature, address)[0]
|
||||||
web3_instance, order_hash, signature, address
|
|
||||||
)[0]
|
|
||||||
assert is_valid is True
|
assert is_valid is True
|
||||||
|
@ -181,6 +181,7 @@ setup(
|
|||||||
"black",
|
"black",
|
||||||
"coverage",
|
"coverage",
|
||||||
"coveralls",
|
"coveralls",
|
||||||
|
"deprecated",
|
||||||
"mypy",
|
"mypy",
|
||||||
"mypy_extensions",
|
"mypy_extensions",
|
||||||
"pycodestyle",
|
"pycodestyle",
|
||||||
|
@ -21,12 +21,14 @@ zero_ex.order_utils.asset_data_utils
|
|||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData
|
.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData
|
||||||
|
:members:
|
||||||
See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome.
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData
|
.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData
|
||||||
|
:members:
|
||||||
See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome.
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
@ -10,8 +10,8 @@ from typing import Any, List
|
|||||||
|
|
||||||
from mypy_extensions import TypedDict
|
from mypy_extensions import TypedDict
|
||||||
|
|
||||||
from web3 import Web3
|
|
||||||
from eth_abi import encode_abi
|
from eth_abi import encode_abi
|
||||||
|
from web3 import Web3
|
||||||
|
|
||||||
from .type_assertions import assert_is_string, assert_is_list
|
from .type_assertions import assert_is_string, assert_is_list
|
||||||
|
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
"""Order utilities for 0x applications.
|
"""Order utilities for 0x applications.
|
||||||
|
|
||||||
|
Setup
|
||||||
|
-----
|
||||||
|
|
||||||
|
Install the package with pip::
|
||||||
|
|
||||||
|
pip install 0x-order-utils
|
||||||
|
|
||||||
Some methods require the caller to pass in a `Web3.BaseProvider`:code: object.
|
Some methods require the caller to pass in a `Web3.BaseProvider`:code: object.
|
||||||
For local testing one may construct such a provider pointing at an instance of
|
For local testing one may construct such a provider pointing at an instance of
|
||||||
`ganache-cli <https://www.npmjs.com/package/ganache-cli>`_ which has the 0x
|
`ganache-cli <https://www.npmjs.com/package/ganache-cli>`_ which has the 0x
|
||||||
@ -7,52 +14,51 @@ 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
|
Constructing an order
|
||||||
--------------------
|
---------------------
|
||||||
|
|
||||||
Here is a short demonstration on how to create 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.contract_addresses import (
|
|
||||||
... NETWORK_TO_ADDRESSES, NetworkId)
|
|
||||||
>>> from zero_ex.order_utils import asset_data_utils, Order
|
>>> from zero_ex.order_utils import asset_data_utils, Order
|
||||||
>>> NULL_ADDRESS = "0x0000000000000000000000000000000000000000"
|
>>> from datetime import datetime, timedelta
|
||||||
|
>>> import random
|
||||||
>>> my_address = "0x5409ed021d9299bf6814279a6a1411a7e866a631"
|
>>> my_address = "0x5409ed021d9299bf6814279a6a1411a7e866a631"
|
||||||
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange
|
>>> example_order = Order(
|
||||||
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token
|
... makerAddress=my_address,
|
||||||
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token
|
... takerAddress="0x0000000000000000000000000000000000000000",
|
||||||
>>> maker_asset_data = (
|
... exchangeAddress=NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange,
|
||||||
... asset_data_utils.encode_erc20_asset_data(weth_address))
|
... senderAddress="0x0000000000000000000000000000000000000000",
|
||||||
>>> taker_asset_data = (
|
... feeRecipientAddress="0x0000000000000000000000000000000000000000",
|
||||||
... asset_data_utils.encode_erc20_asset_data(zrx_address))
|
... makerAssetData=asset_data_utils.encode_erc20(
|
||||||
>>> example_order: Order = {
|
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token
|
||||||
... "makerAddress": my_address,
|
... ),
|
||||||
... "takerAddress": NULL_ADDRESS,
|
... takerAssetData=asset_data_utils.encode_erc20(
|
||||||
... "exchangeAddress": exchange_address,
|
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token
|
||||||
... "senderAddress": NULL_ADDRESS,
|
... ),
|
||||||
... "feeRecipientAddress": NULL_ADDRESS,
|
... salt=random.randint(1, 100000000000000000),
|
||||||
... "makerAssetData": maker_asset_data,
|
... makerFee=0,
|
||||||
... "takerAssetData": taker_asset_data,
|
... takerFee=0,
|
||||||
... "salt": 123456789,
|
... makerAssetAmount=1 * 10 ** 18, # Convert token amount to base unit with 18 decimals
|
||||||
... "makerFee": 0,
|
... takerAssetAmount=500 * 10 ** 18, # Convert token amount to base unit with 18 decimals
|
||||||
... "takerFee": 0,
|
... expirationTimeSeconds=round(
|
||||||
... "makerAssetAmount": 1 * 10 ** 18, # Converting token amount to base unit with 18 decimals
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
... "takerAssetAmount": 500 * 10 ** 18, # Converting token amount to base unit with 18 decimals
|
... )
|
||||||
... "expirationTimeSeconds": 1553553429,
|
... )
|
||||||
... }
|
>>> import pprint
|
||||||
>>> pprint.pprint(example_order)
|
>>> pprint.pprint(example_order)
|
||||||
{'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
|
{'exchangeAddress': '0x...',
|
||||||
'expirationTimeSeconds': 1553553429,
|
'expirationTimeSeconds': ...,
|
||||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
'makerAddress': '0x...',
|
||||||
'makerAssetAmount': 1000000000000000000,
|
'makerAssetAmount': 1000000000000000000,
|
||||||
'makerAssetData': '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
'makerAssetData': b...,
|
||||||
'makerFee': 0,
|
'makerFee': 0,
|
||||||
'salt': 123456789,
|
'salt': ...,
|
||||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'takerAssetAmount': 500000000000000000000,
|
'takerAssetAmount': 500000000000000000000,
|
||||||
'takerAssetData': '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498',
|
'takerAssetData': b...,
|
||||||
'takerFee': 0}
|
'takerFee': 0}
|
||||||
""" # noqa E501
|
""" # noqa E501
|
||||||
|
|
||||||
@ -128,7 +134,29 @@ class _Constants:
|
|||||||
|
|
||||||
|
|
||||||
class Order(TypedDict): # pylint: disable=too-many-instance-attributes
|
class Order(TypedDict): # pylint: disable=too-many-instance-attributes
|
||||||
"""A Web3-compatible representation of the Exchange.Order struct."""
|
"""A Web3-compatible representation of the Exchange.Order struct.
|
||||||
|
|
||||||
|
>>> from zero_ex.order_utils import asset_data_utils
|
||||||
|
>>> from eth_utils import remove_0x_prefix
|
||||||
|
>>> from datetime import datetime, timedelta
|
||||||
|
>>> import random
|
||||||
|
>>> order = Order(
|
||||||
|
... makerAddress=maker_address,
|
||||||
|
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||||
|
... makerAssetData=asset_data_utils.encode_erc20(zrx_address),
|
||||||
|
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||||
|
... salt=random.randint(1, 100000000000000000),
|
||||||
|
... makerFee=0,
|
||||||
|
... takerFee=0,
|
||||||
|
... makerAssetAmount=1,
|
||||||
|
... takerAssetAmount=1,
|
||||||
|
... expirationTimeSeconds=round(
|
||||||
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
|
... )
|
||||||
|
... )
|
||||||
|
"""
|
||||||
|
|
||||||
makerAddress: str
|
makerAddress: str
|
||||||
"""Address that created the order."""
|
"""Address that created the order."""
|
||||||
@ -203,11 +231,14 @@ def make_empty_order() -> Order:
|
|||||||
|
|
||||||
|
|
||||||
def order_to_jsdict(
|
def order_to_jsdict(
|
||||||
order: Order, exchange_address="0x0000000000000000000000000000000000000000"
|
order: Order,
|
||||||
|
exchange_address="0x0000000000000000000000000000000000000000",
|
||||||
|
signature: str = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Convert a Web3-compatible order struct to a JSON-schema-compatible dict.
|
"""Convert a Web3-compatible order struct to a JSON-schema-compatible dict.
|
||||||
|
|
||||||
More specifically, do explicit decoding for the `bytes`:code: fields.
|
More specifically, do explicit decoding for the `bytes`:code: fields, and
|
||||||
|
convert numerics to strings.
|
||||||
|
|
||||||
>>> import pprint
|
>>> import pprint
|
||||||
>>> pprint.pprint(order_to_jsdict(
|
>>> pprint.pprint(order_to_jsdict(
|
||||||
@ -228,18 +259,18 @@ def order_to_jsdict(
|
|||||||
... },
|
... },
|
||||||
... ))
|
... ))
|
||||||
{'exchangeAddress': '0x0000000000000000000000000000000000000000',
|
{'exchangeAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'expirationTimeSeconds': 1,
|
'expirationTimeSeconds': '1',
|
||||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'makerAddress': '0x0000000000000000000000000000000000000000',
|
'makerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'makerAssetAmount': 1,
|
'makerAssetAmount': '1',
|
||||||
'makerAssetData': '0x0000000000000000000000000000000000000000',
|
'makerAssetData': '0x0000000000000000000000000000000000000000',
|
||||||
'makerFee': 0,
|
'makerFee': '0',
|
||||||
'salt': 1,
|
'salt': '1',
|
||||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
'takerAssetAmount': 1,
|
'takerAssetAmount': '1',
|
||||||
'takerAssetData': '0x0000000000000000000000000000000000000000',
|
'takerAssetData': '0x0000000000000000000000000000000000000000',
|
||||||
'takerFee': 0}
|
'takerFee': '0'}
|
||||||
"""
|
"""
|
||||||
jsdict = cast(Dict, copy(order))
|
jsdict = cast(Dict, copy(order))
|
||||||
|
|
||||||
@ -249,29 +280,43 @@ def order_to_jsdict(
|
|||||||
|
|
||||||
jsdict["exchangeAddress"] = exchange_address
|
jsdict["exchangeAddress"] = exchange_address
|
||||||
|
|
||||||
|
jsdict["expirationTimeSeconds"] = str(order["expirationTimeSeconds"])
|
||||||
|
|
||||||
|
jsdict["makerAssetAmount"] = str(order["makerAssetAmount"])
|
||||||
|
jsdict["takerAssetAmount"] = str(order["takerAssetAmount"])
|
||||||
|
|
||||||
|
jsdict["makerFee"] = str(order["makerFee"])
|
||||||
|
jsdict["takerFee"] = str(order["takerFee"])
|
||||||
|
|
||||||
|
jsdict["salt"] = str(order["salt"])
|
||||||
|
|
||||||
|
if signature is not None:
|
||||||
|
jsdict["signature"] = signature
|
||||||
|
|
||||||
assert_valid(jsdict, "/orderSchema")
|
assert_valid(jsdict, "/orderSchema")
|
||||||
|
|
||||||
return jsdict
|
return jsdict
|
||||||
|
|
||||||
|
|
||||||
def jsdict_order_to_struct(jsdict: dict) -> Order:
|
def jsdict_to_order(jsdict: dict) -> Order:
|
||||||
r"""Convert a JSON-schema-compatible dict order to a Web3-compatible struct.
|
r"""Convert a JSON-schema-compatible dict order to a Web3-compatible struct.
|
||||||
|
|
||||||
More specifically, do explicit encoding of the `bytes`:code: fields.
|
More specifically, do explicit encoding of the `bytes`:code: fields, and
|
||||||
|
parse integers from strings.
|
||||||
|
|
||||||
>>> import pprint
|
>>> import pprint
|
||||||
>>> pprint.pprint(jsdict_order_to_struct(
|
>>> pprint.pprint(jsdict_to_order(
|
||||||
... {
|
... {
|
||||||
... 'makerAddress': "0x0000000000000000000000000000000000000000",
|
... 'makerAddress': "0x0000000000000000000000000000000000000000",
|
||||||
... 'takerAddress': "0x0000000000000000000000000000000000000000",
|
... 'takerAddress': "0x0000000000000000000000000000000000000000",
|
||||||
... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000",
|
... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000",
|
||||||
... 'senderAddress': "0x0000000000000000000000000000000000000000",
|
... 'senderAddress': "0x0000000000000000000000000000000000000000",
|
||||||
... 'makerAssetAmount': 1000000000000000000,
|
... 'makerAssetAmount': "1000000000000000000",
|
||||||
... 'takerAssetAmount': 1000000000000000000,
|
... 'takerAssetAmount': "1000000000000000000",
|
||||||
... 'makerFee': 0,
|
... 'makerFee': "0",
|
||||||
... 'takerFee': 0,
|
... 'takerFee': "0",
|
||||||
... 'expirationTimeSeconds': 12345,
|
... 'expirationTimeSeconds': "12345",
|
||||||
... 'salt': 12345,
|
... 'salt': "12345",
|
||||||
... 'makerAssetData': "0x0000000000000000000000000000000000000000",
|
... 'makerAssetData': "0x0000000000000000000000000000000000000000",
|
||||||
... 'takerAssetData': "0x0000000000000000000000000000000000000000",
|
... 'takerAssetData': "0x0000000000000000000000000000000000000000",
|
||||||
... 'exchangeAddress': "0x0000000000000000000000000000000000000000",
|
... 'exchangeAddress': "0x0000000000000000000000000000000000000000",
|
||||||
@ -303,6 +348,16 @@ def jsdict_order_to_struct(jsdict: dict) -> Order:
|
|||||||
remove_0x_prefix(jsdict["takerAssetData"])
|
remove_0x_prefix(jsdict["takerAssetData"])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
order["makerAssetAmount"] = int(jsdict["makerAssetAmount"])
|
||||||
|
order["takerAssetAmount"] = int(jsdict["takerAssetAmount"])
|
||||||
|
|
||||||
|
order["makerFee"] = int(jsdict["makerFee"])
|
||||||
|
order["takerFee"] = int(jsdict["takerFee"])
|
||||||
|
|
||||||
|
order["expirationTimeSeconds"] = int(jsdict["expirationTimeSeconds"])
|
||||||
|
|
||||||
|
order["salt"] = int(jsdict["salt"])
|
||||||
|
|
||||||
del order["exchangeAddress"] # type: ignore
|
del order["exchangeAddress"] # type: ignore
|
||||||
# silence mypy pending release of
|
# silence mypy pending release of
|
||||||
# https://github.com/python/mypy/issues/3550
|
# https://github.com/python/mypy/issues/3550
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
"""Asset data encoding and decoding utilities."""
|
"""Asset data encoding and decoding utilities."""
|
||||||
|
|
||||||
from mypy_extensions import TypedDict
|
from typing import NamedTuple
|
||||||
|
|
||||||
import eth_abi
|
import eth_abi
|
||||||
|
from deprecated.sphinx import deprecated
|
||||||
|
|
||||||
from zero_ex.dev_utils import abi_utils
|
from zero_ex.dev_utils import abi_utils
|
||||||
from zero_ex.dev_utils.type_assertions import assert_is_string, assert_is_int
|
from zero_ex.dev_utils.type_assertions import assert_is_string, assert_is_int
|
||||||
@ -13,23 +14,30 @@ ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH = 53
|
|||||||
SELECTOR_LENGTH = 10
|
SELECTOR_LENGTH = 10
|
||||||
|
|
||||||
|
|
||||||
class ERC20AssetData(TypedDict):
|
class ERC20AssetData(NamedTuple):
|
||||||
"""Object interface to ERC20 asset data."""
|
"""Object interface to ERC20 asset data."""
|
||||||
|
|
||||||
asset_proxy_id: str
|
asset_proxy_id: str
|
||||||
"""asset proxy id"""
|
"""Asset proxy identifier."""
|
||||||
|
|
||||||
token_address: str
|
token_address: str
|
||||||
|
"""Token address"""
|
||||||
|
|
||||||
|
|
||||||
class ERC721AssetData(TypedDict):
|
class ERC721AssetData(NamedTuple):
|
||||||
"""Object interface to ERC721 asset data."""
|
"""Object interface to ERC721 asset data."""
|
||||||
|
|
||||||
asset_proxy_id: str
|
asset_proxy_id: str
|
||||||
|
"""Asset proxy identifier."""
|
||||||
|
|
||||||
token_address: str
|
token_address: str
|
||||||
|
"""Token address"""
|
||||||
|
|
||||||
token_id: int
|
token_id: int
|
||||||
|
"""Token identifier."""
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated(reason='use `"0x"+encode_erc20().hex()` instead')
|
||||||
def encode_erc20_asset_data(token_address: str) -> str:
|
def encode_erc20_asset_data(token_address: str) -> str:
|
||||||
"""Encode an ERC20 token address into an asset data string.
|
"""Encode an ERC20 token address into an asset data string.
|
||||||
|
|
||||||
@ -48,13 +56,28 @@ def encode_erc20_asset_data(token_address: str) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_erc20(token_address: str) -> bytes:
|
||||||
|
"""Encode an ERC20 token address into asset data bytes.
|
||||||
|
|
||||||
|
:param token_address: the ERC20 token's contract address.
|
||||||
|
:returns: hex encoded asset data string, usable in the makerAssetData or
|
||||||
|
takerAssetData fields in a 0x order.
|
||||||
|
|
||||||
|
>>> encode_erc20('0x1dc4c1cefef38a777b15aa20260a54e584b16c48').hex()
|
||||||
|
'f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48'
|
||||||
|
"""
|
||||||
|
assert_is_string(token_address, "token_address")
|
||||||
|
|
||||||
|
return abi_utils.simple_encode("ERC20Token(address)", token_address)
|
||||||
|
|
||||||
|
|
||||||
def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData:
|
def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData:
|
||||||
"""Decode an ERC20 asset data hex string.
|
"""Decode an ERC20 asset data hex string.
|
||||||
|
|
||||||
:param asset_data: String produced by prior call to encode_erc20_asset_data()
|
:param asset_data: String produced by prior call to encode_erc20_asset_data()
|
||||||
|
|
||||||
>>> decode_erc20_asset_data("0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48")
|
>>> decode_erc20_asset_data("0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48")
|
||||||
{'asset_proxy_id': '0xf47261b0', 'token_address': '0x1dc4c1cefef38a777b15aa20260a54e584b16c48'}
|
ERC20AssetData(asset_proxy_id='0xf47261b0', token_address='0x1dc4c1cefef38a777b15aa20260a54e584b16c48')
|
||||||
""" # noqa: E501 (line too long)
|
""" # noqa: E501 (line too long)
|
||||||
assert_is_string(asset_data, "asset_data")
|
assert_is_string(asset_data, "asset_data")
|
||||||
|
|
||||||
@ -79,9 +102,12 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData:
|
|||||||
["address"], bytes.fromhex(asset_data[SELECTOR_LENGTH:])
|
["address"], bytes.fromhex(asset_data[SELECTOR_LENGTH:])
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
return {"asset_proxy_id": asset_proxy_id, "token_address": token_address}
|
return ERC20AssetData(
|
||||||
|
asset_proxy_id=asset_proxy_id, token_address=token_address
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated(reason='use `"0x"+encode_erc721().hex()` instead')
|
||||||
def encode_erc721_asset_data(token_address: str, token_id: int) -> str:
|
def encode_erc721_asset_data(token_address: str, token_id: int) -> str:
|
||||||
"""Encode an ERC721 asset data hex string.
|
"""Encode an ERC721 asset data hex string.
|
||||||
|
|
||||||
@ -104,11 +130,30 @@ def encode_erc721_asset_data(token_address: str, token_id: int) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def encode_erc721(token_address: str, token_id: int) -> bytes:
|
||||||
|
"""Encode an ERC721 token address into asset data bytes.
|
||||||
|
|
||||||
|
:param token_address: the ERC721 token's contract address.
|
||||||
|
:param token_id: the identifier of the asset's instance of the token.
|
||||||
|
:returns: hex encoded asset data string, usable in the makerAssetData or
|
||||||
|
takerAssetData fields in a 0x order.
|
||||||
|
|
||||||
|
>>> encode_erc721('0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 1).hex()
|
||||||
|
'025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001'
|
||||||
|
""" # noqa: E501 (line too long)
|
||||||
|
assert_is_string(token_address, "token_address")
|
||||||
|
assert_is_int(token_id, "token_id")
|
||||||
|
|
||||||
|
return abi_utils.simple_encode(
|
||||||
|
"ERC721Token(address,uint256)", token_address, token_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData:
|
def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData:
|
||||||
"""Decode an ERC721 asset data hex string.
|
"""Decode an ERC721 asset data hex string.
|
||||||
|
|
||||||
>>> decode_erc721_asset_data('0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001')
|
>>> decode_erc721_asset_data('0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001')
|
||||||
{'asset_proxy_id': '0x02571792', 'token_address': '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 'token_id': 1}
|
ERC721AssetData(asset_proxy_id='0x02571792', token_address='0x1dc4c1cefef38a777b15aa20260a54e584b16c48', token_id=1)
|
||||||
""" # noqa: E501 (line too long)
|
""" # noqa: E501 (line too long)
|
||||||
assert_is_string(asset_data, "asset_data")
|
assert_is_string(asset_data, "asset_data")
|
||||||
|
|
||||||
@ -134,8 +179,8 @@ def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData:
|
|||||||
["address", "uint256"], bytes.fromhex(asset_data[SELECTOR_LENGTH:])
|
["address", "uint256"], bytes.fromhex(asset_data[SELECTOR_LENGTH:])
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return ERC721AssetData(
|
||||||
"asset_proxy_id": asset_proxy_id,
|
asset_proxy_id=asset_proxy_id,
|
||||||
"token_address": token_address,
|
token_address=token_address,
|
||||||
"token_id": token_id,
|
token_id=token_id,
|
||||||
}
|
)
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
def deprecated(*args, **kwargs):
|
||||||
|
...
|
12
python-packages/sra_client/.discharge.json
Normal file
12
python-packages/sra_client/.discharge.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"domain": "0x-sra-client-py",
|
||||||
|
"build_command": "python setup.py build_sphinx",
|
||||||
|
"upload_directory": "build/docs/html",
|
||||||
|
"index_key": "index.html",
|
||||||
|
"error_key": "index.html",
|
||||||
|
"cache": 3600,
|
||||||
|
"aws_profile": "default",
|
||||||
|
"aws_region": "us-east-1",
|
||||||
|
"cdn": false,
|
||||||
|
"dns_configured": true
|
||||||
|
}
|
@ -97,17 +97,20 @@ class LintCommand(distutils.command.build_py.build_py):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run linter shell commands."""
|
"""Run linter shell commands."""
|
||||||
|
lint_targets = "test src/zero_ex/sra_client/__init__.py setup.py"
|
||||||
lint_commands = [
|
lint_commands = [
|
||||||
# formatter:
|
# formatter:
|
||||||
"black --line-length 79 --check --diff test sra_client/__init__.py setup.py".split(), # noqa: E501 (line too long)
|
(
|
||||||
|
f"black --line-length 79 --check --diff test {lint_targets}"
|
||||||
|
).split(),
|
||||||
# style guide checker (formerly pep8):
|
# style guide checker (formerly pep8):
|
||||||
"pycodestyle test sra_client/__init__.py setup.py".split(),
|
f"pycodestyle {lint_targets}".split(),
|
||||||
# docstring style checker:
|
# docstring style checker:
|
||||||
"pydocstyle src test sra_client/__init__.py setup.py".split(),
|
f"pydocstyle {lint_targets}".split(),
|
||||||
# static type checker:
|
# static type checker:
|
||||||
"bandit -r test sra_client/__init__.py setup.py".split(),
|
f"bandit -r {lint_targets}".split(),
|
||||||
# general linter:
|
# general linter:
|
||||||
"pylint test sra_client/__init__.py setup.py".split(),
|
f"pylint {lint_targets}".split(),
|
||||||
# pylint takes relatively long to run, so it runs last, to enable
|
# pylint takes relatively long to run, so it runs last, to enable
|
||||||
# fast failures.
|
# fast failures.
|
||||||
]
|
]
|
||||||
@ -134,7 +137,7 @@ class PublishDocsCommand(distutils.command.build_py.build_py):
|
|||||||
|
|
||||||
description = (
|
description = (
|
||||||
"Publish docs to "
|
"Publish docs to "
|
||||||
+ "http://0x-sra-demos-py.s3-website-us-east-1.amazonaws.com/"
|
+ "http://0x-sra-client-py.s3-website-us-east-1.amazonaws.com/"
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -150,7 +153,9 @@ setup(
|
|||||||
url="https://github.com/0xproject/0x-monorepo/python-packages/sra_client",
|
url="https://github.com/0xproject/0x-monorepo/python-packages/sra_client",
|
||||||
keywords=["OpenAPI", "OpenAPI-Generator", "Standard Relayer REST API"],
|
keywords=["OpenAPI", "OpenAPI-Generator", "Standard Relayer REST API"],
|
||||||
install_requires=REQUIRES,
|
install_requires=REQUIRES,
|
||||||
packages=find_packages(),
|
namespace_packages=["zero_ex"],
|
||||||
|
packages=find_packages("src"),
|
||||||
|
package_dir={"": "src"},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
long_description=README_MD,
|
long_description=README_MD,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
@ -183,7 +188,7 @@ setup(
|
|||||||
},
|
},
|
||||||
command_options={
|
command_options={
|
||||||
"build_sphinx": {
|
"build_sphinx": {
|
||||||
"source_dir": ("setup.py", "."),
|
"source_dir": ("setup.py", "src"),
|
||||||
"build_dir": ("setup.py", "build/docs"),
|
"build_dir": ("setup.py", "build/docs"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,236 +0,0 @@
|
|||||||
# coding: utf-8
|
|
||||||
|
|
||||||
# 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))
|
|
||||||
|
|
||||||
Wrapping ETH
|
|
||||||
------------
|
|
||||||
|
|
||||||
>>> from web3 import HTTPProvider, Web3
|
|
||||||
>>> from zero_ex.contract_addresses import (
|
|
||||||
... NETWORK_TO_ADDRESSES, NetworkId)
|
|
||||||
>>> from zero_ex.contract_artifacts import abi_by_name
|
|
||||||
>>> provider = HTTPProvider("http://localhost:8545")
|
|
||||||
>>> maker_address = "0x5409ed021d9299bf6814279a6a1411a7e866a631"
|
|
||||||
>>> erc20_proxy = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy
|
|
||||||
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
|
||||||
>>> weth_instance = Web3(provider).eth.contract(
|
|
||||||
... address=Web3.toChecksumAddress(weth_address),
|
|
||||||
... abi=abi_by_name("WETH9"))
|
|
||||||
>>> tx = weth_instance.functions.deposit().transact(
|
|
||||||
... {"from": Web3.toChecksumAddress(maker_address), "value": 1000000000000000000})
|
|
||||||
>>> tx = weth_instance.functions.approve(
|
|
||||||
... Web3.toChecksumAddress(erc20_proxy),
|
|
||||||
... 1000000000000000000).transact(
|
|
||||||
... {"from": Web3.toChecksumAddress(maker_address)})
|
|
||||||
|
|
||||||
Post Order
|
|
||||||
-----------
|
|
||||||
Post an order to an SRA-compliant Relayer.
|
|
||||||
|
|
||||||
>>> from zero_ex.order_utils import (
|
|
||||||
... asset_data_utils,
|
|
||||||
... generate_order_hash_hex,
|
|
||||||
... jsdict_order_to_struct,
|
|
||||||
... sign_hash)
|
|
||||||
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange
|
|
||||||
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].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=50, 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': '0x48bacb9266a570d521063ef5dd96e61686dbe788',
|
|
||||||
'expiration_time_seconds': '1000000000000000000000',
|
|
||||||
'fee_recipient_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'maker_address': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
|
||||||
'maker_asset_amount': '1000000000000000000',
|
|
||||||
'maker_asset_data': '0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082',
|
|
||||||
'maker_fee': '0',
|
|
||||||
'salt': '2362734632784682376287462',
|
|
||||||
'sender_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'taker_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'taker_asset_amount': '500000000000000000000',
|
|
||||||
'taker_asset_data': '0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
|
|
||||||
'taker_fee': '0'}}]}
|
|
||||||
|
|
||||||
Get Order
|
|
||||||
---------
|
|
||||||
Get an order by hash from an SRA-compliant Relayer.
|
|
||||||
|
|
||||||
>>> relayer_api.get_order("0x" + order_hash)
|
|
||||||
{'meta_data': {},
|
|
||||||
'order': {'exchange_address': '0x48bacb9266a570d521063ef5dd96e61686dbe788',
|
|
||||||
'expiration_time_seconds': '1000000000000000000000',
|
|
||||||
'fee_recipient_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'maker_address': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
|
||||||
'maker_asset_amount': '1000000000000000000',
|
|
||||||
'maker_asset_data': '0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082',
|
|
||||||
'maker_fee': '0',
|
|
||||||
'salt': '2362734632784682376287462',
|
|
||||||
'sender_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'taker_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'taker_asset_amount': '500000000000000000000',
|
|
||||||
'taker_asset_data': '0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
|
|
||||||
'taker_fee': '0'}}
|
|
||||||
|
|
||||||
Get Asset Pair
|
|
||||||
---------------
|
|
||||||
Get available asset pairs from an SRA-compliant Relayer.
|
|
||||||
|
|
||||||
>>> relayer_api.get_asset_pairs()
|
|
||||||
{'records': [{'assetDataA': {'assetData': '0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082',
|
|
||||||
'maxAmount': '115792089237316195423570985008687907853269984665640564039457584007913129639936',
|
|
||||||
'minAmount': '0',
|
|
||||||
'precision': 18},
|
|
||||||
'assetDataB': {'assetData': '0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
|
|
||||||
'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': '0x48bacb9266a570d521063ef5dd96e61686dbe788',
|
|
||||||
'expiration_time_seconds': '1000000000000000000000',
|
|
||||||
'fee_recipient_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'maker_address': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
|
||||||
'maker_asset_amount': '1000000000000000000',
|
|
||||||
'maker_asset_data': '0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082',
|
|
||||||
'maker_fee': '0',
|
|
||||||
'salt': '2362734632784682376287462',
|
|
||||||
'sender_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'taker_address': '0x0000000000000000000000000000000000000000',
|
|
||||||
'taker_asset_amount': '500000000000000000000',
|
|
||||||
'taker_asset_data': '0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
|
|
||||||
'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
|
|
||||||
|
|
||||||
__version__ = "1.0.0"
|
|
||||||
|
|
||||||
# import apis into sdk package
|
|
||||||
from sra_client.api.default_api import DefaultApi
|
|
||||||
|
|
||||||
# import ApiClient
|
|
||||||
from sra_client.api_client import ApiClient
|
|
||||||
from sra_client.configuration import Configuration
|
|
||||||
|
|
||||||
# import models into sdk package
|
|
||||||
from sra_client.models.order_schema import OrderSchema
|
|
||||||
from sra_client.models.paginated_collection_schema import (
|
|
||||||
PaginatedCollectionSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_asset_data_pairs_response_schema import (
|
|
||||||
RelayerApiAssetDataPairsResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_asset_data_trade_info_schema import (
|
|
||||||
RelayerApiAssetDataTradeInfoSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_error_response_schema import (
|
|
||||||
RelayerApiErrorResponseSchema,
|
|
||||||
)
|
|
||||||
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 (
|
|
||||||
RelayerApiFeeRecipientsResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_order_config_payload_schema import (
|
|
||||||
RelayerApiOrderConfigPayloadSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_order_config_response_schema import (
|
|
||||||
RelayerApiOrderConfigResponseSchema,
|
|
||||||
)
|
|
||||||
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 ( # noqa: E501 (line too long)
|
|
||||||
RelayerApiOrdersChannelSubscribePayloadSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_orders_channel_subscribe_schema import (
|
|
||||||
RelayerApiOrdersChannelSubscribeSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_orders_channel_update_schema import (
|
|
||||||
RelayerApiOrdersChannelUpdateSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_orders_response_schema import (
|
|
||||||
RelayerApiOrdersResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.signed_order_schema import SignedOrderSchema
|
|
@ -1,49 +0,0 @@
|
|||||||
# coding: utf-8
|
|
||||||
|
|
||||||
# flake8: noqa
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
# import models into model package
|
|
||||||
from sra_client.models.order_schema import OrderSchema
|
|
||||||
from sra_client.models.paginated_collection_schema import (
|
|
||||||
PaginatedCollectionSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_asset_data_pairs_response_schema import (
|
|
||||||
RelayerApiAssetDataPairsResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_asset_data_trade_info_schema import (
|
|
||||||
RelayerApiAssetDataTradeInfoSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_error_response_schema import (
|
|
||||||
RelayerApiErrorResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_error_response_schema_validation_errors import (
|
|
||||||
RelayerApiErrorResponseSchemaValidationErrors,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_fee_recipients_response_schema import (
|
|
||||||
RelayerApiFeeRecipientsResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_order_config_payload_schema import (
|
|
||||||
RelayerApiOrderConfigPayloadSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_order_config_response_schema import (
|
|
||||||
RelayerApiOrderConfigResponseSchema,
|
|
||||||
)
|
|
||||||
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 (
|
|
||||||
RelayerApiOrdersChannelSubscribePayloadSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_orders_channel_subscribe_schema import (
|
|
||||||
RelayerApiOrdersChannelSubscribeSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_orders_channel_update_schema import (
|
|
||||||
RelayerApiOrdersChannelUpdateSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.relayer_api_orders_response_schema import (
|
|
||||||
RelayerApiOrdersResponseSchema,
|
|
||||||
)
|
|
||||||
from sra_client.models.signed_order_schema import SignedOrderSchema
|
|
0
python-packages/sra_client/src/doc_static/.gitkeep
Normal file
0
python-packages/sra_client/src/doc_static/.gitkeep
Normal file
@ -1,20 +1,18 @@
|
|||||||
.. source for the sphinx-generated build/docs/web/index.html
|
.. source for the sphinx-generated build/docs/web/index.html
|
||||||
|
|
||||||
Python zero_ex.sra_client.api_client
|
Python zero_ex.sra_client
|
||||||
====================================
|
=========================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
.. automodule:: sra_client
|
.. automodule:: zero_ex.sra_client
|
||||||
|
|
||||||
----
|
zero_ex.sra_client.DefaultApi
|
||||||
|
=============================
|
||||||
|
|
||||||
API
|
.. autoclass:: zero_ex.sra_client.DefaultApi
|
||||||
---
|
|
||||||
|
|
||||||
.. automodule:: sra_client.api.default_api
|
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
2
python-packages/sra_client/src/zero_ex/__init__.py
Normal file
2
python-packages/sra_client/src/zero_ex/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"""0x Python API."""
|
||||||
|
__import__("pkg_resources").declare_namespace(__name__)
|
392
python-packages/sra_client/src/zero_ex/sra_client/__init__.py
Normal file
392
python-packages/sra_client/src/zero_ex/sra_client/__init__.py
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
# flake8: noqa
|
||||||
|
|
||||||
|
r"""A Python client for interacting with SRA-compatible Relayers.
|
||||||
|
|
||||||
|
0x Protocol is an open standard. Many Relayers opt to implementing a set of
|
||||||
|
`Standard Relayer API (SRA)
|
||||||
|
<http://sra-spec.s3-website-us-east-1.amazonaws.com/>`_ endpoints, 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 the `0x-sra-client`:code: module to
|
||||||
|
interact with 0x relayers that implement the SRA specification.
|
||||||
|
|
||||||
|
Setup
|
||||||
|
-----
|
||||||
|
|
||||||
|
Install the package with pip::
|
||||||
|
|
||||||
|
pip install 0x-sra-client
|
||||||
|
|
||||||
|
To interact with a 0x Relayer, you need the HTTP endpoint of the Relayer you'd
|
||||||
|
like to connect to (eg https://api.radarrelay.com/0x/v2).
|
||||||
|
|
||||||
|
For testing one can use the `0x-launch-kit
|
||||||
|
<https://github.com/0xProject/0x-launch-kit#table-of-contents/>`_ to host
|
||||||
|
orders locally. The examples below assume that this server is running locally
|
||||||
|
and listening on port 3000, so the Relayer URL they use is
|
||||||
|
`http://localhost:3000`:code:.
|
||||||
|
|
||||||
|
By default, Launch Kit will connect to Kovan via Infura. However, it can be
|
||||||
|
configured to connect to any JSON-RPC endpoint, on any network. The examples
|
||||||
|
below assume that Launch Kit is connected to a Ganache development network
|
||||||
|
accessible at `http://localhost:8545`:code:.
|
||||||
|
|
||||||
|
To replicate this setup, one could run the following commands:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
docker run -d -p 8545:8545 0xorg/ganache-cli
|
||||||
|
|
||||||
|
docker run -d --network host \
|
||||||
|
-e RPC_URL=http://localhost:8545 \
|
||||||
|
-e NETWORK_ID=50 \
|
||||||
|
-e WHITELIST_ALL_TOKENS=True \
|
||||||
|
0xorg/launch-kit-ci
|
||||||
|
|
||||||
|
(Note: This will only work on Linux, because `--network host`:code: only works
|
||||||
|
on Linux. For other platforms one would have to clone `the 0x-launch-kit
|
||||||
|
repository <https://github.com/0xProject/0x-launch-kit>`_ and build and start
|
||||||
|
the server.)
|
||||||
|
|
||||||
|
Configure and create an API client instance
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
>>> from zero_ex.sra_client import ApiClient, Configuration, DefaultApi
|
||||||
|
>>> config = Configuration()
|
||||||
|
>>> config.host = "http://localhost:3000"
|
||||||
|
>>> relayer = DefaultApi(ApiClient(config))
|
||||||
|
|
||||||
|
Preparing to trade
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Making and taking orders induces the SRA endpoint to deal with the Ethereum
|
||||||
|
network. Before we can start trading, we need to do a few things with the
|
||||||
|
network directly.
|
||||||
|
|
||||||
|
To start, connect to the Ethereum network:
|
||||||
|
|
||||||
|
>>> from web3 import HTTPProvider, Web3
|
||||||
|
>>> eth_node = HTTPProvider("http://localhost:8545")
|
||||||
|
|
||||||
|
What network is it?
|
||||||
|
|
||||||
|
>>> from zero_ex.contract_addresses import NetworkId
|
||||||
|
>>> network_id = NetworkId.GANACHE # you might use .MAINNET or .KOVAN
|
||||||
|
|
||||||
|
For our Maker role, we'll just use the first address available in the node:
|
||||||
|
|
||||||
|
>>> maker_address = Web3(eth_node).eth.accounts[0].lower()
|
||||||
|
|
||||||
|
The 0x Ganache snapshot loaded into our eth_node has a pre-loaded ZRX balance
|
||||||
|
for this account, so the example orders below have the maker trading away ZRX.
|
||||||
|
Before such an order can be valid, though, the maker must give the 0x contracts
|
||||||
|
permission to trade their ZRX tokens:
|
||||||
|
|
||||||
|
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES
|
||||||
|
>>> contract_addresses = NETWORK_TO_ADDRESSES[network_id]
|
||||||
|
>>>
|
||||||
|
>>> from zero_ex.contract_artifacts import abi_by_name
|
||||||
|
>>> zrx_token_contract = Web3(eth_node).eth.contract(
|
||||||
|
... address=Web3.toChecksumAddress(contract_addresses.zrx_token),
|
||||||
|
... abi=abi_by_name("ZRXToken")
|
||||||
|
... )
|
||||||
|
>>>
|
||||||
|
>>> zrx_token_contract.functions.approve(
|
||||||
|
... Web3.toChecksumAddress(contract_addresses.erc20_proxy),
|
||||||
|
... 1000000000000000000
|
||||||
|
... ).transact(
|
||||||
|
... {"from": Web3.toChecksumAddress(maker_address)}
|
||||||
|
... )
|
||||||
|
HexBytes('0x...')
|
||||||
|
|
||||||
|
Post Order
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Post an order for our Maker to trade ZRX for WETH:
|
||||||
|
|
||||||
|
>>> from zero_ex.order_utils import (
|
||||||
|
... asset_data_utils,
|
||||||
|
... Order,
|
||||||
|
... order_to_jsdict,
|
||||||
|
... sign_hash)
|
||||||
|
>>> import random
|
||||||
|
>>> from datetime import datetime, timedelta
|
||||||
|
>>> order = Order(
|
||||||
|
... makerAddress=maker_address,
|
||||||
|
... takerAddress="0x0000000000000000000000000000000000000000",
|
||||||
|
... senderAddress="0x0000000000000000000000000000000000000000",
|
||||||
|
... exchangeAddress=contract_addresses.exchange,
|
||||||
|
... feeRecipientAddress="0x0000000000000000000000000000000000000000",
|
||||||
|
... makerAssetData=asset_data_utils.encode_erc20(
|
||||||
|
... contract_addresses.zrx_token
|
||||||
|
... ),
|
||||||
|
... takerAssetData=asset_data_utils.encode_erc20(
|
||||||
|
... contract_addresses.ether_token
|
||||||
|
... ),
|
||||||
|
... salt=random.randint(1, 100000000000000000),
|
||||||
|
... makerFee=0,
|
||||||
|
... takerFee=0,
|
||||||
|
... makerAssetAmount=2,
|
||||||
|
... takerAssetAmount=2,
|
||||||
|
... expirationTimeSeconds=round(
|
||||||
|
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||||
|
... )
|
||||||
|
... )
|
||||||
|
|
||||||
|
>>> from zero_ex.order_utils import generate_order_hash_hex
|
||||||
|
>>> order_hash_hex = generate_order_hash_hex(
|
||||||
|
... order, contract_addresses.exchange
|
||||||
|
... )
|
||||||
|
>>> relayer.post_order_with_http_info(
|
||||||
|
... network_id=network_id.value,
|
||||||
|
... signed_order_schema=order_to_jsdict(
|
||||||
|
... order=order,
|
||||||
|
... exchange_address=contract_addresses.exchange,
|
||||||
|
... signature=sign_hash(
|
||||||
|
... eth_node, Web3.toChecksumAddress(maker_address), order_hash_hex
|
||||||
|
... )
|
||||||
|
... )
|
||||||
|
... )[1]
|
||||||
|
200
|
||||||
|
|
||||||
|
Get Order
|
||||||
|
---------
|
||||||
|
|
||||||
|
Retrieve the order we just posted:
|
||||||
|
|
||||||
|
>>> relayer.get_order("0x" + order_hash_hex)
|
||||||
|
{'meta_data': {},
|
||||||
|
'order': {'exchangeAddress': '0x...',
|
||||||
|
'expirationTimeSeconds': '...',
|
||||||
|
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'makerAddress': '0x...',
|
||||||
|
'makerAssetAmount': '2',
|
||||||
|
'makerAssetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'makerFee': '0',
|
||||||
|
'salt': '...',
|
||||||
|
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'signature': '0x...',
|
||||||
|
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'takerAssetAmount': '2',
|
||||||
|
'takerAssetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'takerFee': '0'}}
|
||||||
|
|
||||||
|
Get Orders
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Retrieve all of the Relayer's orders, a set which at this point consists solely
|
||||||
|
of the one we just posted:
|
||||||
|
|
||||||
|
>>> relayer.get_orders()
|
||||||
|
{'records': [{'meta_data': {},
|
||||||
|
'order': {'exchangeAddress': '0x...',
|
||||||
|
'expirationTimeSeconds': '...',
|
||||||
|
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'makerAddress': '0x...',
|
||||||
|
'makerAssetAmount': '2',
|
||||||
|
'makerAssetData': '0xf47261b000000000000000000000000...',
|
||||||
|
'makerFee': '0',
|
||||||
|
'salt': '...',
|
||||||
|
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'signature': '0x...',
|
||||||
|
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'takerAssetAmount': '2',
|
||||||
|
'takerAssetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'takerFee': '0'}}]}
|
||||||
|
|
||||||
|
Get Asset Pairs
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Get all of the Relayer's available asset pairs, which here means just WETH and
|
||||||
|
ZRX, since that's all there is on this Relayer's order book:
|
||||||
|
|
||||||
|
>>> relayer.get_asset_pairs()
|
||||||
|
{'records': [{'assetDataA': {'assetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'maxAmount': '115792089237316195423570985008687907853269984665640564039457584007913129639936',
|
||||||
|
'minAmount': '0',
|
||||||
|
'precision': 18},
|
||||||
|
'assetDataB': {'assetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'maxAmount': '115792089237316195423570985008687907853269984665640564039457584007913129639936',
|
||||||
|
'minAmount': '0',
|
||||||
|
'precision': 18}}]}
|
||||||
|
>>> asset_data_utils.decode_erc20_asset_data(
|
||||||
|
... relayer.get_asset_pairs().records[0]['assetDataA']['assetData']
|
||||||
|
... ).token_address == contract_addresses.zrx_token
|
||||||
|
True
|
||||||
|
>>> asset_data_utils.decode_erc20_asset_data(
|
||||||
|
... relayer.get_asset_pairs().records[0]['assetDataB']['assetData']
|
||||||
|
... ).token_address == contract_addresses.ether_token
|
||||||
|
True
|
||||||
|
|
||||||
|
Get Orderbook
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Get the Relayer's order book for the WETH/ZRX asset pair (which, again,
|
||||||
|
consists just of our order):
|
||||||
|
|
||||||
|
>>> orderbook = relayer.get_orderbook(
|
||||||
|
... base_asset_data= "0x" + asset_data_utils.encode_erc20(
|
||||||
|
... contract_addresses.ether_token
|
||||||
|
... ).hex(),
|
||||||
|
... quote_asset_data= "0x" + asset_data_utils.encode_erc20(
|
||||||
|
... contract_addresses.zrx_token
|
||||||
|
... ).hex(),
|
||||||
|
... )
|
||||||
|
>>> orderbook
|
||||||
|
{'asks': {'records': []},
|
||||||
|
'bids': {'records': [{'meta_data': {},
|
||||||
|
'order': {'exchangeAddress': '0x...',
|
||||||
|
'expirationTimeSeconds': '...',
|
||||||
|
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'makerAddress': '0x...',
|
||||||
|
'makerAssetAmount': '2',
|
||||||
|
'makerAssetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'makerFee': '0',
|
||||||
|
'salt': '...',
|
||||||
|
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'signature': '0x...',
|
||||||
|
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'takerAssetAmount': '2',
|
||||||
|
'takerAssetData': '0xf47261b0000000000000000000000000...',
|
||||||
|
'takerFee': '0'}}]}}
|
||||||
|
|
||||||
|
Select an order from the orderbook
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
>>> from zero_ex.order_utils import jsdict_to_order
|
||||||
|
>>> order = jsdict_to_order(orderbook.bids.records[0].order)
|
||||||
|
>>> from pprint import pprint
|
||||||
|
>>> pprint(order)
|
||||||
|
{'expirationTimeSeconds': ...,
|
||||||
|
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'makerAddress': '0x...',
|
||||||
|
'makerAssetAmount': 2,
|
||||||
|
'makerAssetData': b...
|
||||||
|
'makerFee': 0,
|
||||||
|
'salt': ...,
|
||||||
|
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'signature': '0x...',
|
||||||
|
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||||
|
'takerAssetAmount': 2,
|
||||||
|
'takerAssetData': b...
|
||||||
|
'takerFee': 0}
|
||||||
|
|
||||||
|
Filling or Cancelling an Order
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Fills and cancels are triggered by dealing directly with the 0x Exchange
|
||||||
|
contract, not by going through a Relayer.
|
||||||
|
|
||||||
|
See `the 0x-contract-wrappers documentation
|
||||||
|
<http://0x-contract-wrappers-py.s3-website-us-east-1.amazonaws.com/>`_ for more
|
||||||
|
examples.
|
||||||
|
|
||||||
|
Filling
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
>>> taker_address = Web3(eth_node).eth.accounts[1].lower()
|
||||||
|
|
||||||
|
Our taker will take a ZRX/WETH order, but it doesn't have any WETH yet. By
|
||||||
|
depositing some ether into the WETH contract, it will be given some WETH to
|
||||||
|
trade with:
|
||||||
|
|
||||||
|
>>> weth_instance = Web3(eth_node).eth.contract(
|
||||||
|
... address=Web3.toChecksumAddress(contract_addresses.ether_token),
|
||||||
|
... abi=abi_by_name("WETH9")
|
||||||
|
... )
|
||||||
|
>>> weth_instance.functions.deposit().transact(
|
||||||
|
... {"from": Web3.toChecksumAddress(taker_address),
|
||||||
|
... "value": 1000000000000000000}
|
||||||
|
... )
|
||||||
|
HexBytes('0x...')
|
||||||
|
|
||||||
|
Next the taker needs to give the 0x contracts permission to trade their WETH:
|
||||||
|
|
||||||
|
>>> weth_instance.functions.approve(
|
||||||
|
... Web3.toChecksumAddress(contract_addresses.erc20_proxy),
|
||||||
|
... 1000000000000000000).transact(
|
||||||
|
... {"from": Web3.toChecksumAddress(taker_address)})
|
||||||
|
HexBytes('0x...')
|
||||||
|
|
||||||
|
Now the taker is ready to trade.
|
||||||
|
|
||||||
|
Recall that in a previous example we selected a specific order from the order
|
||||||
|
book. Now let's have the taker fill it:
|
||||||
|
|
||||||
|
>>> from zero_ex.contract_wrappers import Exchange, TxParams
|
||||||
|
>>> from zero_ex.order_utils import Order
|
||||||
|
>>> Exchange(eth_node).fill_order(
|
||||||
|
... order=order,
|
||||||
|
... taker_amount=order['makerAssetAmount']/2, # note the half fill
|
||||||
|
... signature=order['signature'],
|
||||||
|
... tx_params=TxParams(from_=taker_address)
|
||||||
|
... )
|
||||||
|
HexBytes('0x...')
|
||||||
|
|
||||||
|
Cancelling
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
Note that the above fill was partial: it only filled half of the order. Now
|
||||||
|
we'll have our maker cancel the remaining order:
|
||||||
|
|
||||||
|
>>> Exchange(eth_node).cancel_order(
|
||||||
|
... order=order,
|
||||||
|
... tx_params=TxParams(from_=maker_address)
|
||||||
|
... )
|
||||||
|
HexBytes('0x...')
|
||||||
|
|
||||||
|
""" # noqa: E501 (line too long)
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
__version__ = "1.0.0"
|
||||||
|
|
||||||
|
# import apis into sdk package
|
||||||
|
from .api.default_api import DefaultApi
|
||||||
|
|
||||||
|
# import ApiClient
|
||||||
|
from .api_client import ApiClient
|
||||||
|
from .configuration import Configuration
|
||||||
|
|
||||||
|
# import models into sdk package
|
||||||
|
from .models.order_schema import OrderSchema
|
||||||
|
from .models.paginated_collection_schema import PaginatedCollectionSchema
|
||||||
|
from .models.relayer_api_asset_data_pairs_response_schema import (
|
||||||
|
RelayerApiAssetDataPairsResponseSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_asset_data_trade_info_schema import (
|
||||||
|
RelayerApiAssetDataTradeInfoSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_error_response_schema import (
|
||||||
|
RelayerApiErrorResponseSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_error_response_schema_validation_errors import (
|
||||||
|
RelayerApiErrorResponseSchemaValidationErrors,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_fee_recipients_response_schema import (
|
||||||
|
RelayerApiFeeRecipientsResponseSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_order_config_payload_schema import (
|
||||||
|
RelayerApiOrderConfigPayloadSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_order_config_response_schema import (
|
||||||
|
RelayerApiOrderConfigResponseSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_order_schema import RelayerApiOrderSchema
|
||||||
|
from .models.relayer_api_orderbook_response_schema import (
|
||||||
|
RelayerApiOrderbookResponseSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_orders_channel_subscribe_payload_schema import (
|
||||||
|
RelayerApiOrdersChannelSubscribePayloadSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_orders_channel_subscribe_schema import (
|
||||||
|
RelayerApiOrdersChannelSubscribeSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_orders_channel_update_schema import (
|
||||||
|
RelayerApiOrdersChannelUpdateSchema,
|
||||||
|
)
|
||||||
|
from .models.relayer_api_orders_response_schema import (
|
||||||
|
RelayerApiOrdersResponseSchema,
|
||||||
|
)
|
||||||
|
from .models.signed_order_schema import SignedOrderSchema
|
@ -3,4 +3,4 @@ from __future__ import absolute_import
|
|||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
|
||||||
# import apis into api package
|
# import apis into api package
|
||||||
from sra_client.api.default_api import DefaultApi
|
from zero_ex.sra_client.api.default_api import DefaultApi
|
@ -8,8 +8,8 @@ import re # noqa: F401
|
|||||||
# python 2 and python 3 compatibility library
|
# python 2 and python 3 compatibility library
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from sra_client.api_client import ApiClient
|
from zero_ex.sra_client.api_client import ApiClient
|
||||||
from sra_client.models.relayer_api_order_config_payload_schema import (
|
from zero_ex.sra_client.models.relayer_api_order_config_payload_schema import (
|
||||||
RelayerApiOrderConfigPayloadSchema,
|
RelayerApiOrderConfigPayloadSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,9 +42,9 @@ class DefaultApi(object):
|
|||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param str asset_data_a: The assetData value for the first asset in the pair.
|
:param str asset_data_a: The assetData value for the first asset in the pair.
|
||||||
:param str asset_data_b: The assetData value for the second asset in the pair.
|
:param str asset_data_b: The assetData value for the second asset in the pair.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiAssetDataPairsResponseSchema`.
|
:return: :class:`RelayerApiAssetDataPairsResponseSchema`.
|
||||||
If the method is called asynchronously returns the request thread.
|
If the method is called asynchronously returns the request thread.
|
||||||
@ -71,12 +71,14 @@ class DefaultApi(object):
|
|||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param str asset_data_a: The assetData value for the first asset in the pair.
|
:param str asset_data_a: The assetData value for the first asset in the pair.
|
||||||
:param str asset_data_b: The assetData value for the second asset in the pair.
|
:param str asset_data_b: The assetData value for the second asset in the pair.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiAssetDataPairsResponseSchema`.
|
:return: A tuple consisting of a
|
||||||
If the method is called asynchronously returns the request thread.
|
:class:`RelayerApiAssetDataPairsResponseSchema`, an HTTP status
|
||||||
|
code integer, and a collection of HTTP headers. If the method is
|
||||||
|
called asynchronously returns the request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
||||||
@ -168,9 +170,9 @@ class DefaultApi(object):
|
|||||||
>>> result = thread.get() # doctest: +SKIP
|
>>> result = thread.get() # doctest: +SKIP
|
||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiFeeRecipientsResponseSchema`.
|
:return: :class:`RelayerApiFeeRecipientsResponseSchema`.
|
||||||
If the method is called asynchronously, returns the request thread.
|
If the method is called asynchronously, returns the request thread.
|
||||||
@ -194,12 +196,14 @@ class DefaultApi(object):
|
|||||||
>>> result = thread.get() # doctest: +SKIP
|
>>> result = thread.get() # doctest: +SKIP
|
||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiFeeRecipientsResponseSchema`.
|
:return: A tuple consisting of a
|
||||||
If the method is called asynchronously, returns the request thread.
|
:class:`RelayerApiFeeRecipientsResponseSchema`, an HTTP status
|
||||||
|
code integer, and a collection of HTTP headers. If the method is
|
||||||
|
called asynchronously returns the request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
||||||
@ -277,7 +281,7 @@ class DefaultApi(object):
|
|||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param str order_hash: The hash of the desired 0x order. (required)
|
:param str order_hash: The hash of the desired 0x order. (required)
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
|
|
||||||
:return: :class:`RelayerApiOrderSchema`.
|
:return: :class:`RelayerApiOrderSchema`.
|
||||||
If the method is called asynchronously, returns the request thread.
|
If the method is called asynchronously, returns the request thread.
|
||||||
@ -301,10 +305,12 @@ class DefaultApi(object):
|
|||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param str order_hash: The hash of the desired 0x order. (required)
|
:param str order_hash: The hash of the desired 0x order. (required)
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
|
|
||||||
:return: :class:`RelayerApiOrderSchema`.
|
:return: A tuple consisting of a
|
||||||
If the method is called asynchronously returns the request thread.
|
:class:`RelayerApiOrderSchema`, an HTTP status code integer, and a
|
||||||
|
collection of HTTP headers. If the method is called
|
||||||
|
asynchronously returns the request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
||||||
@ -395,7 +401,7 @@ class DefaultApi(object):
|
|||||||
>>> result = thread.get() # doctest: +SKIP
|
>>> result = thread.get() # doctest: +SKIP
|
||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param relayer_api_order_config_payload_schema: instance of
|
:param relayer_api_order_config_payload_schema: instance of
|
||||||
:class:`RelayerApiOrderConfigPayloadSchema`. The fields of a 0x
|
:class:`RelayerApiOrderConfigPayloadSchema`. The fields of a 0x
|
||||||
order the relayer may want to decide what configuration to send
|
order the relayer may want to decide what configuration to send
|
||||||
@ -430,14 +436,16 @@ class DefaultApi(object):
|
|||||||
>>> result = thread.get() # doctest: +SKIP
|
>>> result = thread.get() # doctest: +SKIP
|
||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param relayer_api_order_config_payload_schema: instance of
|
:param relayer_api_order_config_payload_schema: instance of
|
||||||
:class: `RelayerApiOrderConfigPayloadSchema`. The fields of a 0x
|
:class: `RelayerApiOrderConfigPayloadSchema`. The fields of a 0x
|
||||||
order the relayer may want to decide what configuration to send
|
order the relayer may want to decide what configuration to send
|
||||||
back.
|
back.
|
||||||
|
|
||||||
:return: :class:`RelayerApiOrderConfigResponseSchema`.
|
:return: A tuple consisting of a
|
||||||
If the method is called asynchronously returns the request thread.
|
:class:`RelayerApiOrderConfigResponseSchema`, an HTTP status code
|
||||||
|
integer, and a collection of HTTP headers. If the method is
|
||||||
|
called asynchronously returns the request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
||||||
@ -537,9 +545,9 @@ class DefaultApi(object):
|
|||||||
:param str quote_asset_data: assetData (makerAssetData or
|
:param str quote_asset_data: assetData (makerAssetData or
|
||||||
takerAssetData) designated as the quote currency in the currency
|
takerAssetData) designated as the quote currency in the currency
|
||||||
pair calculation of price. (required)
|
pair calculation of price. (required)
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiOrderbookResponseSchema`.
|
:return: :class:`RelayerApiOrderbookResponseSchema`.
|
||||||
If the method is called asynchronously, returns the request thread.
|
If the method is called asynchronously, returns the request thread.
|
||||||
@ -586,12 +594,14 @@ class DefaultApi(object):
|
|||||||
:param str quote_asset_data: assetData (makerAssetData or
|
:param str quote_asset_data: assetData (makerAssetData or
|
||||||
takerAssetData) designated as the quote currency in the currency
|
takerAssetData) designated as the quote currency in the currency
|
||||||
pair calculation of price. (required)
|
pair calculation of price. (required)
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiOrderbookResponseSchema`.
|
:return: A tuple consisting of a
|
||||||
If the method is called asynchronously, returns the request thread.
|
:class:`RelayerApiOrderbookResponseSchema`, an HTTP status code
|
||||||
|
integer, and a collection of HTTP headers. If the method is
|
||||||
|
called asynchronously returns the request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
||||||
@ -752,9 +762,9 @@ class DefaultApi(object):
|
|||||||
`0x Protocol v2 Specification
|
`0x Protocol v2 Specification
|
||||||
<https://github.com/0xProject/0x-protocol-specification/blob/
|
<https://github.com/0xProject/0x-protocol-specification/blob/
|
||||||
master/v2/v2-specification.md#order-message-format>`__
|
master/v2/v2-specification.md#order-message-format>`__
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: :class:`RelayerApiOrdersResponseSchema`.
|
:return: :class:`RelayerApiOrdersResponseSchema`.
|
||||||
If the method is called asynchronously, returns the request thread.
|
If the method is called asynchronously, returns the request thread.
|
||||||
@ -829,12 +839,14 @@ class DefaultApi(object):
|
|||||||
`0x Protocol v2 Specification
|
`0x Protocol v2 Specification
|
||||||
<https://github.com/0xProject/0x-protocol-specification/blob/
|
<https://github.com/0xProject/0x-protocol-specification/blob/
|
||||||
master/v2/v2-specification.md#order-message-format>`__
|
master/v2/v2-specification.md#order-message-format>`__
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param float page: The number of the page to request in the collection.
|
:param int page: The number of the page to request in the collection.
|
||||||
:param float per_page: The number of records to return per page.
|
:param int per_page: The number of records to return per page.
|
||||||
|
|
||||||
:return: RelayerApiOrdersResponseSchema.
|
:return: A tuple consisting of a
|
||||||
If the method is called asynchronously, returns the request thread.
|
:class:`RelayerApiOrdersResponseSchema`, an HTTP status code
|
||||||
|
integer, and a collection of HTTP headers. If the method is
|
||||||
|
called asynchronously returns the request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
||||||
@ -983,7 +995,7 @@ class DefaultApi(object):
|
|||||||
>>> result = thread.get() # doctest: +SKIP
|
>>> result = thread.get() # doctest: +SKIP
|
||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param signed_order_schema: Instance of :class:`SignedOrderSchema`.
|
:param signed_order_schema: Instance of :class:`SignedOrderSchema`.
|
||||||
A valid signed 0x order based on the schema.
|
A valid signed 0x order based on the schema.
|
||||||
|
|
||||||
@ -1008,12 +1020,14 @@ class DefaultApi(object):
|
|||||||
>>> result = thread.get() # doctest: +SKIP
|
>>> result = thread.get() # doctest: +SKIP
|
||||||
|
|
||||||
:param bool async_req: Whether request should be asynchronous.
|
:param bool async_req: Whether request should be asynchronous.
|
||||||
:param float network_id: The id of the Ethereum network
|
:param int network_id: The id of the Ethereum network
|
||||||
:param signed_order_schema: Instance of :class:`SignedOrderSchema`
|
:param signed_order_schema: Instance of :class:`SignedOrderSchema`
|
||||||
A valid signed 0x order based on the schema.
|
A valid signed 0x order based on the schema.
|
||||||
|
|
||||||
:return: None.
|
:return: A tuple consisting of the response data (always empty for this
|
||||||
If the method is called asynchronously, returns the request thread.
|
method), an HTTP status code integer, and a collection of HTTP
|
||||||
|
headers. If the method is called asynchronously returns the
|
||||||
|
request thread.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
local_var_params = locals()
|
local_var_params = locals()
|
@ -14,9 +14,9 @@ import tempfile
|
|||||||
import six
|
import six
|
||||||
from six.moves.urllib.parse import quote
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
from sra_client.configuration import Configuration
|
from zero_ex.sra_client.configuration import Configuration
|
||||||
import sra_client.models
|
import zero_ex.sra_client.models
|
||||||
from sra_client import rest
|
from zero_ex.sra_client import rest
|
||||||
|
|
||||||
|
|
||||||
class ApiClient(object):
|
class ApiClient(object):
|
||||||
@ -300,7 +300,7 @@ class ApiClient(object):
|
|||||||
if klass in self.NATIVE_TYPES_MAPPING:
|
if klass in self.NATIVE_TYPES_MAPPING:
|
||||||
klass = self.NATIVE_TYPES_MAPPING[klass]
|
klass = self.NATIVE_TYPES_MAPPING[klass]
|
||||||
else:
|
else:
|
||||||
klass = getattr(sra_client.models, klass)
|
klass = getattr(zero_ex.sra_client.models, klass)
|
||||||
|
|
||||||
if klass in self.PRIMITIVE_TYPES:
|
if klass in self.PRIMITIVE_TYPES:
|
||||||
return self.__deserialize_primitive(data, klass)
|
return self.__deserialize_primitive(data, klass)
|
@ -0,0 +1,49 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
# flake8: noqa
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
# import models into model package
|
||||||
|
from zero_ex.sra_client.models.order_schema import OrderSchema
|
||||||
|
from zero_ex.sra_client.models.paginated_collection_schema import (
|
||||||
|
PaginatedCollectionSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_asset_data_pairs_response_schema import (
|
||||||
|
RelayerApiAssetDataPairsResponseSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_asset_data_trade_info_schema import (
|
||||||
|
RelayerApiAssetDataTradeInfoSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_error_response_schema import (
|
||||||
|
RelayerApiErrorResponseSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_error_response_schema_validation_errors import (
|
||||||
|
RelayerApiErrorResponseSchemaValidationErrors,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_fee_recipients_response_schema import (
|
||||||
|
RelayerApiFeeRecipientsResponseSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_order_config_payload_schema import (
|
||||||
|
RelayerApiOrderConfigPayloadSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_order_config_response_schema import (
|
||||||
|
RelayerApiOrderConfigResponseSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_order_schema import RelayerApiOrderSchema
|
||||||
|
from zero_ex.sra_client.models.relayer_api_orderbook_response_schema import (
|
||||||
|
RelayerApiOrderbookResponseSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_orders_channel_subscribe_payload_schema import (
|
||||||
|
RelayerApiOrdersChannelSubscribePayloadSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_orders_channel_subscribe_schema import (
|
||||||
|
RelayerApiOrdersChannelSubscribeSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_orders_channel_update_schema import (
|
||||||
|
RelayerApiOrdersChannelUpdateSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.relayer_api_orders_response_schema import (
|
||||||
|
RelayerApiOrdersResponseSchema,
|
||||||
|
)
|
||||||
|
from zero_ex.sra_client.models.signed_order_schema import SignedOrderSchema
|
@ -21,21 +21,7 @@ class OrderSchema(object):
|
|||||||
attribute_map (dict): The key is attribute name
|
attribute_map (dict): The key is attribute name
|
||||||
and the value is json key in definition.
|
and the value is json key in definition.
|
||||||
"""
|
"""
|
||||||
openapi_types = {
|
openapi_types = {}
|
||||||
"maker_address": "str",
|
|
||||||
"taker_address": "str",
|
|
||||||
"maker_fee": "str",
|
|
||||||
"taker_fee": "str",
|
|
||||||
"sender_address": "str",
|
|
||||||
"maker_asset_amount": "str",
|
|
||||||
"taker_asset_amount": "str",
|
|
||||||
"maker_asset_data": "str",
|
|
||||||
"taker_asset_data": "str",
|
|
||||||
"salt": "str",
|
|
||||||
"exchange_address": "str",
|
|
||||||
"fee_recipient_address": "str",
|
|
||||||
"expiration_time_seconds": "str",
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_map = {
|
attribute_map = {
|
||||||
"maker_address": "makerAddress",
|
"maker_address": "makerAddress",
|
@ -1,98 +0,0 @@
|
|||||||
"""Test the default api client"""
|
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from sra_client import ApiClient, Configuration
|
|
||||||
from sra_client.api import DefaultApi
|
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultApi(unittest.TestCase):
|
|
||||||
"""DefaultApi unit test stubs"""
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
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 = {
|
|
||||||
"records": [
|
|
||||||
{
|
|
||||||
"assetDataA": {
|
|
||||||
"assetData": "0xf47261b00000000000000000000000000"
|
|
||||||
"b1ba0af832d7c05fd64161e0db78e85978e8082",
|
|
||||||
"maxAmount": "115792089237316195423570985008687907853"
|
|
||||||
"269984665640564039457584007913129639936",
|
|
||||||
"minAmount": "0",
|
|
||||||
"precision": 18,
|
|
||||||
},
|
|
||||||
"assetDataB": {
|
|
||||||
"assetData": "0xf47261b0000000000000000000000000"
|
|
||||||
"871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c",
|
|
||||||
"maxAmount": "115792089237316195423570985008687907853"
|
|
||||||
"269984665640564039457584007913129639936",
|
|
||||||
"minAmount": "0",
|
|
||||||
"precision": 18,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
actual = self.api.get_asset_pairs()
|
|
||||||
|
|
||||||
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__":
|
|
||||||
unittest.main()
|
|
Loading…
x
Reference in New Issue
Block a user