diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index 9ddafbeead..25219022e8 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -10,6 +10,7 @@ from distutils.command.clean import clean import distutils.command.build_py from setuptools import find_packages, setup +from setuptools.command.test import test as TestCommand class LintCommand(distutils.command.build_py.build_py): @@ -30,8 +31,6 @@ def run(self): "mypy src setup.py".split(), # security issue checker: "bandit -r src ./setup.py".split(), - # run doctests: - "pytest --doctest-modules".split(), # general linter: "pylint src setup.py".split(), # pylint takes relatively long to run, so it runs last, to enable @@ -103,6 +102,16 @@ def run(self): 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: README_MD = file_handle.read() @@ -122,6 +131,7 @@ def run(self): cmdclass={ "clean": CleanCommandExtension, "lint": LintCommand, + "test": TestCommandExtension, "test_publish": TestPublishCommand, "publish": PublishCommand, "publish_docs": PublishDocsCommand, diff --git a/python-packages/contract_addresses/src/index.rst b/python-packages/contract_addresses/src/index.rst index 7ac329ce2e..5dd096b16a 100644 --- a/python-packages/contract_addresses/src/index.rst +++ b/python-packages/contract_addresses/src/index.rst @@ -7,15 +7,20 @@ Python zero_ex.contract_addresses :maxdepth: 2 :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 :members: + :show-inheritance: .. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES - :annotation: + :annotation: : Dict[NetworkId, ContractAddresses] Indices and tables ================== diff --git a/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py b/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py index 225a0d18a5..d9814f088b 100644 --- a/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py +++ b/python-packages/contract_addresses/src/zero_ex/contract_addresses/__init__.py @@ -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 typing import Dict, NamedTuple -class ContractAddresses(NamedTuple): # noqa +class ContractAddresses(NamedTuple): """An abstract record listing all the contracts that have addresses.""" erc20_proxy: str + """Address of the ERC20Proxy contract.""" + erc721_proxy: str + """Address of the ERC20Proxy contract.""" + zrx_token: str + """Address of the ZRX token contract.""" + ether_token: str + """Address of the WETH token contract.""" + exchange: str + """Address of the Exchange contract.""" + asset_proxy_owner: str + """Address of the AssetProxyOwner contract.""" + forwarder: str + """Address of the Forwarder contract.""" + order_validator: str + """Address of the OrderValidator contract.""" + coordinator_registry: str + """Address of the CoordinatorRegistry contract.""" + coordinator: str + """Address of the Coordinator contract.""" class NetworkId(Enum): @@ -24,6 +52,9 @@ class NetworkId(Enum): >>> NetworkId.MAINNET + + >>> NetworkId.MAINNET.value + 1 """ MAINNET = 1 @@ -98,7 +129,7 @@ class NetworkId(Enum): """A mapping from instances of NetworkId to instances of ContractAddresses. Addresses under NetworkId.Ganache are from our Ganache snapshot generated from -migrations. +npm package @0x/migrations. >>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange 0x4f833a24e1f95d70f028921e27040ca56e09ab0b diff --git a/python-packages/sra_client/doc_static/.gitkeep b/python-packages/contract_addresses/stubs/pytest/__init__.pyi similarity index 100% rename from python-packages/sra_client/doc_static/.gitkeep rename to python-packages/contract_addresses/stubs/pytest/__init__.pyi diff --git a/python-packages/contract_artifacts/setup.py b/python-packages/contract_artifacts/setup.py index 8d93fc3fab..05e7819915 100755 --- a/python-packages/contract_artifacts/setup.py +++ b/python-packages/contract_artifacts/setup.py @@ -10,6 +10,7 @@ from distutils.command.clean import clean import distutils.command.build_py from setuptools import find_packages, setup +from setuptools.command.test import test as TestCommand class LintCommand(distutils.command.build_py.build_py): @@ -110,6 +111,16 @@ def run(self): 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: README_MD = file_handle.read() @@ -129,6 +140,7 @@ def run(self): cmdclass={ "clean": CleanCommandExtension, "lint": LintCommand, + "test": TestCommandExtension, "test_publish": TestPublishCommand, "publish": PublishCommand, "publish_docs": PublishDocsCommand, diff --git a/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/__init__.py b/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/__init__.py index 18947ee3bc..3c60f5fe55 100644 --- a/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/__init__.py +++ b/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/__init__.py @@ -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 from typing import Dict @@ -31,5 +39,21 @@ def contract_name_to_abi(cls, 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) diff --git a/python-packages/contract_artifacts/stubs/pytest/__init__.pyi b/python-packages/contract_artifacts/stubs/pytest/__init__.pyi new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-packages/contract_wrappers/.pylintrc b/python-packages/contract_wrappers/.pylintrc index 85011fdcfe..f7c1a4d9d3 100644 --- a/python-packages/contract_wrappers/.pylintrc +++ b/python-packages/contract_wrappers/.pylintrc @@ -1,4 +1,6 @@ [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`. -min-similarity-lines=10 + +[SIMILARITIES] +min-similarity-lines=6 diff --git a/python-packages/contract_wrappers/src/index.rst b/python-packages/contract_wrappers/src/index.rst index 38750dbb12..6fe9885d55 100644 --- a/python-packages/contract_wrappers/src/index.rst +++ b/python-packages/contract_wrappers/src/index.rst @@ -1,5 +1,5 @@ Python zero_ex.contract_wrappers -================================================ +================================ .. toctree:: :maxdepth: 2 @@ -7,27 +7,26 @@ Python zero_ex.contract_wrappers .. automodule:: zero_ex.contract_wrappers :members: - :inherited-members: zero_ex.contract_wrappers.Exchange ----------------------------------- +================================== .. autoclass:: zero_ex.contract_wrappers.Exchange :members: - :inherited-members: + :special-members: zero_ex.contract_wrappers.ERC20Token -------------------------------------- +==================================== .. autoclass:: zero_ex.contract_wrappers.ERC20Token :members: - :inherited-members: + :special-members: zero_ex.contract_wrappers.TxParams ----------------------------------- +================================== .. autoclass:: zero_ex.contract_wrappers.TxParams :members: diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py index b34f070df4..6290097216 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py @@ -1,350 +1,290 @@ """Python wrappers for interacting with 0x smart contracts. -The smart contract wrappers have simplified interfaces, -and perform client-side validation on transactions and throw -helpful error messages. +The smart contract wrappers have simplified interfaces, performing client-side +validation on transactions, and throwing helpful error messages. -Installing -========== -Install the 0x-contract-wrappers with pip: +Setup +----- -``pip install 0x-contract-wrappers`` +Install the 0x-contract-wrappers with pip:: -Demo -==== -We will demonstrate some basic steps to help you get started trading on 0x. + pip install 0x-contract-wrappers -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 +`_. The examples below +assume there's a local instance of Ganache listening on port 8545: + +>>> from web3 import HTTPProvider +>>> ganache = HTTPProvider("http://localhost:8545") + +To replicate these examples, one can use the `0xorg/ganache-cli`:code: docker +image, which comes with the 0x contracts pre-deployed. To start it:: + + docker run docker run -d -p 8545:8545 0xorg/ganache-cli + +Accounts +-------- + +In the examples below, we will use the accounts provided by Ganache, which are +accessible through the Web3 instance. The first account will be the maker, and +the second account will be the taker. + +>>> from web3 import Web3 +>>> accounts = Web3(ganache).eth.accounts +>>> maker_address = accounts[0].lower() +>>> taker_address = accounts[1].lower() + +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: + +>>> from zero_ex.contract_wrappers import TxParams + +Contract Addresses ------------------ -The first step to interact with the 0x smart contract is to import -the following relevant packages: +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: ->>> import random ->>> from eth_utils import to_checksum_address >>> 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) +>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token +>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token +>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange -Provider --------- +Wrapping ETH +------------ -We need a web3 provider to allow us to talk to the blockchain. You can -read more about providers -`here `__. In our -case, we are using our local node (ganache), we will connect to our provider -at http://localhost:8545. - ->>> from web3 import HTTPProvider, Web3 ->>> provider = HTTPProvider("http://localhost:8545") ->>> # Create a web3 instance from the provider ->>> web3_instance = Web3(provider) - -Declaring Decimals and Addresses ---------------------------------- - -Since we are dealing with a few contracts, we will specify them now to -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: - ->>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token ->>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token - -Approvals and WETH Balance --------------------------- - -To trade on 0x, the participants (maker and taker) require a small -amount of initial set up. They need to approve the 0x smart contracts -to move funds on their behalf. In order to give 0x protocol smart contract -access to funds, we need to set allowances (you can read about allowances -`here `__). -In our demo the taker asset is WETH (or Wrapped ETH, you can read about WETH -`here `__)., -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 -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), +The examples below demonstrate constructing an order with the maker providing +ZRX in exchange for the taker providing some WETH. For the order to be valid, +our Taker first needs to wrap some ether as WETH. + +First get an instance of the WETH contract on the network: + +>>> from zero_ex.contract_artifacts import abi_by_name +>>> weth_instance = Web3(ganache).eth.contract( +... address=Web3.toChecksumAddress(weth_address), +... abi=abi_by_name("WETH9") ... ) ->>> # Check the allowance given to the 0x ERC20 Proxy ->>> maker_allowance = erc20_wrapper.allowance( -... weth_address, -... maker, -... erc20_proxy, + +Then have the Taker deposit some ETH into that contract, which will result in +it receiving WETH: + +>>> from eth_utils import to_wei +>>> weth_instance.functions.deposit().transact( +... {"from": Web3.toChecksumAddress(taker_address), +... "value": to_wei(1, 'ether')} ... ) ->>> (maker_allowance) // 10 ** 18 -100 +HexBytes('0x...') + +Approvals +--------- + +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: + +>>> from zero_ex.contract_wrappers import ERC20Token +>>> erc20_wrapper = ERC20Token(ganache) + +>>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy ->>> # Set allowance to the erc20_proxy from taker account >>> tx = erc20_wrapper.approve( -... weth_address, -... erc20_proxy, -... ALLOWANCE, -... tx_params=TxParams(from_=taker), +... zrx_address, +... erc20_proxy_addr, +... to_wei(100, 'ether'), +... tx_params=TxParams(from_=maker_address), ... ) ->>> # Check the allowance given to the 0x ERC20 Proxy ->>> taker_allowance = erc20_wrapper.allowance( + +>>> tx = erc20_wrapper.approve( ... weth_address, -... taker, -... erc20_proxy, +... erc20_proxy_addr, +... to_wei(100, 'ether'), +... tx_params=TxParams(from_=taker_address), ... ) ->>> (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. +Constructing an order +--------------------- ->>> from zero_ex.contract_artifacts import abi_by_name ->>> # Converting 0.5 ETH to base unit wei ->>> deposit_amount = int(0.5 * 10 ** 18) - ->>> # Let's have our maker wrap 1 ETH for 1 WETH ->>> tx = erc20_wrapper.execute_method( -... 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 ->>> tx = erc20_wrapper.execute_method( -... address=weth_address, -... abi=abi_by_name("WETH9"), -... method="deposit", -... tx_params=TxParams(from_=taker, value=deposit_amount)) ->>> # 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! - -Signing an order ----------------- +>>> from zero_ex.order_utils import asset_data_utils, Order +>>> 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() +... ) +... ) -Here is an example of a JSON order previously generated by our maker -to sell 0.1 WETH. To confirm his intent to sell and recieve the described -token amounts in this order, our maker must first sign the order by -creating a signature with the given order data. - ->>> maker -'0x5409ED021D9299bf6814279A6A1411A7e866A631' - ->>> example_order = { -... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', -... 'takerAddress': '0x0000000000000000000000000000000000000000', -... 'senderAddress': '0x0000000000000000000000000000000000000000', -... 'exchangeAddress': '0x48bacb9266a570d521063ef5dd96e61686dbe788', -... '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 -`__ -if you would like to see how you can create an 0x order -with our python packages. - -To sign this order, we first need to generate the order hash. - ->>> order_hash = generate_order_hash_hex( -... example_order, example_order["exchangeAddress"]) - -Now our maker can sign this order hash with our web3 provider and -the `sign_hash` function from the order utils package. +For this order to be valid, our Maker must sign a hash of it: +>>> 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( -... 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 -directly to the taker, or he can choose to broadcast the order -with his signature to a 0x-relayer. +Now our Maker can either deliver this order, along with his signature, directly +to the taker, or he can choose to broadcast the order to a 0x Relayer. For +more information on working with Relayers, see `the documentation for +0x-sra-client `_. Filling an order ---------------- -We finally have a valid order! We can now have our taker try -to fill the example order. The *takerAssetAmount* is simply the -amount of tokens (in our case WETH) the taker wants to fill. -For this demonstration, we will be completely filling the order. -Orders may also be partially filled. +Now our Taker will fill the order. The `takerAssetAmount`:code: parameter +specifies the amount of tokens (in this case WETH) that the taker wants to +fill. This example fills the order completely, but partial fills are possible +too. -Now let's fill the example order: - ->>> # Instantiate an instance of the exchange_wrapper with ->>> # the provider ->>> zero_ex_exchange = Exchange(provider) ->>> tx_hash = zero_ex_exchange.fill_order( -... order=example_order, -... taker_amount=example_order["takerAssetAmount"], +>>> from zero_ex.contract_wrappers import Exchange +>>> exchange_contract = Exchange(ganache) +>>> tx_hash = exchange_contract.fill_order( +... order=order, +... taker_amount=order["takerAssetAmount"], ... signature=maker_signature, -... tx_params=TxParams(from_=taker)) - -Once the transaction is mined, we can get the details of -our exchange through the exchange wrapper. +... tx_params=TxParams(from_=taker_address) +... ) ->>> fill_event = zero_ex_exchange.get_fill_event(tx_hash) ->>> taker_filled_amount = fill_event[0].args.takerAssetFilledAmount ->>> taker_filled_amount / 10 ** 18 -0.1 +Once the transaction is mined, we can get the details of our exchange through +the exchange wrapper: + +>>> exchange_contract.get_fill_event(tx_hash) +(AttributeDict({'args': ...({'makerAddress': ...}), 'event': 'Fill', ...}),) +>>> from pprint import pprint +>>> 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 -------------------- -Now we will show how to cancel an order if the maker no -long wishes to exchange his WETH tokens. We will use a second example -order to demonstrate. - ->>> example_order_2 = { -... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', -... 'takerAddress': '0x0000000000000000000000000000000000000000', -... 'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', -... 'senderAddress': '0x0000000000000000000000000000000000000000', -... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', -... 'makerAssetData': bytes.fromhex( -... 'f47261b0000000000000000000000000' -... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), -... 'takerAssetData': bytes.fromhex( -... 'f47261b0000000000000000000000000' -... 'e41d2489571d322189246dafa5ebde1f4699f498'), -... 'salt': random.randint(1, 100000000000000000), -... 'makerFee': 0, -... 'takerFee': 0, -... '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 -our cancellation through the exchange wrapper. - ->>> cancel_event = zero_ex_exchange.get_cancel_event(tx_hash); ->>> cancelled_order_hash = cancel_event[0].args.orderHash.hex() +A Maker can cancel an order that has yet to be filled. + +>>> order = Order( +... makerAddress=maker_address, +... takerAddress='0x0000000000000000000000000000000000000000', +... exchangeAddress=exchange_address, +... senderAddress='0x0000000000000000000000000000000000000000', +... feeRecipientAddress='0x0000000000000000000000000000000000000000', +... makerAssetData=asset_data_utils.encode_erc20(weth_address), +... takerAssetData=asset_data_utils.encode_erc20(weth_address), +... salt=random.randint(1, 100000000000000000), +... makerFee=0, +... takerFee=0, +... makerAssetAmount=1000000000000000000, +... takerAssetAmount=500000000000000000000, +... expirationTimeSeconds=round( +... (datetime.utcnow() + timedelta(days=1)).timestamp() +... ) +... ) + +>>> tx_hash = exchange_contract.cancel_order( +... order=order, tx_params=TxParams(from_=maker_address) +... ) + +Once the transaction is mined, we can get the details of the cancellation +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 ---------------- -The 0x exchange contract can also process multiple orders at -the same time. Here is an example where the taker fills -two orders in one transaction. - ->>> order_1 = { -... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', -... 'takerAddress': '0x0000000000000000000000000000000000000000', -... 'senderAddress': '0x0000000000000000000000000000000000000000', -... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', -... 'makerAssetData': bytes.fromhex( -... 'f47261b0000000000000000000000000' -... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), -... 'takerAssetData': bytes.fromhex( -... 'f47261b0000000000000000000000000' -... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), -... 'salt': random.randint(1, 100000000000000000), -... 'makerFee': 0, -... 'takerFee': 0, -... 'makerAssetAmount': 100, -... 'takerAssetAmount': 100, -... 'expirationTimeSeconds': 1000000000000000000} ->>> order_hash_1 = generate_order_hash_hex( -... order_1, zero_ex_exchange.address) +The Exchange contract can also process multiple orders at the same time. Here +is an example where the taker fills two orders in one transaction: + +>>> order_1 = 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=100, +... takerAssetAmount=100, +... expirationTimeSeconds=round( +... (datetime.utcnow() + timedelta(days=1)).timestamp() +... ) +... ) >>> signature_1 = sign_hash( -... provider, to_checksum_address(maker), order_hash_1) ->>> order_2 = { -... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', -... 'takerAddress': '0x0000000000000000000000000000000000000000', -... 'senderAddress': '0x0000000000000000000000000000000000000000', -... 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', -... 'makerAssetData': bytes.fromhex( -... 'f47261b0000000000000000000000000' -... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), -... 'takerAssetData': bytes.fromhex( -... 'f47261b0000000000000000000000000' -... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'), -... 'salt': random.randint(1, 100000000000000000), -... 'makerFee': 0, -... 'takerFee': 0, -... 'makerAssetAmount': 200, -... 'takerAssetAmount': 200, -... 'expirationTimeSeconds': 2000000000000000000} ->>> order_hash_2 = generate_order_hash_hex( -... order_2, zero_ex_exchange.address) +... ganache, +... Web3.toChecksumAddress(maker_address), +... generate_order_hash_hex(order_1, exchange_contract.address) +... ) +>>> order_2 = 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=200, +... takerAssetAmount=200, +... expirationTimeSeconds=round( +... (datetime.utcnow() + timedelta(days=1)).timestamp() +... ) +... ) >>> 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: ->>> tx_hash = zero_ex_exchange.batch_fill_orders( +>>> exchange_contract.batch_fill_orders( ... orders=[order_1, order_2], ... taker_amounts=[1, 2], ... signatures=[signature_1, signature_2], -... tx_params=TxParams(from_=taker)) +... tx_params=TxParams(from_=taker_address)) +HexBytes('0x...') """ from .tx_params import TxParams diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py index b5986d630b..d85fb10893 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py @@ -95,6 +95,7 @@ def _sign_and_send_raw_direct(self, func, tx_params): ) return self._web3_eth.sendRawTransaction(signed_tx.rawTransaction) + # pylint: disable=too-many-arguments def execute_method( self, address: str, diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_wrapper.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_wrapper.py index 9763e40390..a3d76a38f0 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_wrapper.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_wrapper.py @@ -13,12 +13,7 @@ class ERC20Token(BaseContractWrapper): - """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 - """ + """Wrapper class for Ethereum ERC20 smart contract.""" def __init__( self, @@ -26,7 +21,10 @@ def __init__( account_address: 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__( provider=provider, account_address=account_address, @@ -36,14 +34,13 @@ def __init__( def _erc20(self, token_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 """ return self._contract_instance( address=token_address, abi=abi_by_name("ERC20Token") ) + # pylint: disable=too-many-arguments def transfer( self, token_address: str, @@ -54,12 +51,9 @@ def transfer( ) -> Union[HexBytes, bytes]: """Transfer the balance from owner's account to another account. - :param token_address: string address of token smart contract - :param to_address: string address of receiver - :param value: integer amount to send in Wei base unit - :param tx_params: default None, dict of transaction options - :param view_only: default False, boolean of whether to transact or - view only + :param value: integer amount to send + :param tx_params: transaction parameters + :param view_only: whether to use transact() or call() :returns: transaction hash """ @@ -72,6 +66,7 @@ def transfer( func=func, tx_params=tx_params, view_only=view_only ) + # pylint: disable=too-many-arguments def approve( self, token_address: str, @@ -80,14 +75,11 @@ def approve( tx_params: Optional[TxParams] = None, view_only: bool = False, ) -> 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 spender_address: string address of receiver - :param value: integer amount of allowance in Wei base unit - :param tx_params: default None, dict of transaction options - :param view_only: default False, boolean of whether to transact or - view only + :param value: amount of allowance + :param tx_params: transaction options + :param view_only: whether to use transact() or call() :returns: transaction hash """ @@ -102,6 +94,7 @@ def approve( func=func, tx_params=tx_params, view_only=view_only ) + # pylint: disable=too-many-arguments def transfer_from( self, token_address: str, @@ -111,19 +104,16 @@ def transfer_from( tx_params: Optional[TxParams] = None, view_only: bool = False, ) -> 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` - with your address as the `spender_address`. + Note that the `authorized_address`:code: must have already called + `approve`:code: for the `spender_address`:code:. - :param token_address: string address of token smart contract - :param authorized_address: string address you have been authorized to - to transfer tokens from - :param to_address: string address of receiver - :param value: integer amount to send in Wei base unit - :param tx_params: default None, dict of transaction options - :param view_only: default False, boolean of whether to transact or - view only + :param authorized_address: address you have been authorized to transfer + tokens from + :param value: amount to send + :param tx_params: transaction parameters + :param view_only: whether to use transact() or call() :returns: transaction hash """ @@ -144,9 +134,7 @@ def transfer_from( def total_supply(self, token_address: str) -> int: """Get total supply of a given ERC20 Token. - :param token_address: string address of token smart contract - - :returns: integer amount of tokens in Wei + :returns: amount of tokens """ token_address = self._validate_and_checksum_address(token_address) func = self._erc20(token_address).functions.totalSupply() @@ -157,10 +145,7 @@ def total_supply(self, token_address: str) -> int: def balance_of(self, token_address: str, owner_address: str) -> int: """Get token balance of a given owner address. - :param token_address: string address of token smart contract - :param owner_address: string address of owner to check balance for - - :returns: integer amount of tokens in Wei the owner has + :returns: amount of tokens """ token_address = self._validate_and_checksum_address(token_address) owner_address = self._validate_and_checksum_address(owner_address) @@ -174,12 +159,7 @@ def allowance( ) -> Union[HexBytes, bytes]: """Get the amount of tokens approved for a spender. - :param token_address: string address of token smart contract - :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 + :returns: amount of tokens """ token_address = self._validate_and_checksum_address(token_address) owner_address = self._validate_and_checksum_address(owner_address) @@ -196,8 +176,7 @@ def get_transfer_event( ) -> Tuple[AttributeDict]: """Get the result of a transfer from its transaction hash. - :param token_address: string address of token smart contract - :param tx_hash: `HexBytes` hash of transfer transaction + :param tx_hash: hash of transfer transaction """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) token_address = self._validate_and_checksum_address(token_address) @@ -212,8 +191,7 @@ def get_approval_event( ) -> Tuple[AttributeDict]: """Get the result of an approval event from its transaction hash. - :param token_address: string address of token smart contract - :param tx_hash: `HexBytes` hash of approval transaction + :param tx_hash: hash of approval transaction """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) token_address = self._validate_and_checksum_address(token_address) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange_wrapper.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange_wrapper.py index fbdf8d63cd..e936ba66b4 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange_wrapper.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange_wrapper.py @@ -27,12 +27,7 @@ class CancelDisallowedError(Exception): class Exchange(BaseContractWrapper): - """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 - """ + """Wrapper class for 0x Exchange smart contract.""" def __init__( self, @@ -40,7 +35,12 @@ def __init__( account_address: 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__( provider=provider, account_address=account_address, @@ -54,6 +54,7 @@ def __init__( address=self.address, abi=abi_by_name("Exchange") ) + # pylint: disable=too-many-arguments def fill_order( self, order: Order, @@ -82,7 +83,7 @@ def fill_order( :param view_only: default False, boolean of whether to transact or view only - :returns: `HexBytes` transaction hash + :returns: transaction hash """ assert_valid(order_to_jsdict(order, self.address), "/orderSchema") is_valid_signature( @@ -101,6 +102,7 @@ def fill_order( func=func, tx_params=tx_params, view_only=view_only ) + # pylint: disable=too-many-arguments def batch_fill_orders( self, orders: List[Order], @@ -118,7 +120,7 @@ def batch_fill_orders( :param view_only: default False, boolean of whether to transact or view only - :returns: `HexBytes` transaction hash + :returns: transaction hash """ order_jsdicts = [ order_to_jsdict(order, self.address) for order in orders @@ -139,6 +141,7 @@ def batch_fill_orders( func=func, tx_params=tx_params, view_only=view_only ) + # pylint: disable=too-many-arguments def fill_or_kill_order( self, order: Order, @@ -156,7 +159,7 @@ def fill_or_kill_order( :param view_only: default False, boolean of whether to transact or view only - :returns: `HexBytes` transaction hash + :returns: transaction hash """ assert_valid(order_to_jsdict(order, self.address), "/orderSchema") is_valid_signature( @@ -175,6 +178,7 @@ def fill_or_kill_order( func=func, tx_params=tx_params, view_only=view_only ) + # pylint: disable=too-many-arguments def batch_fill_or_kill_orders( self, orders: List[Order], @@ -192,7 +196,7 @@ def batch_fill_or_kill_orders( :param view_only: default False, boolean of whether to transact or view only - :returns: `HexBytes` transaction hash + :returns: transaction hash """ order_jsdicts = [ order_to_jsdict(order, self.address) for order in orders @@ -230,7 +234,7 @@ def cancel_order( :param view_only: default False, boolean of whether to transact or view only - :returns: `HexBytes` transaction hash + :returns: transaction hash """ assert_valid(order_to_jsdict(order, self.address), "/orderSchema") maker_address = self._validate_and_checksum_address( @@ -264,7 +268,7 @@ def batch_cancel_orders( :param view_only: default False, boolean of whether to transact or view only - :returns: `HexBytes` transaction hash + :returns: transaction hash """ order_jsdicts = [ order_to_jsdict(order, self.address) for order in orders @@ -298,7 +302,7 @@ def get_fill_event( :param tx_hash: `HexBytes` hash of fill transaction - :returns: tuple of `FillResults`. + :returns: fill event """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) return self._exchange.events.Fill().processReceipt(tx_receipt) diff --git a/python-packages/contract_wrappers/test/conftest.py b/python-packages/contract_wrappers/test/conftest.py index 789ca639ed..206909dabc 100644 --- a/python-packages/contract_wrappers/test/conftest.py +++ b/python-packages/contract_wrappers/test/conftest.py @@ -1,7 +1,7 @@ """Fixtures for 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 zero_ex.order_utils import asset_data_utils @@ -48,11 +48,7 @@ def weth_address(): @pytest.fixture(scope="module") def weth_asset_data(weth_address): # pylint: disable=redefined-outer-name """Get 0x asset data for Wrapped Ether (WETH) token.""" - return bytes.fromhex( - remove_0x_prefix( - asset_data_utils.encode_erc20_asset_data(weth_address) - ) - ) + return asset_data_utils.encode_erc20(weth_address) @pytest.fixture(scope="module") @@ -74,6 +70,4 @@ def zrx_address(): @pytest.fixture(scope="module") def zrx_asset_data(zrx_address): # pylint: disable=redefined-outer-name """Get 0x asset data for ZRX token.""" - return bytes.fromhex( - remove_0x_prefix(asset_data_utils.encode_erc20_asset_data(zrx_address)) - ) + return asset_data_utils.encode_erc20(zrx_address) diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index 389d145913..8fc13b0e22 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -159,6 +159,7 @@ def run(self): "pylint", "pytest", "sphinx", + "sphinx-autodoc-typehints", "tox", "twine", ] diff --git a/python-packages/json_schemas/src/conf.py b/python-packages/json_schemas/src/conf.py index 1ae1493e36..e8dbddacc4 100644 --- a/python-packages/json_schemas/src/conf.py +++ b/python-packages/json_schemas/src/conf.py @@ -22,6 +22,7 @@ "sphinx.ext.intersphinx", "sphinx.ext.coverage", "sphinx.ext.viewcode", + "sphinx_autodoc_typehints", ] templates_path = ["doc_templates"] diff --git a/python-packages/json_schemas/src/zero_ex/json_schemas/__init__.py b/python-packages/json_schemas/src/zero_ex/json_schemas/__init__.py index de7507016b..b9345a009f 100644 --- a/python-packages/json_schemas/src/zero_ex/json_schemas/__init__.py +++ b/python-packages/json_schemas/src/zero_ex/json_schemas/__init__.py @@ -1,30 +1,12 @@ -"""JSON schemas and associated utilities. - -Validating a 0x Order ---------------------- - -Here is an example on how to validate a 0x order. - ->>> from zero_ex.json_schemas import assert_valid ->>> example_order = { -... 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', -... 'takerAddress': '0x0000000000000000000000000000000000000000', -... 'senderAddress': '0x0000000000000000000000000000000000000000', -... 'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', -... 'feeRecipientAddress': -... '0x0000000000000000000000000000000000000000', -... 'makerAssetData': '0xf47261b0000000000000000000000000' -... 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', -... 'takerAssetData': '0xf47261b0000000000000000000000000' -... 'e41d2489571d322189246dafa5ebde1f4699f498', -... 'salt': 123456789, -... 'makerFee': 0, -... 'takerFee': 0, -... 'makerAssetAmount': 1000000000000000000, -... 'takerAssetAmount': 500000000000000000000, -... 'expirationTimeSeconds': 1553553429 -... } ->>> assert_valid(example_order, "/orderSchema") +"""0x JSON schemas and associated utilities. + +Setup +----- + +Install the package with pip:: + + pip install 0x-json-schemas + """ from os import path @@ -77,13 +59,37 @@ def assert_valid(data: Mapping, schema_id: str) -> None: Raises an exception if validation fails. - >>> assert_valid( - ... {'v': 27, 'r': '0x'+'f'*64, 's': '0x'+'f'*64}, - ... '/ecSignatureSchema', + >>> from zero_ex.json_schemas import assert_valid + >>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId + >>> 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) 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. >>> assert_valid_json( - ... r'''{ + ... '''{ ... "v": 27, ... "r": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ... "s": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" diff --git a/python-packages/middlewares/.discharge.json b/python-packages/middlewares/.discharge.json new file mode 100644 index 0000000000..352aa16499 --- /dev/null +++ b/python-packages/middlewares/.discharge.json @@ -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 +} diff --git a/python-packages/middlewares/src/zero_ex/middlewares/__init__.py b/python-packages/middlewares/src/zero_ex/middlewares/__init__.py index 8b5b06dc56..30b103dc78 100644 --- a/python-packages/middlewares/src/zero_ex/middlewares/__init__.py +++ b/python-packages/middlewares/src/zero_ex/middlewares/__init__.py @@ -1 +1,10 @@ -"""Web3 middlewares for 0x applications.""" +"""Web3 middlewares for 0x applications. + +Setup +----- + +Install the package with pip:: + + pip install 0x-middlewares + +""" diff --git a/python-packages/middlewares/src/zero_ex/middlewares/local_message_signer.py b/python-packages/middlewares/src/zero_ex/middlewares/local_message_signer.py index a570544f41..618446e5cd 100644 --- a/python-packages/middlewares/src/zero_ex/middlewares/local_message_signer.py +++ b/python-packages/middlewares/src/zero_ex/middlewares/local_message_signer.py @@ -60,22 +60,20 @@ def construct_local_message_signer( list, or set of private keys. Keys can be any of the following formats: - - An `eth_account.LocalAccount` object - - An `eth_keys.PrivateKey` object - - A raw private key as a hex `string` or `bytes` + - An `eth_account.LocalAccount`:code: object + - An `eth_keys.PrivateKey`:code: object + - A raw private key as a hex `string`:code: or as `bytes`:code: :returns: callable local_message_signer_middleware - :Example: - - >>> from web3 import Web3 - >>> from zero_ex.middlewares.local_message_signer import ( - ... construct_local_message_signer) - >>> WEB3_RPC_URL="https://mainnet.infura.io/v3/INFURA_API_KEY" - >>> PRIVATE_KEY=( - ... "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d") - >>> web3_instance = Web3.HTTPProvider(WEB3_RPC_URL) - >>> web3_instance.middlewares.add( - ... construct_local_message_signer(PRIVATE_KEY)) + >>> private_key=( + ... "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d" + ... ) + >>> from web3 import Web3, HTTPProvider + >>> Web3( + ... HTTPProvider("https://mainnet.infura.io/v3/API_KEY") + ... ).middleware_stack.add( + ... construct_local_message_signer(private_key) + ... ) """ if not isinstance(private_key_or_account, (list, tuple, set)): diff --git a/python-packages/middlewares/stubs/web3/__init__.pyi b/python-packages/middlewares/stubs/web3/__init__.pyi index c5aa964d3d..e18564de84 100644 --- a/python-packages/middlewares/stubs/web3/__init__.pyi +++ b/python-packages/middlewares/stubs/web3/__init__.pyi @@ -4,5 +4,7 @@ from web3.utils import datatypes from web3.providers.base import BaseProvider class Web3: - class HTTPProvider(BaseProvider): ... def __init__(self, provider: BaseProvider) -> None: ... + +class HTTPProvider(BaseProvider): + ... diff --git a/python-packages/middlewares/test/test_local_message_signer.py b/python-packages/middlewares/test/test_local_message_signer.py index d4031fc8f4..10484ffcf6 100644 --- a/python-packages/middlewares/test/test_local_message_signer.py +++ b/python-packages/middlewares/test/test_local_message_signer.py @@ -1,7 +1,8 @@ """Tests of 0x.middlewares.local_message_signer.""" 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.middlewares.local_message_signer import ( construct_local_message_signer, @@ -25,16 +26,14 @@ def test_local_message_signer__sign_order(): private_key = ( "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d" ) - web3_rpc_url = "http://127.0.0.1:8545" - web3_instance = Web3.HTTPProvider(web3_rpc_url) - web3_instance.middlewares.add(construct_local_message_signer(private_key)) + ganache = HTTPProvider("http://127.0.0.1:8545") + web3_instance = Web3(ganache) + web3_instance.middleware_stack.add( + construct_local_message_signer(private_key) + ) order = make_empty_order() order_hash = generate_order_hash_hex(order, exchange) - signature = sign_hash( - web3_instance, to_checksum_address(address), order_hash - ) + signature = sign_hash(ganache, to_checksum_address(address), order_hash) assert signature == expected_signature - is_valid = is_valid_signature( - web3_instance, order_hash, signature, address - )[0] + is_valid = is_valid_signature(ganache, order_hash, signature, address)[0] assert is_valid is True diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index fc361c23b7..0290a38132 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -181,6 +181,7 @@ def run(self): "black", "coverage", "coveralls", + "deprecated", "mypy", "mypy_extensions", "pycodestyle", diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst index ce618c6ffa..313a36c38d 100644 --- a/python-packages/order_utils/src/index.rst +++ b/python-packages/order_utils/src/index.rst @@ -21,12 +21,14 @@ zero_ex.order_utils.asset_data_utils :members: .. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData - - See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome. + :members: + :undoc-members: + :show-inheritance: .. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData - - See source for class properties. Sphinx is having problems generating docs for ``TypedDict`` declarations; pull requests welcome. + :members: + :undoc-members: + :show-inheritance: Indices and tables ================== diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py b/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py index 3fec775b05..cb062e32ad 100644 --- a/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py +++ b/python-packages/order_utils/src/zero_ex/dev_utils/abi_utils.py @@ -10,8 +10,8 @@ from mypy_extensions import TypedDict -from web3 import Web3 from eth_abi import encode_abi +from web3 import Web3 from .type_assertions import assert_is_string, assert_is_list diff --git a/python-packages/order_utils/src/zero_ex/order_utils/__init__.py b/python-packages/order_utils/src/zero_ex/order_utils/__init__.py index c1fea8c21c..6f128a8d3f 100644 --- a/python-packages/order_utils/src/zero_ex/order_utils/__init__.py +++ b/python-packages/order_utils/src/zero_ex/order_utils/__init__.py @@ -1,5 +1,12 @@ """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. For local testing one may construct such a provider pointing at an instance of `ganache-cli `_ which has the 0x @@ -7,52 +14,51 @@ just this purpose. To start it: `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. ->>> 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 ->>> NULL_ADDRESS = "0x0000000000000000000000000000000000000000" +>>> from datetime import datetime, timedelta +>>> import random >>> my_address = "0x5409ed021d9299bf6814279a6a1411a7e866a631" ->>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange ->>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token ->>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token ->>> maker_asset_data = ( -... asset_data_utils.encode_erc20_asset_data(weth_address)) ->>> taker_asset_data = ( -... asset_data_utils.encode_erc20_asset_data(zrx_address)) ->>> example_order: Order = { -... "makerAddress": my_address, -... "takerAddress": NULL_ADDRESS, -... "exchangeAddress": exchange_address, -... "senderAddress": NULL_ADDRESS, -... "feeRecipientAddress": NULL_ADDRESS, -... "makerAssetData": maker_asset_data, -... "takerAssetData": taker_asset_data, -... "salt": 123456789, -... "makerFee": 0, -... "takerFee": 0, -... "makerAssetAmount": 1 * 10 ** 18, # Converting token amount to base unit with 18 decimals -... "takerAssetAmount": 500 * 10 ** 18, # Converting token amount to base unit with 18 decimals -... "expirationTimeSeconds": 1553553429, -... } +>>> example_order = Order( +... makerAddress=my_address, +... takerAddress="0x0000000000000000000000000000000000000000", +... exchangeAddress=NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange, +... senderAddress="0x0000000000000000000000000000000000000000", +... feeRecipientAddress="0x0000000000000000000000000000000000000000", +... makerAssetData=asset_data_utils.encode_erc20( +... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token +... ), +... takerAssetData=asset_data_utils.encode_erc20( +... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token +... ), +... salt=random.randint(1, 100000000000000000), +... makerFee=0, +... takerFee=0, +... makerAssetAmount=1 * 10 ** 18, # Convert token amount to base unit with 18 decimals +... takerAssetAmount=500 * 10 ** 18, # Convert token amount to base unit with 18 decimals +... expirationTimeSeconds=round( +... (datetime.utcnow() + timedelta(days=1)).timestamp() +... ) +... ) +>>> import pprint >>> pprint.pprint(example_order) -{'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', - 'expirationTimeSeconds': 1553553429, +{'exchangeAddress': '0x...', + 'expirationTimeSeconds': ..., 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', - 'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', + 'makerAddress': '0x...', 'makerAssetAmount': 1000000000000000000, - 'makerAssetData': '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + 'makerAssetData': b..., 'makerFee': 0, - 'salt': 123456789, + 'salt': ..., 'senderAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': 500000000000000000000, - 'takerAssetData': '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498', + 'takerAssetData': b..., 'takerFee': 0} """ # noqa E501 @@ -128,7 +134,29 @@ class SignatureType(Enum): 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 """Address that created the order.""" @@ -203,11 +231,14 @@ def make_empty_order() -> Order: def order_to_jsdict( - order: Order, exchange_address="0x0000000000000000000000000000000000000000" + order: Order, + exchange_address="0x0000000000000000000000000000000000000000", + signature: str = None, ) -> dict: """Convert a Web3-compatible order struct to a JSON-schema-compatible dict. - More specifically, do explicit decoding for the `bytes`:code: fields. + More specifically, do explicit decoding for the `bytes`:code: fields, and + convert numerics to strings. >>> import pprint >>> pprint.pprint(order_to_jsdict( @@ -228,18 +259,18 @@ def order_to_jsdict( ... }, ... )) {'exchangeAddress': '0x0000000000000000000000000000000000000000', - 'expirationTimeSeconds': 1, + 'expirationTimeSeconds': '1', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x0000000000000000000000000000000000000000', - 'makerAssetAmount': 1, + 'makerAssetAmount': '1', 'makerAssetData': '0x0000000000000000000000000000000000000000', - 'makerFee': 0, - 'salt': 1, + 'makerFee': '0', + 'salt': '1', 'senderAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000', - 'takerAssetAmount': 1, + 'takerAssetAmount': '1', 'takerAssetData': '0x0000000000000000000000000000000000000000', - 'takerFee': 0} + 'takerFee': '0'} """ jsdict = cast(Dict, copy(order)) @@ -249,29 +280,43 @@ def order_to_jsdict( 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") 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. - 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 - >>> pprint.pprint(jsdict_order_to_struct( + >>> pprint.pprint(jsdict_to_order( ... { ... 'makerAddress': "0x0000000000000000000000000000000000000000", ... 'takerAddress': "0x0000000000000000000000000000000000000000", ... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000", ... 'senderAddress': "0x0000000000000000000000000000000000000000", - ... 'makerAssetAmount': 1000000000000000000, - ... 'takerAssetAmount': 1000000000000000000, - ... 'makerFee': 0, - ... 'takerFee': 0, - ... 'expirationTimeSeconds': 12345, - ... 'salt': 12345, + ... 'makerAssetAmount': "1000000000000000000", + ... 'takerAssetAmount': "1000000000000000000", + ... 'makerFee': "0", + ... 'takerFee': "0", + ... 'expirationTimeSeconds': "12345", + ... 'salt': "12345", ... 'makerAssetData': "0x0000000000000000000000000000000000000000", ... 'takerAssetData': "0x0000000000000000000000000000000000000000", ... 'exchangeAddress': "0x0000000000000000000000000000000000000000", @@ -303,6 +348,16 @@ def jsdict_order_to_struct(jsdict: dict) -> Order: 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 # silence mypy pending release of # https://github.com/python/mypy/issues/3550 diff --git a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py index f8e9bc42b7..b25d0f4f18 100644 --- a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py +++ b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py @@ -1,8 +1,9 @@ """Asset data encoding and decoding utilities.""" -from mypy_extensions import TypedDict +from typing import NamedTuple import eth_abi +from deprecated.sphinx import deprecated from zero_ex.dev_utils import abi_utils from zero_ex.dev_utils.type_assertions import assert_is_string, assert_is_int @@ -13,23 +14,30 @@ SELECTOR_LENGTH = 10 -class ERC20AssetData(TypedDict): +class ERC20AssetData(NamedTuple): """Object interface to ERC20 asset data.""" asset_proxy_id: str - """asset proxy id""" + """Asset proxy identifier.""" token_address: str + """Token address""" -class ERC721AssetData(TypedDict): +class ERC721AssetData(NamedTuple): """Object interface to ERC721 asset data.""" asset_proxy_id: str + """Asset proxy identifier.""" + token_address: str + """Token address""" + token_id: int + """Token identifier.""" +@deprecated(reason='use `"0x"+encode_erc20().hex()` instead') def encode_erc20_asset_data(token_address: str) -> str: """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: """Decode an ERC20 asset data hex string. :param asset_data: String produced by prior call to encode_erc20_asset_data() >>> 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) 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:]) )[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: """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: """Decode an ERC721 asset data hex string. >>> 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) 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:]) ) - return { - "asset_proxy_id": asset_proxy_id, - "token_address": token_address, - "token_id": token_id, - } + return ERC721AssetData( + asset_proxy_id=asset_proxy_id, + token_address=token_address, + token_id=token_id, + ) diff --git a/python-packages/order_utils/stubs/deprecated/__init__.pyi b/python-packages/order_utils/stubs/deprecated/__init__.pyi new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-packages/order_utils/stubs/deprecated/sphinx/__init__.pyi b/python-packages/order_utils/stubs/deprecated/sphinx/__init__.pyi new file mode 100644 index 0000000000..17abdffde4 --- /dev/null +++ b/python-packages/order_utils/stubs/deprecated/sphinx/__init__.pyi @@ -0,0 +1,2 @@ +def deprecated(*args, **kwargs): + ... diff --git a/python-packages/sra_client/.discharge.json b/python-packages/sra_client/.discharge.json new file mode 100644 index 0000000000..ce2e1558c6 --- /dev/null +++ b/python-packages/sra_client/.discharge.json @@ -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 +} diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index 091398343a..404c3d9810 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -97,17 +97,20 @@ class LintCommand(distutils.command.build_py.build_py): def run(self): """Run linter shell commands.""" + lint_targets = "test src/zero_ex/sra_client/__init__.py setup.py" lint_commands = [ # 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): - "pycodestyle test sra_client/__init__.py setup.py".split(), + f"pycodestyle {lint_targets}".split(), # docstring style checker: - "pydocstyle src test sra_client/__init__.py setup.py".split(), + f"pydocstyle {lint_targets}".split(), # static type checker: - "bandit -r test sra_client/__init__.py setup.py".split(), + f"bandit -r {lint_targets}".split(), # 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 # fast failures. ] @@ -134,7 +137,7 @@ class PublishDocsCommand(distutils.command.build_py.build_py): description = ( "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): @@ -150,7 +153,9 @@ def run(self): url="https://github.com/0xproject/0x-monorepo/python-packages/sra_client", keywords=["OpenAPI", "OpenAPI-Generator", "Standard Relayer REST API"], install_requires=REQUIRES, - packages=find_packages(), + namespace_packages=["zero_ex"], + packages=find_packages("src"), + package_dir={"": "src"}, include_package_data=True, long_description=README_MD, long_description_content_type="text/markdown", @@ -183,7 +188,7 @@ def run(self): }, command_options={ "build_sphinx": { - "source_dir": ("setup.py", "."), + "source_dir": ("setup.py", "src"), "build_dir": ("setup.py", "build/docs"), } }, diff --git a/python-packages/sra_client/sra_client/__init__.py b/python-packages/sra_client/sra_client/__init__.py deleted file mode 100644 index 8e3f4a5144..0000000000 --- a/python-packages/sra_client/sra_client/__init__.py +++ /dev/null @@ -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 `_ -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 -`_ -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 -`_ -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 diff --git a/python-packages/sra_client/sra_client/models/__init__.py b/python-packages/sra_client/sra_client/models/__init__.py deleted file mode 100644 index 5e6d1baa33..0000000000 --- a/python-packages/sra_client/sra_client/models/__init__.py +++ /dev/null @@ -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 diff --git a/python-packages/sra_client/conf.py b/python-packages/sra_client/src/conf.py similarity index 100% rename from python-packages/sra_client/conf.py rename to python-packages/sra_client/src/conf.py diff --git a/python-packages/sra_client/src/doc_static/.gitkeep b/python-packages/sra_client/src/doc_static/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python-packages/sra_client/index.rst b/python-packages/sra_client/src/index.rst similarity index 53% rename from python-packages/sra_client/index.rst rename to python-packages/sra_client/src/index.rst index 27c814b316..ee65300a57 100644 --- a/python-packages/sra_client/index.rst +++ b/python-packages/sra_client/src/index.rst @@ -1,20 +1,18 @@ .. source for the sphinx-generated build/docs/web/index.html -Python zero_ex.sra_client.api_client -==================================== +Python zero_ex.sra_client +========================= .. toctree:: :maxdepth: 2 :caption: Contents: -.. automodule:: sra_client +.. automodule:: zero_ex.sra_client ----- +zero_ex.sra_client.DefaultApi +============================= -API ---- - -.. automodule:: sra_client.api.default_api +.. autoclass:: zero_ex.sra_client.DefaultApi :members: Indices and tables diff --git a/python-packages/sra_client/src/zero_ex/__init__.py b/python-packages/sra_client/src/zero_ex/__init__.py new file mode 100644 index 0000000000..e90d833db6 --- /dev/null +++ b/python-packages/sra_client/src/zero_ex/__init__.py @@ -0,0 +1,2 @@ +"""0x Python API.""" +__import__("pkg_resources").declare_namespace(__name__) diff --git a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py new file mode 100644 index 0000000000..e09ce3f78b --- /dev/null +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -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) +`_ 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 +`_ 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 `_ 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 +`_ 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 diff --git a/python-packages/sra_client/sra_client/api/__init__.py b/python-packages/sra_client/src/zero_ex/sra_client/api/__init__.py similarity index 60% rename from python-packages/sra_client/sra_client/api/__init__.py rename to python-packages/sra_client/src/zero_ex/sra_client/api/__init__.py index b7b206bb16..01214f1cba 100644 --- a/python-packages/sra_client/sra_client/api/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/api/__init__.py @@ -3,4 +3,4 @@ # flake8: noqa # import apis into api package -from sra_client.api.default_api import DefaultApi +from zero_ex.sra_client.api.default_api import DefaultApi diff --git a/python-packages/sra_client/sra_client/api/default_api.py b/python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py similarity index 91% rename from python-packages/sra_client/sra_client/api/default_api.py rename to python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py index 4e1fac032b..89d003202e 100644 --- a/python-packages/sra_client/sra_client/api/default_api.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py @@ -8,8 +8,8 @@ # python 2 and python 3 compatibility library import six -from sra_client.api_client import ApiClient -from sra_client.models.relayer_api_order_config_payload_schema import ( +from zero_ex.sra_client.api_client import ApiClient +from zero_ex.sra_client.models.relayer_api_order_config_payload_schema import ( RelayerApiOrderConfigPayloadSchema, ) @@ -42,9 +42,9 @@ def get_asset_pairs(self, **kwargs): :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_b: The assetData value for the second asset in the pair. - :param float network_id: The id of the Ethereum network - :param float 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 network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. :return: :class:`RelayerApiAssetDataPairsResponseSchema`. If the method is called asynchronously returns the request thread. @@ -71,12 +71,14 @@ def get_asset_pairs_with_http_info(self, **kwargs): :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_b: The assetData value for the second asset in the pair. - :param float network_id: The id of the Ethereum network - :param float page: The number of the page to request in the collection. - :param float per_page: The number of records to return per page. - - :return: :class:`RelayerApiAssetDataPairsResponseSchema`. - If the method is called asynchronously returns the request thread. + :param int network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. + + :return: A tuple consisting of a + :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() @@ -168,9 +170,9 @@ def get_fee_recipients(self, **kwargs): >>> result = thread.get() # doctest: +SKIP :param bool async_req: Whether request should be asynchronous. - :param float network_id: The id of the Ethereum network - :param float 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 network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. :return: :class:`RelayerApiFeeRecipientsResponseSchema`. If the method is called asynchronously, returns the request thread. @@ -194,12 +196,14 @@ def get_fee_recipients_with_http_info(self, **kwargs): >>> result = thread.get() # doctest: +SKIP :param bool async_req: Whether request should be asynchronous. - :param float network_id: The id of the Ethereum network - :param float page: The number of the page to request in the collection. - :param float per_page: The number of records to return per page. - - :return: :class:`RelayerApiFeeRecipientsResponseSchema`. - If the method is called asynchronously, returns the request thread. + :param int network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. + + :return: A tuple consisting of a + :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() @@ -277,7 +281,7 @@ def get_order(self, order_hash, **kwargs): :param bool async_req: Whether request should be asynchronous. :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`. If the method is called asynchronously, returns the request thread. @@ -301,10 +305,12 @@ def get_order_with_http_info(self, order_hash, **kwargs): :param bool async_req: Whether request should be asynchronous. :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`. - If the method is called asynchronously returns the request thread. + :return: A tuple consisting of a + :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() @@ -395,7 +401,7 @@ def get_order_config(self, **kwargs): >>> result = thread.get() # doctest: +SKIP :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 :class:`RelayerApiOrderConfigPayloadSchema`. The fields of a 0x order the relayer may want to decide what configuration to send @@ -430,14 +436,16 @@ def get_order_config_with_http_info(self, **kwargs): >>> result = thread.get() # doctest: +SKIP :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 :class: `RelayerApiOrderConfigPayloadSchema`. The fields of a 0x order the relayer may want to decide what configuration to send back. - :return: :class:`RelayerApiOrderConfigResponseSchema`. - If the method is called asynchronously returns the request thread. + :return: A tuple consisting of a + :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() @@ -537,9 +545,9 @@ def get_orderbook(self, base_asset_data, quote_asset_data, **kwargs): :param str quote_asset_data: assetData (makerAssetData or takerAssetData) designated as the quote currency in the currency pair calculation of price. (required) - :param float network_id: The id of the Ethereum network - :param float 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 network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. :return: :class:`RelayerApiOrderbookResponseSchema`. If the method is called asynchronously, returns the request thread. @@ -586,12 +594,14 @@ def get_orderbook_with_http_info( :param str quote_asset_data: assetData (makerAssetData or takerAssetData) designated as the quote currency in the currency pair calculation of price. (required) - :param float network_id: The id of the Ethereum network - :param float page: The number of the page to request in the collection. - :param float per_page: The number of records to return per page. - - :return: :class:`RelayerApiOrderbookResponseSchema`. - If the method is called asynchronously, returns the request thread. + :param int network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. + + :return: A tuple consisting of a + :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() @@ -752,9 +762,9 @@ def get_orders(self, **kwargs): `0x Protocol v2 Specification `__ - :param float network_id: The id of the Ethereum network - :param float 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 network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. :return: :class:`RelayerApiOrdersResponseSchema`. If the method is called asynchronously, returns the request thread. @@ -829,12 +839,14 @@ def get_orders_with_http_info(self, **kwargs): `0x Protocol v2 Specification `__ - :param float network_id: The id of the Ethereum network - :param float page: The number of the page to request in the collection. - :param float per_page: The number of records to return per page. - - :return: RelayerApiOrdersResponseSchema. - If the method is called asynchronously, returns the request thread. + :param int network_id: The id of the Ethereum network + :param int page: The number of the page to request in the collection. + :param int per_page: The number of records to return per page. + + :return: A tuple consisting of a + :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() @@ -983,7 +995,7 @@ def post_order(self, **kwargs): >>> result = thread.get() # doctest: +SKIP :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`. A valid signed 0x order based on the schema. @@ -1008,12 +1020,14 @@ def post_order_with_http_info(self, **kwargs): >>> result = thread.get() # doctest: +SKIP :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` A valid signed 0x order based on the schema. - :return: None. - If the method is called asynchronously, returns the request thread. + :return: A tuple consisting of the response data (always empty for this + 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() diff --git a/python-packages/sra_client/sra_client/api_client.py b/python-packages/sra_client/src/zero_ex/sra_client/api_client.py similarity index 99% rename from python-packages/sra_client/sra_client/api_client.py rename to python-packages/sra_client/src/zero_ex/sra_client/api_client.py index ad2b6385bf..829833ec86 100644 --- a/python-packages/sra_client/sra_client/api_client.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/api_client.py @@ -14,9 +14,9 @@ import six from six.moves.urllib.parse import quote -from sra_client.configuration import Configuration -import sra_client.models -from sra_client import rest +from zero_ex.sra_client.configuration import Configuration +import zero_ex.sra_client.models +from zero_ex.sra_client import rest class ApiClient(object): @@ -300,7 +300,7 @@ def __deserialize(self, data, klass): if klass in self.NATIVE_TYPES_MAPPING: klass = self.NATIVE_TYPES_MAPPING[klass] else: - klass = getattr(sra_client.models, klass) + klass = getattr(zero_ex.sra_client.models, klass) if klass in self.PRIMITIVE_TYPES: return self.__deserialize_primitive(data, klass) diff --git a/python-packages/sra_client/sra_client/configuration.py b/python-packages/sra_client/src/zero_ex/sra_client/configuration.py similarity index 100% rename from python-packages/sra_client/sra_client/configuration.py rename to python-packages/sra_client/src/zero_ex/sra_client/configuration.py diff --git a/python-packages/sra_client/src/zero_ex/sra_client/models/__init__.py b/python-packages/sra_client/src/zero_ex/sra_client/models/__init__.py new file mode 100644 index 0000000000..5b078f533b --- /dev/null +++ b/python-packages/sra_client/src/zero_ex/sra_client/models/__init__.py @@ -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 diff --git a/python-packages/sra_client/sra_client/models/order_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/order_schema.py similarity index 97% rename from python-packages/sra_client/sra_client/models/order_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/order_schema.py index 1eeff510d1..9b84a227d9 100644 --- a/python-packages/sra_client/sra_client/models/order_schema.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/models/order_schema.py @@ -21,21 +21,7 @@ class OrderSchema(object): attribute_map (dict): The key is attribute name and the value is json key in definition. """ - 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", - } + openapi_types = {} attribute_map = { "maker_address": "makerAddress", diff --git a/python-packages/sra_client/sra_client/models/paginated_collection_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/paginated_collection_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/paginated_collection_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/paginated_collection_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_asset_data_pairs_response_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_asset_data_pairs_response_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_asset_data_pairs_response_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_asset_data_pairs_response_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_asset_data_trade_info_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_asset_data_trade_info_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_asset_data_trade_info_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_asset_data_trade_info_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_error_response_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_error_response_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_error_response_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_error_response_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_error_response_schema_validation_errors.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_error_response_schema_validation_errors.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_error_response_schema_validation_errors.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_error_response_schema_validation_errors.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_fee_recipients_response_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_fee_recipients_response_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_fee_recipients_response_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_fee_recipients_response_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_order_config_payload_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_order_config_payload_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_order_config_payload_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_order_config_payload_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_order_config_response_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_order_config_response_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_order_config_response_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_order_config_response_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_order_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_order_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_order_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_order_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_orderbook_response_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orderbook_response_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_orderbook_response_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orderbook_response_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_orders_channel_subscribe_payload_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_channel_subscribe_payload_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_orders_channel_subscribe_payload_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_channel_subscribe_payload_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_orders_channel_subscribe_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_channel_subscribe_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_orders_channel_subscribe_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_channel_subscribe_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_orders_channel_update_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_channel_update_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_orders_channel_update_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_channel_update_schema.py diff --git a/python-packages/sra_client/sra_client/models/relayer_api_orders_response_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_response_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/relayer_api_orders_response_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/relayer_api_orders_response_schema.py diff --git a/python-packages/sra_client/sra_client/models/signed_order_schema.py b/python-packages/sra_client/src/zero_ex/sra_client/models/signed_order_schema.py similarity index 100% rename from python-packages/sra_client/sra_client/models/signed_order_schema.py rename to python-packages/sra_client/src/zero_ex/sra_client/models/signed_order_schema.py diff --git a/python-packages/sra_client/sra_client/rest.py b/python-packages/sra_client/src/zero_ex/sra_client/rest.py similarity index 100% rename from python-packages/sra_client/sra_client/rest.py rename to python-packages/sra_client/src/zero_ex/sra_client/rest.py diff --git a/python-packages/sra_client/test/test_default_api.py b/python-packages/sra_client/test/test_default_api.py deleted file mode 100644 index 5aca6aacb5..0000000000 --- a/python-packages/sra_client/test/test_default_api.py +++ /dev/null @@ -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()