From 119cc5773c3b02e8195f199a35b24753b282b82c Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 16 Oct 2019 14:50:52 -0400 Subject: [PATCH 01/28] .gitignore migrations/0x_ganache_snapshot --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 934fdf50d0..d99444d636 100644 --- a/.gitignore +++ b/.gitignore @@ -171,3 +171,6 @@ python-packages/json_schemas/src/zero_ex/json_schemas/schemas packages/*/docs/README.md .DS_Store + +# the snapshot that gets built for migrations sure does have a ton of files +packages/migrations/0x_ganache_snapshot* From ed7c6bc9f9c8cf8ef6ce4a46bd25612c50655ba5 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 16 Oct 2019 14:51:20 -0400 Subject: [PATCH 02/28] .gitignore new-ish Python contract wrappers These should have been added back when we started generating these wrappers. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index d99444d636..14898c770c 100644 --- a/.gitignore +++ b/.gitignore @@ -130,6 +130,7 @@ contracts/erc1155/generated-wrappers/ contracts/extensions/generated-wrappers/ contracts/exchange-forwarder/generated-wrappers/ contracts/dev-utils/generated-wrappers/ +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py @@ -138,6 +139,8 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_regi python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py @@ -148,6 +151,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__in python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py From 4206e6df5ced57ecd8671b1bfdfbd0a92c062223 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 16 Oct 2019 15:11:26 -0400 Subject: [PATCH 03/28] rm superfluous contract artifact in Python package All of the contract artifacts were removed from the Python package recently, because now they're copied from the monorepo/packages area as an automated build step. Somehow this one artifact slipped through the cracks. --- .../artifacts/EthBalanceChecker.json | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/EthBalanceChecker.json diff --git a/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/EthBalanceChecker.json b/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/EthBalanceChecker.json deleted file mode 100644 index a0708e3ee8..0000000000 --- a/python-packages/contract_artifacts/src/zero_ex/contract_artifacts/artifacts/EthBalanceChecker.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "schemaVersion": "2.0.0", - "contractName": "EthBalanceChecker", - "compilerOutput": { - "abi": [ - { - "constant": true, - "inputs": [{ "name": "addresses", "type": "address[]" }], - "name": "getEthBalances", - "outputs": [{ "name": "", "type": "uint256[]" }], - "payable": false, - "stateMutability": "view", - "type": "function" - } - ], - "evm": { - "bytecode": { - "object": "0x608060405234801561001057600080fd5b506101e5806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a0901e5114610030575b600080fd5b6100d36004803603602081101561004657600080fd5b81019060208101813564010000000081111561006157600080fd5b82018360208201111561007357600080fd5b8035906020019184602083028401116401000000008311171561009557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610123945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561010f5781810151838201526020016100f7565b505050509050019250505060405180910390f35b6060808251604051908082528060200260200182016040528015610151578160200160208202803883390190505b50905060005b835181146101a95783818151811061016b57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff163182828151811061019657fe5b6020908102919091010152600101610157565b509291505056fea265627a7a72305820c934dc478ccdc0f8a6d0fb6135610c21efcb23a2fd5075c6d2c4891b449b70f964736f6c63430005090032" - } - } - }, - "networks": {} -} From 363363aee28642b3cbdef18b77c886ebb60032c8 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 18 Oct 2019 16:03:24 -0400 Subject: [PATCH 04/28] Eliminate circular dependency This was preventing the Exchange wrapper from ever importing its validator! --- .../contract_wrappers/exchange/types.py | 139 ----------------- .../contract_wrappers/exchange/validator.py | 2 +- .../contract_wrappers/order_conversions.py | 144 ++++++++++++++++++ python-packages/json_schemas/setup.py | 1 - .../src/zero_ex/json_schemas/__init__.py | 51 ++++--- .../src/zero_ex/order_utils/__init__.py | 2 +- .../src/zero_ex/sra_client/__init__.py | 3 +- 7 files changed, 175 insertions(+), 167 deletions(-) create mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py index 3bdb3e4e75..21ce43df03 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py @@ -11,12 +11,6 @@ converting Exchange structs between JSON and Python objects. """ -from copy import copy -from typing import cast, Dict - -from eth_utils import remove_0x_prefix - -from zero_ex.json_schemas import assert_valid from . import ( Tuple0xbb41e5b3, @@ -65,136 +59,3 @@ class OrderInfo(Tuple0xb1e4a1ae): """ -def order_to_jsdict( - 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, and - convert numerics to strings. - - >>> import pprint - >>> pprint.pprint(order_to_jsdict( - ... { - ... 'makerAddress': "0x0000000000000000000000000000000000000000", - ... 'takerAddress': "0x0000000000000000000000000000000000000000", - ... 'feeRecipientAddress': - ... "0x0000000000000000000000000000000000000000", - ... 'senderAddress': "0x0000000000000000000000000000000000000000", - ... 'makerAssetAmount': 1, - ... 'takerAssetAmount': 1, - ... 'makerFee': 0, - ... 'takerFee': 0, - ... 'expirationTimeSeconds': 1, - ... 'salt': 1, - ... 'makerAssetData': (0).to_bytes(1, byteorder='big') * 20, - ... 'takerAssetData': (0).to_bytes(1, byteorder='big') * 20, - ... }, - ... )) - {'exchangeAddress': '0x0000000000000000000000000000000000000000', - 'expirationTimeSeconds': '1', - 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', - 'makerAddress': '0x0000000000000000000000000000000000000000', - 'makerAssetAmount': '1', - 'makerAssetData': '0x0000000000000000000000000000000000000000', - 'makerFee': '0', - 'salt': '1', - 'senderAddress': '0x0000000000000000000000000000000000000000', - 'takerAddress': '0x0000000000000000000000000000000000000000', - 'takerAssetAmount': '1', - 'takerAssetData': '0x0000000000000000000000000000000000000000', - 'takerFee': '0'} - """ - jsdict = cast(Dict, copy(order)) - - # encode bytes fields - jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex() - jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex() - - 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_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, and - parse integers from strings. - - >>> import pprint - >>> 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", - ... 'makerAssetData': "0x0000000000000000000000000000000000000000", - ... 'takerAssetData': "0x0000000000000000000000000000000000000000", - ... 'exchangeAddress': "0x0000000000000000000000000000000000000000", - ... }, - ... )) - {'expirationTimeSeconds': 12345, - 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', - 'makerAddress': '0x0000000000000000000000000000000000000000', - 'makerAssetAmount': 1000000000000000000, - 'makerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00', - 'makerFee': 0, - 'salt': 12345, - 'senderAddress': '0x0000000000000000000000000000000000000000', - 'takerAddress': '0x0000000000000000000000000000000000000000', - 'takerAssetAmount': 1000000000000000000, - 'takerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x00\x00\x00\x00', - 'takerFee': 0} - """ # noqa: E501 (line too long) - assert_valid(jsdict, "/orderSchema") - - order = cast(Order, copy(jsdict)) - - order["makerAssetData"] = bytes.fromhex( - remove_0x_prefix(jsdict["makerAssetData"]) - ) - order["takerAssetData"] = bytes.fromhex( - 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 - - return order diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py index 2479ee8179..12726336b5 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py @@ -5,9 +5,9 @@ from web3.providers.base import BaseProvider from zero_ex import json_schemas +from zero_ex.contract_wrappers.order_conversions import order_to_jsdict from ..bases import Validator -from .types import order_to_jsdict class ExchangeValidator(Validator): diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py new file mode 100644 index 0000000000..c1ade7b9a1 --- /dev/null +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py @@ -0,0 +1,144 @@ +"""Utilities to convert between JSON and Python-native objects.""" + +from copy import copy +from typing import cast, Dict + +from eth_utils import remove_0x_prefix + +from zero_ex.json_schemas import assert_valid +from zero_ex.contract_wrappers.exchange.types import Order + + +def order_to_jsdict( + 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, and + convert numerics to strings. + + >>> import pprint + >>> pprint.pprint(order_to_jsdict( + ... { + ... 'makerAddress': "0x0000000000000000000000000000000000000000", + ... 'takerAddress': "0x0000000000000000000000000000000000000000", + ... 'feeRecipientAddress': + ... "0x0000000000000000000000000000000000000000", + ... 'senderAddress': "0x0000000000000000000000000000000000000000", + ... 'makerAssetAmount': 1, + ... 'takerAssetAmount': 1, + ... 'makerFee': 0, + ... 'takerFee': 0, + ... 'expirationTimeSeconds': 1, + ... 'salt': 1, + ... 'makerAssetData': (0).to_bytes(1, byteorder='big') * 20, + ... 'takerAssetData': (0).to_bytes(1, byteorder='big') * 20, + ... }, + ... )) + {'exchangeAddress': '0x0000000000000000000000000000000000000000', + 'expirationTimeSeconds': '1', + 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', + 'makerAddress': '0x0000000000000000000000000000000000000000', + 'makerAssetAmount': '1', + 'makerAssetData': '0x0000000000000000000000000000000000000000', + 'makerFee': '0', + 'salt': '1', + 'senderAddress': '0x0000000000000000000000000000000000000000', + 'takerAddress': '0x0000000000000000000000000000000000000000', + 'takerAssetAmount': '1', + 'takerAssetData': '0x0000000000000000000000000000000000000000', + 'takerFee': '0'} + """ + jsdict = cast(Dict, copy(order)) + + # encode bytes fields + jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex() + jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex() + + 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_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, and + parse integers from strings. + + >>> import pprint + >>> 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", + ... 'makerAssetData': "0x0000000000000000000000000000000000000000", + ... 'takerAssetData': "0x0000000000000000000000000000000000000000", + ... 'exchangeAddress': "0x0000000000000000000000000000000000000000", + ... }, + ... )) + {'expirationTimeSeconds': 12345, + 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', + 'makerAddress': '0x0000000000000000000000000000000000000000', + 'makerAssetAmount': 1000000000000000000, + 'makerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00', + 'makerFee': 0, + 'salt': 12345, + 'senderAddress': '0x0000000000000000000000000000000000000000', + 'takerAddress': '0x0000000000000000000000000000000000000000', + 'takerAssetAmount': 1000000000000000000, + 'takerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00', + 'takerFee': 0} + """ # noqa: E501 (line too long) + assert_valid(jsdict, "/orderSchema") + + order = cast(Order, copy(jsdict)) + + order["makerAssetData"] = bytes.fromhex( + remove_0x_prefix(jsdict["makerAssetData"]) + ) + order["takerAssetData"] = bytes.fromhex( + 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 + + return order diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index 650e2d8469..efa9056833 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -162,7 +162,6 @@ def run(self): extras_require={ "dev": [ "0x-contract-addresses", - "0x-contract-wrappers", "bandit", "black", "coverage", 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 e74717d6ea..01f6ccb4bd 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 @@ -61,35 +61,38 @@ def assert_valid(data: Mapping, schema_id: str) -> None: >>> from zero_ex.json_schemas import assert_valid >>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId - >>> from zero_ex.contract_wrappers.exchange.types import ( - ... Order, order_to_jsdict - ... ) >>> from zero_ex.order_utils import asset_data_utils >>> 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( + ... {'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631', + ... 'takerAddress': '0x0000000000000000000000000000000000000000', + ... 'senderAddress': '0x0000000000000000000000000000000000000000', + ... 'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', + ... 'feeRecipientAddress': ( + ... '0x0000000000000000000000000000000000000000' + ... ), + ... 'makerAssetData': ( + ... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token + ... ), + ... 'takerAssetData': ( + ... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token + ... ), + ... 'salt': random.randint(1, 100000000000000000), + ... 'makerFee': 0, + ... 'makerFeeAssetData': '0x' + '00'*20, + ... 'takerFee': 0, + ... 'takerFeeAssetData': '0x' + '00'*20, + ... 'makerAssetAmount': 1000000000000000000, + ... 'takerAssetAmount': 500000000000000000000, + ... 'expirationTimeSeconds': round( + ... (datetime.utcnow() + timedelta(days=1)).timestamp() + ... ), + ... 'chainId': 50 + ... }, + ... "/orderSchema" ... ) - >>> assert_valid(order_to_jsdict(example_order), "/orderSchema") """ _, schema = _LOCAL_RESOLVER.resolve(schema_id) jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER) 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 62306ce057..0494f41044 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 @@ -31,7 +31,7 @@ from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId import zero_ex.contract_artifacts -from zero_ex.contract_wrappers.exchange.types import Order, order_to_jsdict +from zero_ex.contract_wrappers.order_conversions import order_to_jsdict from zero_ex.dev_utils.type_assertions import ( assert_is_address, assert_is_hex_string, 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 index a1ccdd1ff8..31aeb30f57 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -105,7 +105,8 @@ Post an order for our Maker to trade ZRX for WETH: ->>> from zero_ex.contract_wrappers.exchange.types import Order, order_to_jsdict +>>> from zero_ex.contract_wrappers.exchange.types import Order +>>> from zero_ex.contract_wrappers.order_conversions import order_to_jsdict >>> from zero_ex.order_utils import ( ... asset_data_utils, ... sign_hash) From 7b5bf319f021e556c283ae8d2066bc32aee19068 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 22 Oct 2019 18:45:18 -0400 Subject: [PATCH 05/28] Improve output of monorepo-level parallel script - Capture stderr (and have it included in stdout) so that it doesn't leak onto the console for commands that didn't actually fail. - Include all error output in the Exception object (eliminate print statement). --- python-packages/parallel | 11 +++++++---- python-packages/parallel_without_sra_client | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/python-packages/parallel b/python-packages/parallel index c127d3cf5f..0f374bb950 100755 --- a/python-packages/parallel +++ b/python-packages/parallel @@ -22,7 +22,7 @@ $ ./parallel pip uninstall $(basename $(pwd)) from concurrent.futures import ProcessPoolExecutor, wait from os import chdir -from subprocess import CalledProcessError, check_output +from subprocess import CalledProcessError, check_output, STDOUT from sys import argv PACKAGES = [ @@ -38,11 +38,14 @@ PACKAGES = [ def run_cmd_on_package(package: str): """cd to the package dir, ./setup.py lint, cd ..""" chdir(package) + command = f"{' '.join(argv[1:])}" try: - check_output(f"{' '.join(argv[1:])}".split()) + check_output(command.split(), stderr=STDOUT) except CalledProcessError as error: - print(f"standard output from command:\n{error.output.decode('utf-8')}") - raise RuntimeError(f"Above exception raised in {package}, ") from error + raise RuntimeError( + f"Failure return code received from command `{command}` in package" + + f" {package}, which produced the following output:\n" + + f"{error.output.decode('utf-8')}") from error finally: chdir("..") diff --git a/python-packages/parallel_without_sra_client b/python-packages/parallel_without_sra_client index b0744824bc..206415b758 100755 --- a/python-packages/parallel_without_sra_client +++ b/python-packages/parallel_without_sra_client @@ -22,7 +22,7 @@ $ ./parallel pip uninstall $(basename $(pwd)) from concurrent.futures import ProcessPoolExecutor, wait from os import chdir -from subprocess import CalledProcessError, check_output +from subprocess import CalledProcessError, check_output, STDOUT from sys import argv PACKAGES = [ @@ -37,11 +37,14 @@ PACKAGES = [ def run_cmd_on_package(package: str): """cd to the package dir, ./setup.py lint, cd ..""" chdir(package) + command = f"{' '.join(argv[1:])}" try: - check_output(f"{' '.join(argv[1:])}".split()) + check_output(command.split(), stderr=STDOUT) except CalledProcessError as error: - print(f"standard output from command:\n{error.output.decode('utf-8')}") - raise RuntimeError(f"Above exception raised in {package}, ") from error + raise RuntimeError( + f"Failure return code received from command `{command}` in package" + + f" {package}, which produced the following output:\n" + + f"{error.output.decode('utf-8')}") from error finally: chdir("..") From 923edcd9e19e494390eeaf2306954b37e77bb1cf Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 22 Oct 2019 23:47:41 -0400 Subject: [PATCH 06/28] Silence new versions of linters Newer versions care about this stuff. Old versions didn't, and we don't either. --- python-packages/contract_addresses/setup.py | 6 +++++- python-packages/contract_addresses/src/zero_ex/__init__.py | 2 +- python-packages/contract_artifacts/setup.py | 6 +++++- python-packages/contract_artifacts/src/zero_ex/__init__.py | 2 +- python-packages/contract_wrappers/setup.py | 6 +++++- python-packages/contract_wrappers/src/zero_ex/__init__.py | 2 +- .../src/zero_ex/contract_wrappers/tx_params.py | 5 ++++- python-packages/json_schemas/setup.py | 6 +++++- python-packages/json_schemas/src/zero_ex/__init__.py | 2 +- python-packages/middlewares/setup.py | 6 +++++- python-packages/middlewares/src/zero_ex/__init__.py | 2 +- python-packages/order_utils/setup.py | 6 +++++- python-packages/order_utils/src/zero_ex/__init__.py | 2 +- python-packages/sra_client/setup.py | 5 +++++ python-packages/sra_client/src/zero_ex/__init__.py | 2 +- 15 files changed, 46 insertions(+), 14 deletions(-) diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index 1e1d811f6e..a84ee3551c 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -2,10 +2,14 @@ """setuptools module for contract_addresses package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import subprocess # nosec from shutil import rmtree from os import environ, path -from sys import argv +from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean import distutils.command.build_py diff --git a/python-packages/contract_addresses/src/zero_ex/__init__.py b/python-packages/contract_addresses/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/contract_addresses/src/zero_ex/__init__.py +++ b/python-packages/contract_addresses/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore diff --git a/python-packages/contract_artifacts/setup.py b/python-packages/contract_artifacts/setup.py index 101fb2fc41..60cd883979 100755 --- a/python-packages/contract_artifacts/setup.py +++ b/python-packages/contract_artifacts/setup.py @@ -2,10 +2,14 @@ """setuptools module for contract_artifacts package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import subprocess # nosec from shutil import copytree, rmtree from os import environ, path -from sys import argv +from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean import distutils.command.build_py diff --git a/python-packages/contract_artifacts/src/zero_ex/__init__.py b/python-packages/contract_artifacts/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/contract_artifacts/src/zero_ex/__init__.py +++ b/python-packages/contract_artifacts/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore diff --git a/python-packages/contract_wrappers/setup.py b/python-packages/contract_wrappers/setup.py index 58ad0aba81..a3c32fa126 100755 --- a/python-packages/contract_wrappers/setup.py +++ b/python-packages/contract_wrappers/setup.py @@ -2,11 +2,15 @@ """setuptools module for contract_wrappers package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import subprocess # nosec from shutil import rmtree from os import environ, path, remove from pathlib import Path -from sys import argv +from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean import distutils.command.build_py diff --git a/python-packages/contract_wrappers/src/zero_ex/__init__.py b/python-packages/contract_wrappers/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/contract_wrappers/src/zero_ex/__init__.py +++ b/python-packages/contract_wrappers/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/tx_params.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/tx_params.py index 4b864898ec..226a1f908e 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/tx_params.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/tx_params.py @@ -23,7 +23,7 @@ class TxParams: gas: Optional[int] = attr.ib( default=None, converter=attr.converters.optional(int) ) - gasPrice: Optional[int] = attr.ib( + gas_price: Optional[int] = attr.ib( default=None, converter=attr.converters.optional(int) ) nonce: Optional[int] = attr.ib( @@ -36,4 +36,7 @@ def as_dict(self): if "from_" in res: res["from"] = res["from_"] del res["from_"] + if "gas_price" in res: + res["gasPrice"] = res["gas_price"] + del res["gas_price"] return res diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index efa9056833..34b92c924c 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -2,12 +2,16 @@ """setuptools module for json_schemas package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import distutils.command.build_py from distutils.command.clean import clean import subprocess # nosec from shutil import copytree, rmtree from os import environ, path -from sys import argv +from sys import argv, exit # pylint: disable=redefined-builtin from setuptools import find_packages, setup from setuptools.command.test import test as TestCommand diff --git a/python-packages/json_schemas/src/zero_ex/__init__.py b/python-packages/json_schemas/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/json_schemas/src/zero_ex/__init__.py +++ b/python-packages/json_schemas/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore diff --git a/python-packages/middlewares/setup.py b/python-packages/middlewares/setup.py index 3a02febcf0..53b78a8023 100755 --- a/python-packages/middlewares/setup.py +++ b/python-packages/middlewares/setup.py @@ -2,10 +2,14 @@ """setuptools module for middlewares package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import subprocess # nosec from shutil import rmtree from os import environ, path -from sys import argv +from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean import distutils.command.build_py diff --git a/python-packages/middlewares/src/zero_ex/__init__.py b/python-packages/middlewares/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/middlewares/src/zero_ex/__init__.py +++ b/python-packages/middlewares/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index e7a4b2e2fd..2732639521 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -2,11 +2,15 @@ """setuptools module for order_utils package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import subprocess # nosec from shutil import rmtree from os import environ, path from pathlib import Path -from sys import argv +from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean import distutils.command.build_py diff --git a/python-packages/order_utils/src/zero_ex/__init__.py b/python-packages/order_utils/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/order_utils/src/zero_ex/__init__.py +++ b/python-packages/order_utils/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index a79d148c47..ec784d4b4c 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -3,10 +3,15 @@ """setuptools module for sra_client package.""" +# pylint: disable=import-outside-toplevel +# we import things outside of top-level because 3rd party libs may not yet be +# installed when you invoke this script + import subprocess # nosec import distutils.command.build_py from distutils.command.clean import clean from shutil import rmtree +from sys import exit # pylint: disable=redefined-builtin from urllib.request import urlopen from urllib.error import URLError diff --git a/python-packages/sra_client/src/zero_ex/__init__.py b/python-packages/sra_client/src/zero_ex/__init__.py index e90d833db6..3fe1300cfb 100644 --- a/python-packages/sra_client/src/zero_ex/__init__.py +++ b/python-packages/sra_client/src/zero_ex/__init__.py @@ -1,2 +1,2 @@ """0x Python API.""" -__import__("pkg_resources").declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) # type: ignore From 1c1fc50316352edc2d2092fdccc3539e668c5c0d Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 07:55:22 -0400 Subject: [PATCH 07/28] Support Rich Reverts via Web3.py middleware --- .../templates/Python/contract.handlebars | 27 +- .../output/python/abi_gen_dummy/__init__.py | 27 +- .../output/python/lib_dummy/__init__.py | 27 +- .../output/python/test_lib_dummy/__init__.py | 27 +- .../zero_ex/contract_wrappers/exceptions.py | 80 ++++ .../contract_wrappers/exchange/exceptions.py | 341 ++++++++++++++++++ .../contract_wrappers/exchange/middleware.py | 36 ++ 7 files changed, 553 insertions(+), 12 deletions(-) create mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exceptions.py create mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/exceptions.py create mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/middleware.py diff --git a/packages/abi-gen/templates/Python/contract.handlebars b/packages/abi-gen/templates/Python/contract.handlebars index 6aec525d3f..38afd29ef0 100644 --- a/packages/abi-gen/templates/Python/contract.handlebars +++ b/packages/abi-gen/templates/Python/contract.handlebars @@ -40,6 +40,12 @@ except ImportError: """No-op input validator.""" +try: + from .middleware import MIDDLEWARE # type: ignore +except ImportError: + pass + + {{tupleDefinitions ABIString}} {{#each methods}} @@ -69,14 +75,29 @@ class {{contractName}}: :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ + # pylint: disable=too-many-statements + self.contract_address = contract_address if not validator: validator = {{contractName}}Validator(provider, contract_address) - self._web3_eth = Web3( # type: ignore # pylint: disable=no-member - provider - ).eth + web3 = Web3(provider) + + # if any middleware was imported, inject it + try: + MIDDLEWARE + except NameError: + pass + else: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware['function'], layer=middleware['layer'], + ) + + self._web3_eth = ( + web3.eth # type: ignore # pylint: disable=no-member + ) {{#if methods}} functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index e215b5f7cc..49bcb59cc8 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -40,6 +40,12 @@ class AbiGenDummyValidator( # type: ignore """No-op input validator.""" +try: + from .middleware import MIDDLEWARE # type: ignore +except ImportError: + pass + + class Tuple0x246f9407(TypedDict): """Python representation of a tuple or struct. @@ -1953,14 +1959,29 @@ def __init__( :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ + # pylint: disable=too-many-statements + self.contract_address = contract_address if not validator: validator = AbiGenDummyValidator(provider, contract_address) - self._web3_eth = Web3( # type: ignore # pylint: disable=no-member - provider - ).eth + web3 = Web3(provider) + + # if any middleware was imported, inject it + try: + MIDDLEWARE + except NameError: + pass + else: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware["function"], layer=middleware["layer"] + ) + + self._web3_eth = ( + web3.eth # type: ignore # pylint: disable=no-member + ) functions = self._web3_eth.contract( address=to_checksum_address(contract_address), diff --git a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py index 1181817fdc..e5ec5d76cd 100644 --- a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py @@ -40,6 +40,12 @@ class LibDummyValidator( # type: ignore """No-op input validator.""" +try: + from .middleware import MIDDLEWARE # type: ignore +except ImportError: + pass + + # pylint: disable=too-many-public-methods,too-many-instance-attributes class LibDummy: """Wrapper class for LibDummy Solidity contract.""" @@ -56,14 +62,29 @@ def __init__( :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ + # pylint: disable=too-many-statements + self.contract_address = contract_address if not validator: validator = LibDummyValidator(provider, contract_address) - self._web3_eth = Web3( # type: ignore # pylint: disable=no-member - provider - ).eth + web3 = Web3(provider) + + # if any middleware was imported, inject it + try: + MIDDLEWARE + except NameError: + pass + else: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware["function"], layer=middleware["layer"] + ) + + self._web3_eth = ( + web3.eth # type: ignore # pylint: disable=no-member + ) @staticmethod def abi(): diff --git a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py index ebdd8c6c6a..7af4d6b224 100644 --- a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py @@ -40,6 +40,12 @@ class TestLibDummyValidator( # type: ignore """No-op input validator.""" +try: + from .middleware import MIDDLEWARE # type: ignore +except ImportError: + pass + + class PublicAddConstantMethod(ContractMethod): """Various interfaces to the publicAddConstant method.""" @@ -176,14 +182,29 @@ def __init__( :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ + # pylint: disable=too-many-statements + self.contract_address = contract_address if not validator: validator = TestLibDummyValidator(provider, contract_address) - self._web3_eth = Web3( # type: ignore # pylint: disable=no-member - provider - ).eth + web3 = Web3(provider) + + # if any middleware was imported, inject it + try: + MIDDLEWARE + except NameError: + pass + else: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware["function"], layer=middleware["layer"] + ) + + self._web3_eth = ( + web3.eth # type: ignore # pylint: disable=no-member + ) functions = self._web3_eth.contract( address=to_checksum_address(contract_address), diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exceptions.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exceptions.py new file mode 100644 index 0000000000..32cf19f25c --- /dev/null +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exceptions.py @@ -0,0 +1,80 @@ +"""Exception classes common to all wrappers.""" + +from inspect import isclass +from typing import List + +from eth_abi import decode_abi + + +class RichRevert(Exception): + """Raised when a contract method returns a rich revert error.""" + + def __init__( + self, abi_signature: str, param_names: List[str], return_data: str + ): + """Populate instance variables with decoded return data values.""" + arg_start_index = abi_signature.index("(") + 1 + arg_end_index = abi_signature.index(")") + arguments = decode_abi( + abi_signature[arg_start_index:arg_end_index].split(","), + bytes.fromhex(return_data[10:]), + ) + for (param_name, argument) in zip(param_names, arguments): + setattr(self, param_name, argument) + super().__init__(vars(self)) + + +class NoExceptionForSelector(Exception): + """Indicates that no exception could be found for the given selector.""" + + +def exception_class_from_rich_revert_selector( + selector: str, exceptions_module +) -> RichRevert: + """Return the appropriate exception class. + + :param selector: A string of the format '0xffffffff' which indicates the + 4-byte ABI function selector of a rich revert error type, which is + expected to be found as a class attribute on some class in + `exceptions_module`:code:. + :param exceptions_module: The Python module in which to look for a class + with a `selector`:code: attribute matching the value of the + `selector`:code: argument. + """ + # noqa: D202 (No blank lines allowed after function docstring + def _get_rich_revert_exception_classes(): + def _exception_name_is_class_with_selector(name: str): + if not isclass(getattr(exceptions_module, name)): + return False + + try: + getattr(exceptions_module, name).selector + except AttributeError: + return False + + return True + + def _convert_class_name_to_class(name: str): + return getattr(exceptions_module, name) + + return list( + map( + _convert_class_name_to_class, + filter( + _exception_name_is_class_with_selector, + dir(exceptions_module), + ), + ) + ) + + rich_reverts = _get_rich_revert_exception_classes() + + try: + return next( + filter( + lambda rich_revert: rich_revert.selector == selector, + rich_reverts, + ) + ) + except StopIteration: + raise NoExceptionForSelector(selector) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/exceptions.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/exceptions.py new file mode 100644 index 0000000000..ab60b845ac --- /dev/null +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/exceptions.py @@ -0,0 +1,341 @@ +"""Exchange-specific exception classes.""" + +from enum import auto, Enum + +from zero_ex.contract_wrappers.exceptions import RichRevert + +# pylint: disable=missing-docstring + + +class AssetProxyDispatchErrorCodes(Enum): # noqa: D101 (missing docstring) + INVALID_ASSET_DATA_LENGTH = 0 + UNKNOWN_ASSET_PROXY = auto() + + +class BatchMatchOrdersErrorCodes(Enum): # noqa: D101 (missing docstring) + ZERO_LEFT_ORDERS = 0 + ZERO_RIGHT_ORDERS = auto() + INVALID_LENGTH_LEFT_SIGNATURES = auto() + INVALID_LENGTH_RIGHT_SIGNATURES = auto() + + +class ExchangeContextErrorCodes(Enum): # noqa: D101 (missing docstring) + INVALID_MAKER = 0 + INVALID_TAKER = auto() + INVALID_SENDER = auto() + + +class FillErrorCodes(Enum): # noqa: D101 (missing docstring) + INVALID_TAKER_AMOUNT = 0 + TAKER_OVERPAY = auto() + OVERFILL = auto() + INVALID_FILL_PRICE = auto() + + +class SignatureErrorCodes(Enum): # noqa: D101 (missing docstring) + BAD_ORDER_SIGNATURE = 0 + BAD_TRANSACTION_SIGNATURE = auto() + INVALID_LENGTH = auto() + UNSUPPORTED = auto() + ILLEGAL = auto() + INAPPROPRIATE_SIGNATURE_TYPE = auto() + INVALID_SIGNER = auto() + + +class TransactionErrorCodes(Enum): # noqa: D101 (missing docstring) + ALREADY_EXECUTED = 0 + EXPIRED = auto() + + +class IncompleteFillErrorCode(Enum): # noqa: D101 (missing docstring) + INCOMPLETE_MARKET_BUY_ORDERS = 0 + INCOMPLETE_MARKET_SELL_ORDERS = auto() + INCOMPLETE_FILL_ORDER = auto() + + +class SignatureError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "SignatureError(uint8,bytes32,address,bytes)", + ["errorCode", "hash", "signerAddress", "signature"], + return_data, + ) + + errorCode: SignatureErrorCodes + hash: bytes + signerAddress: str + signature: bytes + + selector = "0x7e5a2318" + + +class SignatureValidatorNotApprovedError( + RichRevert +): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "SignatureValidatorNotApprovedError(address,address)", + ["signerAddress", "validatorAddress"], + return_data, + ) + + signerAddress: str + validatorAddress: str + + selector = "0xa15c0d06" + + +class EIP1271SignatureError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "EIP1271SignatureError(address,bytes,bytes,bytes)", + ["verifyingContractAddress", "data", "signature", "errorData"], + return_data, + ) + + verifyingContractAddress: str + data: bytes + signature: bytes + errorData: bytes + + selector = "0x5bd0428d" + + +class SignatureWalletError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "SignatureWalletError(bytes32,address,bytes,bytes)", + ["hash", "walletAddress", "signature", "errorData"], + return_data, + ) + + hash: bytes + walletAddress: str + signature: bytes + errorData: bytes + + selector = "0x1b8388f7" + + +class OrderStatusError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "OrderStatusError(bytes32,uint8)", + ["orderHash", "orderStatus"], + return_data, + ) + + orderHash: bytes + orderStatus: int + + selector = "0xfdb6ca8d" + + +class ExchangeInvalidContextError( + RichRevert +): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "ExchangeInvalidContextError(uint8,bytes32,address)", + ["errorCode", "orderHash", "contextAddress"], + return_data, + ) + + errorCode: ExchangeContextErrorCodes + orderHash: bytes + contextAddress: str + + selector = "0xe53c76c8" + + +class FillError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "FillError(uint8,bytes32)", ["errorCode", "orderHash"], return_data + ) + + errorCode: FillErrorCodes + orderHash: bytes + + selector = "0xe94a7ed0" + + +class OrderEpochError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "OrderEpochError(address,address,uint256)", + ["makerAddress", "orderSenderAddress", "currentEpoch"], + return_data, + ) + + makerAddress: str + orderSenderAddress: str + currentEpoch: int + + selector = "0x4ad31275" + + +class AssetProxyExistsError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "AssetProxyExistsError(bytes4,address)", + ["assetProxyId", "assetProxyAddress"], + return_data, + ) + + assetProxyId: bytes + assetProxyAddress: str + + selector = "0x11c7b720" + + +class AssetProxyDispatchError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "AssetProxyDispatchError(uint8,bytes32,bytes)", + ["errorCode", "orderHash", "assetData"], + return_data, + ) + + errorCode: AssetProxyDispatchErrorCodes + orderHash: bytes + assetData: bytes + + selector = "0x488219a6" + + +class AssetProxyTransferError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "AssetProxyTransferError(bytes32,bytes,bytes)", + ["orderHash", "assetData", "errorData"], + return_data, + ) + + orderHash: bytes + assetData: bytes + errorData: bytes + + selector = "0x4678472b" + + +class NegativeSpreadError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "NegativeSpreadError(bytes32,bytes32)", + ["leftOrderHash", "rightOrderHash"], + return_data, + ) + + leftOrderHash: bytes + rightOrderHash: bytes + + selector = "0xb6555d6f" + + +class TransactionError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "TransactionError(uint8,bytes32)", + ["errorCode", "transactionHash"], + return_data, + ) + + errorCode: TransactionErrorCodes + transactionHash: bytes + + selector = "0xf5985184" + + +class TransactionExecutionError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "TransactionExecutionError(bytes32,bytes)", + ["transactionHash", "errorData"], + return_data, + ) + + transactionHash: bytes + errorData: bytes + + selector = "0x20d11f61" + + +class TransactionGasPriceError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "TransactionGasPriceError(bytes32,uint256,uint256)", + ["transactionHash", "actualGasPrice", "requiredGasPrice"], + return_data, + ) + + transactionHash: bytes + actualGasPrice: int + requiredGasPrice: int + + selector = "0xa26dac09" + + +class TransactionInvalidContextError( + RichRevert +): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "TransactionInvalidContextError(bytes32,address)", + ["transactionHash", "currentContextAddress"], + return_data, + ) + + transactionHash: bytes + currentContextAddress: str + + selector = "0xdec4aedf" + + +class IncompleteFillError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "IncompleteFillError(uint8,uint256,uint256)", + ["errorCode", "expectedAssetAmount", "actualAssetAmount"], + return_data, + ) + + errorCode: IncompleteFillErrorCode + expectedAssetAmount: int + actualAssetAmount: int + + selector = "0x18e4b141" + + +class BatchMatchOrdersError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "BatchMatchOrdersError(uint8)", ["errorCode"], return_data + ) + + errorCode: BatchMatchOrdersErrorCodes + + selector = "0xd4092f4f" + + +class PayProtocolFeeError(RichRevert): # noqa: D101 (missing docstring) + def __init__(self, return_data): # noqa: D107 (missing docstring) + super().__init__( + "PayProtocolFeeError(bytes32,uint256,address,address,bytes)", + [ + "orderHash", + "protocolFee", + "makerAddress", + "takerAddress", + "errorData", + ], + return_data, + ) + + orderHash: bytes + protocolFee: int + makerAddress: str + takerAddress: str + errorData: bytes + + selector = "0x87cb1e75" diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/middleware.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/middleware.py new file mode 100644 index 0000000000..4b509fdd8c --- /dev/null +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/middleware.py @@ -0,0 +1,36 @@ +"""Web3.py-compatible middleware to be injected upon contract instantiation.""" + +from zero_ex.contract_wrappers.exceptions import ( + exception_class_from_rich_revert_selector, + NoExceptionForSelector, +) + +from . import exceptions + + +def rich_revert_handler(make_request, _): + """Return a middlware to raise exceptions for rich revert return data.""" + # noqa: D202 (No blank lines allowed after function docstring + def middleware(method, params): + response = make_request(method, params) + try: + raise exception_class_from_rich_revert_selector( + response["result"][0:10], exceptions + )(response["result"]) + except NoExceptionForSelector: + # response prefix didn't indicate a known error + pass + except TypeError: + # eg "unhashable type: 'slice'". if response["result"] isn't + # sliceable (eg if it's a dict), then it definitely isn't a rich + # revert. + pass + except KeyError: + # response doesn't have a "result" key + pass + return response + + return middleware + + +MIDDLEWARE = [{"layer": 0, "function": rich_revert_handler}] From 66ce2c0c4a8688d5083fa70c663124031205c6d5 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 07:59:44 -0400 Subject: [PATCH 08/28] Fix bug in generated wrappers' bytes handling `bytes.fromhex(bytes.decode('utf-8')` is just plain wrong. It would work for some cases, but is not working when trying to fill orders with the latest Exchange contract. --- .../templates/Python/partials/method_class.handlebars | 7 ------- .../test-cli/output/python/abi_gen_dummy/__init__.py | 3 --- 2 files changed, 10 deletions(-) diff --git a/packages/abi-gen/templates/Python/partials/method_class.handlebars b/packages/abi-gen/templates/Python/partials/method_class.handlebars index 1817eac591..6bbbb07684 100644 --- a/packages/abi-gen/templates/Python/partials/method_class.handlebars +++ b/packages/abi-gen/templates/Python/partials/method_class.handlebars @@ -21,13 +21,6 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod): {{else if (equal type 'uint256')}} # safeguard against fractional inputs {{toPythonIdentifier this.name}} = int({{toPythonIdentifier this.name}}) - {{else if (equal type 'bytes')}} - {{toPythonIdentifier this.name}} = bytes.fromhex({{toPythonIdentifier this.name}}.decode("utf-8")) - {{else if (equal type 'bytes[]')}} - {{toPythonIdentifier this.name}} = [ - bytes.fromhex({{toPythonIdentifier this.name}}_element.decode("utf-8")) - for {{toPythonIdentifier this.name}}_element in {{toPythonIdentifier this.name}} - ] {{/if}} {{/each}} return ({{> params }}) diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index 49bcb59cc8..98bb551cde 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -239,7 +239,6 @@ def validate_and_normalize_inputs(self, a: List[bytes]): parameter_name="a", argument_value=a, ) - a = [bytes.fromhex(a_element.decode("utf-8")) for a_element in a] return a def call( @@ -426,7 +425,6 @@ def validate_and_normalize_inputs( parameter_name="index_1", argument_value=index_1, ) - index_1 = bytes.fromhex(index_1.decode("utf-8")) self.validator.assert_valid( method_name="multiInputMultiOutput", parameter_name="index_2", @@ -624,7 +622,6 @@ def validate_and_normalize_inputs(self, a: bytes): self.validator.assert_valid( method_name="acceptsBytes", parameter_name="a", argument_value=a ) - a = bytes.fromhex(a.decode("utf-8")) return a def call(self, a: bytes, tx_params: Optional[TxParams] = None) -> None: From 606ec2110a3a7975942dcae3a069aeb01217c15e Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 08:08:46 -0400 Subject: [PATCH 09/28] Migrate to Exchange v3 --- .../contract_wrappers/src/index.rst | 12 ++- .../src/zero_ex/contract_wrappers/__init__.py | 67 ++++++++++--- .../contract_wrappers/exchange/types.py | 36 +++++-- .../contract_wrappers/exchange/validator.py | 12 ++- .../contract_wrappers/order_conversions.py | 33 ++++++- .../test/test_exchange_wrapper.py | 33 +++++-- .../json_schemas/test/test_json_schemas.py | 3 + .../test/test_local_message_signer.py | 11 ++- .../src/zero_ex/order_utils/__init__.py | 95 ++++++++++--------- .../test/test_generate_order_hash_hex.py | 7 +- .../order_utils/test/test_signature_utils.py | 57 +++++++---- 11 files changed, 261 insertions(+), 105 deletions(-) diff --git a/python-packages/contract_wrappers/src/index.rst b/python-packages/contract_wrappers/src/index.rst index 2cb98cea20..f3c97a34e5 100644 --- a/python-packages/contract_wrappers/src/index.rst +++ b/python-packages/contract_wrappers/src/index.rst @@ -180,18 +180,20 @@ zero_ex.contract_wrappers.exchange.types .. autoclass:: zero_ex.contract_wrappers.exchange.types.MatchedFillResults +.. autoclass:: zero_ex.contract_wrappers.exchange.types.ZeroExTransaction + zero_ex.contract_wrappers.exchange: Generated Tuples ==================================================== -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x260219a2 +.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x6ca34a6f This is the generated class representing `the Order struct `_. -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xbb41e5b3 +.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x735c43e3 This is the generated class representing `the FillResults struct `_. -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x054ca44e +.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b This is the generated class representing `the MatchedFillResults struct `_. @@ -199,6 +201,10 @@ zero_ex.contract_wrappers.exchange: Generated Tuples This is the generated class representing `the OrderInfo struct `_. +.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe + + This is the generated class representing `the ZeroExTransaction struct `_. + Indices and tables ================== 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 3ffd80b6bc..98be012a96 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 @@ -135,16 +135,21 @@ ... takerAssetAmount=to_wei(0.1, 'ether'), ... expirationTimeSeconds=round( ... (datetime.utcnow() + timedelta(days=1)).timestamp() -... ) +... ), +... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), +... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), +... chain_id=Web3(ganache).eth.chainId, ... ) 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) +>>> order_hash_hex = generate_order_hash_hex( +... order, exchange_address, Web3(ganache).eth.chainId +... ) ->>> from zero_ex.order_utils import sign_hash_to_bytes ->>> maker_signature = sign_hash_to_bytes( +>>> from zero_ex.order_utils import sign_hash +>>> maker_signature = sign_hash( ... ganache, Web3.toChecksumAddress(maker_address), order_hash_hex ... ) @@ -156,16 +161,37 @@ Filling an order ---------------- -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 we'll have our Taker fill the order. >>> from zero_ex.contract_wrappers.exchange import Exchange >>> exchange = Exchange( ... provider=ganache, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, ... ) + +But before filling an order, one may wish to check that it's actually fillable: + +>>> from zero_ex.contract_wrappers.exchange.types import OrderStatus +>>> OrderStatus(exchange.get_order_info.call(order)[0]) + + +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. + +One may wish to first call the method in a read-only way, to ensure that it +will not revert, and to validate that the return data is as expected: + +>>> exchange.fill_order.call( +... order=order, +... taker_asset_fill_amount=order["takerAssetAmount"], +... signature=maker_signature, +... tx_params=TxParams(from_=taker_address) +... ) +(100000000000000000, 100000000000000000, 0, 0, 0) + +Finally, submit the transaction: + >>> tx_hash = exchange.fill_order.send_transaction( ... order=order, ... taker_asset_fill_amount=order["takerAssetAmount"], @@ -184,12 +210,15 @@ 'makerAddress': '0x...', 'makerAssetData': b..., 'makerAssetFilledAmount': 100000000000000000, + 'makerFeeAssetData': b..., 'makerFeePaid': 0, 'orderHash': b..., + 'protocolFeePaid': ..., 'senderAddress': '0x...', 'takerAddress': '0x...', 'takerAssetData': b..., 'takerAssetFilledAmount': 100000000000000000, + 'takerFeeAssetData': b..., 'takerFeePaid': 0} >>> exchange.get_fill_event(tx_hash)[0].args.takerAssetFilledAmount 100000000000000000 @@ -206,7 +235,9 @@ ... senderAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... makerAssetData=asset_data_utils.encode_erc20(weth_address), +... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... takerAssetData=asset_data_utils.encode_erc20(weth_address), +... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... salt=random.randint(1, 100000000000000000), ... makerFee=0, ... takerFee=0, @@ -248,7 +279,9 @@ ... senderAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... makerAssetData=asset_data_utils.encode_erc20(zrx_address), +... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... takerAssetData=asset_data_utils.encode_erc20(weth_address), +... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... salt=random.randint(1, 100000000000000000), ... makerFee=0, ... takerFee=0, @@ -258,10 +291,12 @@ ... (datetime.utcnow() + timedelta(days=1)).timestamp() ... ) ... ) ->>> signature_1 = sign_hash_to_bytes( +>>> signature_1 = sign_hash( ... ganache, ... Web3.toChecksumAddress(maker_address), -... generate_order_hash_hex(order_1, exchange.contract_address) +... generate_order_hash_hex( +... order_1, exchange.contract_address, Web3(ganache).eth.chainId +... ), ... ) >>> order_2 = Order( ... makerAddress=maker_address, @@ -269,7 +304,9 @@ ... senderAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... makerAssetData=asset_data_utils.encode_erc20(zrx_address), +... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... takerAssetData=asset_data_utils.encode_erc20(weth_address), +... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... salt=random.randint(1, 100000000000000000), ... makerFee=0, ... takerFee=0, @@ -279,10 +316,12 @@ ... (datetime.utcnow() + timedelta(days=1)).timestamp() ... ) ... ) ->>> signature_2 = sign_hash_to_bytes( +>>> signature_2 = sign_hash( ... ganache, ... Web3.toChecksumAddress(maker_address), -... generate_order_hash_hex(order_2, exchange.contract_address) +... generate_order_hash_hex( +... order_2, exchange.contract_address, Web3(ganache).eth.chainId +... ), ... ) Fill order_1 and order_2 together: @@ -308,7 +347,9 @@ ... senderAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... makerAssetData=asset_data_utils.encode_erc20(weth_address), +... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... takerAssetData=asset_data_utils.encode_erc20(weth_address), +... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), ... salt=random.randint(1, 100000000000000000), ... makerFee=0, ... takerFee=0, @@ -320,7 +361,7 @@ ... ), ... tx_params=TxParams(from_=maker_address), ... ) -73... +74... """ from .tx_params import TxParams diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py index 21ce43df03..500732d6bd 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py @@ -11,11 +11,13 @@ converting Exchange structs between JSON and Python objects. """ +from enum import auto, Enum from . import ( - Tuple0xbb41e5b3, - Tuple0x260219a2, - Tuple0x054ca44e, + Tuple0x735c43e3, + Tuple0x6ca34a6f, + Tuple0x4c5ca29b, + Tuple0xdabc15fe, Tuple0xb1e4a1ae, ) @@ -27,27 +29,35 @@ # of each of these classes. -class FillResults(Tuple0xbb41e5b3): +class FillResults(Tuple0x735c43e3): """The `FillResults`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0xbb41e5b3`:py:class:. + `zero_ex.contract_wrappers.exchange.Tuple0x735c43e3`:py:class:. """ -class Order(Tuple0x260219a2): +class Order(Tuple0x6ca34a6f): """The `Order`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0x260219a2`:py:class:. + `zero_ex.contract_wrappers.exchange.Tuple0x6ca34a6f`:py:class:. """ -class MatchedFillResults(Tuple0x054ca44e): +class MatchedFillResults(Tuple0x4c5ca29b): """The `MatchedFillResults`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0x054ca44e`:py:class:. + `zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b`:py:class:. + """ + + +class ZeroExTransaction(Tuple0xdabc15fe): + """The `ZeroExTransaction`:code: Solidity struct. + + Also known as + `zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe`:py:class:. """ @@ -59,3 +69,11 @@ class OrderInfo(Tuple0xb1e4a1ae): """ +class OrderStatus(Enum): # noqa: D101 # pylint: disable=missing-docstring + INVALID = 0 + INVALID_MAKER_ASSET_AMOUNT = auto() + INVALID_TAKER_ASSET_AMOUNT = auto() + FILLABLE = auto() + EXPIRED = auto() + FULLY_FILLED = auto() + CANCELLED = auto() diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py index 12726336b5..5f56340ffd 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py @@ -2,6 +2,7 @@ from typing import Any +from web3 import Web3 from web3.providers.base import BaseProvider from zero_ex import json_schemas @@ -17,6 +18,9 @@ def __init__(self, provider: BaseProvider, contract_address: str): """Initialize the class.""" super().__init__(provider, contract_address) self.contract_address = contract_address + self.chain_id = Web3( # type: ignore # pylint: disable=no-member + provider + ).eth.chainId def assert_valid( self, method_name: str, parameter_name: str, argument_value: Any @@ -30,13 +34,17 @@ def assert_valid( """ if parameter_name == "order": json_schemas.assert_valid( - order_to_jsdict(argument_value, self.contract_address), + order_to_jsdict( + argument_value, self.chain_id, self.contract_address + ), "/orderSchema", ) if parameter_name == "orders": for order in argument_value: json_schemas.assert_valid( - order_to_jsdict(order, self.contract_address), + order_to_jsdict( + order, self.chain_id, self.contract_address + ), "/orderSchema", ) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py index c1ade7b9a1..42fe5457ca 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py @@ -11,6 +11,7 @@ def order_to_jsdict( order: Order, + chain_id: int, exchange_address="0x0000000000000000000000000000000000000000", signature: str = None, ) -> dict: @@ -35,27 +36,35 @@ def order_to_jsdict( ... 'salt': 1, ... 'makerAssetData': (0).to_bytes(1, byteorder='big') * 20, ... 'takerAssetData': (0).to_bytes(1, byteorder='big') * 20, + ... 'makerFeeAssetData': (0).to_bytes(1, byteorder='big') * 20, + ... 'takerFeeAssetData': (0).to_bytes(1, byteorder='big') * 20, ... }, + ... chain_id=50 ... )) - {'exchangeAddress': '0x0000000000000000000000000000000000000000', + {'chainId': 50, + 'exchangeAddress': '0x0000000000000000000000000000000000000000', 'expirationTimeSeconds': '1', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x0000000000000000000000000000000000000000', 'makerAssetAmount': '1', 'makerAssetData': '0x0000000000000000000000000000000000000000', 'makerFee': '0', + 'makerFeeAssetData': '0x0000000000000000000000000000000000000000', 'salt': '1', 'senderAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': '1', 'takerAssetData': '0x0000000000000000000000000000000000000000', - 'takerFee': '0'} + 'takerFee': '0', + 'takerFeeAssetData': '0x0000000000000000000000000000000000000000'} """ jsdict = cast(Dict, copy(order)) # encode bytes fields jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex() + jsdict["makerFeeAssetData"] = "0x" + order["makerFeeAssetData"].hex() jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex() + jsdict["takerFeeAssetData"] = "0x" + order["takerFeeAssetData"].hex() jsdict["exchangeAddress"] = exchange_address @@ -69,6 +78,8 @@ def order_to_jsdict( jsdict["salt"] = str(order["salt"]) + jsdict["chainId"] = chain_id + if signature is not None: jsdict["signature"] = signature @@ -98,23 +109,31 @@ def jsdict_to_order(jsdict: dict) -> Order: ... 'salt': "12345", ... 'makerAssetData': "0x0000000000000000000000000000000000000000", ... 'takerAssetData': "0x0000000000000000000000000000000000000000", + ... 'makerFeeAssetData': "0x0000000000000000000000000000000000000000", + ... 'takerFeeAssetData': "0x0000000000000000000000000000000000000000", ... 'exchangeAddress': "0x0000000000000000000000000000000000000000", + ... 'chainId': 50 ... }, ... )) - {'expirationTimeSeconds': 12345, + {'chainId': 50, + 'expirationTimeSeconds': 12345, 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x0000000000000000000000000000000000000000', 'makerAssetAmount': 1000000000000000000, 'makerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00', 'makerFee': 0, + 'makerFeeAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00', 'salt': 12345, 'senderAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': 1000000000000000000, 'takerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x00', - 'takerFee': 0} + 'takerFee': 0, + 'takerFeeAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00'} """ # noqa: E501 (line too long) assert_valid(jsdict, "/orderSchema") @@ -123,9 +142,15 @@ def jsdict_to_order(jsdict: dict) -> Order: order["makerAssetData"] = bytes.fromhex( remove_0x_prefix(jsdict["makerAssetData"]) ) + order["makerFeeAssetData"] = bytes.fromhex( + remove_0x_prefix(jsdict["makerFeeAssetData"]) + ) order["takerAssetData"] = bytes.fromhex( remove_0x_prefix(jsdict["takerAssetData"]) ) + order["takerFeeAssetData"] = bytes.fromhex( + remove_0x_prefix(jsdict["takerFeeAssetData"]) + ) order["makerAssetAmount"] = int(jsdict["makerAssetAmount"]) order["takerAssetAmount"] = int(jsdict["takerAssetAmount"]) diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index 8290756a9a..582d35f005 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -10,7 +10,11 @@ from zero_ex.contract_wrappers.exchange import Exchange from zero_ex.contract_wrappers.exchange.types import Order from zero_ex.json_schemas import assert_valid -from zero_ex.order_utils import generate_order_hash_hex, sign_hash_to_bytes +from zero_ex.order_utils import ( + asset_data_utils, + generate_order_hash_hex, + sign_hash, +) @pytest.fixture(scope="module") @@ -43,6 +47,8 @@ def create_test_order( salt=random.randint(1, 1000000000), makerAssetData=maker_asset_data, takerAssetData=taker_asset_data, + makerFeeAssetData=asset_data_utils.encode_erc20("0x" + "00" * 20), + takerFeeAssetData=asset_data_utils.encode_erc20("0x" + "00" * 20), ) return order @@ -67,16 +73,29 @@ def test_exchange_wrapper__fill_order( exchange_wrapper, # pylint: disable=redefined-outer-name ganache_provider, weth_asset_data, + zrx_asset_data, ): """Test filling an order.""" taker = accounts[0] maker = accounts[1] exchange_address = exchange_wrapper.contract_address - order = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data) + order = create_test_order(maker, 1, weth_asset_data, 1, zrx_asset_data) order_hash = generate_order_hash_hex( - order=order, exchange_address=exchange_address + order=order, exchange_address=exchange_address, chain_id=1337 ) - order_signature = sign_hash_to_bytes(ganache_provider, maker, order_hash) + order_signature = sign_hash(ganache_provider, maker, order_hash) + + fill_results = exchange_wrapper.fill_order.call( + order=order, + taker_asset_fill_amount=order["takerAssetAmount"], + signature=order_signature, + tx_params=TxParams(from_=taker), + ) + assert fill_results[0] == 1 + assert fill_results[1] == 1 + assert fill_results[2] == 0 + assert fill_results[3] == 0 + assert fill_results[4] == 0 tx_hash = exchange_wrapper.fill_order.send_transaction( order=order, @@ -107,11 +126,13 @@ def test_exchange_wrapper__batch_fill_orders( orders.append(order_1) orders.append(order_2) order_hashes = [ - generate_order_hash_hex(order=order, exchange_address=exchange_address) + generate_order_hash_hex( + order=order, exchange_address=exchange_address, chain_id=1337 + ) for order in orders ] order_signatures = [ - sign_hash_to_bytes(ganache_provider, maker, order_hash) + sign_hash(ganache_provider, maker, order_hash) for order_hash in order_hashes ] taker_amounts = [order["takerAssetAmount"] for order in orders] diff --git a/python-packages/json_schemas/test/test_json_schemas.py b/python-packages/json_schemas/test/test_json_schemas.py index cfd2787ee6..bba37f333a 100644 --- a/python-packages/json_schemas/test/test_json_schemas.py +++ b/python-packages/json_schemas/test/test_json_schemas.py @@ -15,11 +15,14 @@ "takerAssetData": NULL_ADDRESS, "salt": "0", "makerFee": "0", + "makerFeeAssetData": NULL_ADDRESS, "takerFee": "0", + "takerFeeAssetData": NULL_ADDRESS, "makerAssetAmount": "0", "takerAssetAmount": "0", "expirationTimeSeconds": "0", "exchangeAddress": NULL_ADDRESS, + "chainId": 50, } diff --git a/python-packages/middlewares/test/test_local_message_signer.py b/python-packages/middlewares/test/test_local_message_signer.py index ef1b36f595..6fbe0ba819 100644 --- a/python-packages/middlewares/test/test_local_message_signer.py +++ b/python-packages/middlewares/test/test_local_message_signer.py @@ -17,8 +17,8 @@ def test_local_message_signer__sign_order(): """Test signing order with the local_message_signer middleware""" expected_signature = ( - "0x1cd17d75b891accf16030c572a64cf9e7955de63bcafa5b084439cec630ade2d7" - "c00f47a2f4d5b6a4508267bf4b8527100bd97cf1af9984c0a58e42d25b13f4f0a03" + "0x1c8bdfbb3ce3ed0f38c5a358a7f49ad5f21ea9857224c2fe98c458f2fa25551d4" + "d6db0157d9dfe9f9fadb8dedabb7786352843357f4ec8d0fbcbeeb619b1091f5803" ) address = "0x5409ED021D9299bf6814279A6A1411A7e866A631" exchange = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange @@ -36,7 +36,9 @@ def test_local_message_signer__sign_order(): "senderAddress": "0x0000000000000000000000000000000000000000", "feeRecipientAddress": "0x0000000000000000000000000000000000000000", "makerAssetData": (b"\x00") * 20, + "makerFeeAssetData": (b"\x00") * 20, "takerAssetData": (b"\x00") * 20, + "takerFeeAssetData": (b"\x00") * 20, "salt": 0, "makerFee": 0, "takerFee": 0, @@ -44,8 +46,7 @@ def test_local_message_signer__sign_order(): "takerAssetAmount": 0, "expirationTimeSeconds": 0, } - order_hash = generate_order_hash_hex(order, exchange) + order_hash = generate_order_hash_hex(order, exchange, chain_id=1337) signature = sign_hash(ganache, to_checksum_address(address), order_hash) assert signature == expected_signature - is_valid = is_valid_signature(ganache, order_hash, signature, address)[0] - assert is_valid is True + assert is_valid_signature(ganache, order_hash, signature, address) 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 0494f41044..174e21dcba 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 @@ -31,6 +31,8 @@ from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId import zero_ex.contract_artifacts +from zero_ex.contract_wrappers.exchange import Exchange +from zero_ex.contract_wrappers.exchange.types import Order from zero_ex.contract_wrappers.order_conversions import order_to_jsdict from zero_ex.dev_utils.type_assertions import ( assert_is_address, @@ -48,13 +50,18 @@ class _Constants: eip191_header = b"\x19\x01" eip712_domain_separator_schema_hash = keccak( - b"EIP712Domain(string name,string version,address verifyingContract)" + b"EIP712Domain(" + + b"string name," + + b"string version," + + b"uint256 chainId," + + b"address verifyingContract" + + b")" ) eip712_domain_struct_header = ( eip712_domain_separator_schema_hash + keccak(b"0x Protocol") - + keccak(b"2") + + keccak(b"3.0.0") ) eip712_order_schema_hash = keccak( @@ -70,7 +77,9 @@ class _Constants: + b"uint256 expirationTimeSeconds," + b"uint256 salt," + b"bytes makerAssetData," - + b"bytes takerAssetData" + + b"bytes takerAssetData," + + b"bytes makerFeeAssetData," + + b"bytes takerFeeAssetData" + b")" ) @@ -87,7 +96,9 @@ class SignatureType(Enum): N_SIGNATURE_TYPES = auto() -def generate_order_hash_hex(order: Order, exchange_address: str) -> str: +def generate_order_hash_hex( + order: Order, exchange_address: str, chain_id: int +) -> str: """Calculate the hash of the given order as a hexadecimal string. :param order: The order to be hashed. Must conform to `the 0x order JSON schema `_. @@ -95,27 +106,35 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str: contract has been deployed. :returns: A string, of ASCII hex digits, representing the order hash. + Inputs and expected result below were copied from + @0x/order-utils/test/order_hash_test.ts + >>> generate_order_hash_hex( ... Order( ... makerAddress="0x0000000000000000000000000000000000000000", ... takerAddress="0x0000000000000000000000000000000000000000", ... feeRecipientAddress="0x0000000000000000000000000000000000000000", ... senderAddress="0x0000000000000000000000000000000000000000", - ... makerAssetAmount="1000000000000000000", - ... takerAssetAmount="1000000000000000000", + ... makerAssetAmount="0", + ... takerAssetAmount="0", ... makerFee="0", ... takerFee="0", - ... expirationTimeSeconds="12345", - ... salt="12345", + ... expirationTimeSeconds="0", + ... salt="0", ... makerAssetData=((0).to_bytes(1, byteorder='big') * 20), ... takerAssetData=((0).to_bytes(1, byteorder='big') * 20), + ... makerFeeAssetData=((0).to_bytes(1, byteorder='big') * 20), + ... takerFeeAssetData=((0).to_bytes(1, byteorder='big') * 20), ... ), - ... exchange_address="0x0000000000000000000000000000000000000000", + ... exchange_address="0x1dc4c1cefef38a777b15aa20260a54e584b16c48", + ... chain_id=50 ... ) - '55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692' + '331cb7e07a757bae130702da6646c26531798c92bcfaf671817268fd2c188531' """ # noqa: E501 (line too long) assert_is_address(exchange_address, "exchange_address") - assert_valid(order_to_jsdict(order, exchange_address), "/orderSchema") + assert_valid( + order_to_jsdict(order, chain_id, exchange_address), "/orderSchema" + ) def pad_20_bytes_to_32(twenty_bytes: bytes): return bytes(12) + twenty_bytes @@ -125,6 +144,7 @@ def int_to_32_big_endian_bytes(i: int): eip712_domain_struct_hash = keccak( _Constants.eip712_domain_struct_header + + int_to_32_big_endian_bytes(int(chain_id)) + pad_20_bytes_to_32(to_bytes(hexstr=exchange_address)) ) @@ -142,6 +162,8 @@ def int_to_32_big_endian_bytes(i: int): + int_to_32_big_endian_bytes(int(order["salt"])) + keccak(to_bytes(hexstr=order["makerAssetData"].hex())) + keccak(to_bytes(hexstr=order["takerAssetData"].hex())) + + keccak(to_bytes(hexstr=order["makerFeeAssetData"].hex())) + + keccak(to_bytes(hexstr=order["takerFeeAssetData"].hex())) ) return keccak( @@ -153,7 +175,7 @@ def int_to_32_big_endian_bytes(i: int): def is_valid_signature( provider: BaseProvider, data: str, signature: str, signer_address: str -) -> Tuple[bool, str]: +) -> bool: """Check the validity of the supplied signature. Check if the supplied `signature`:code: corresponds to signing `data`:code: @@ -173,42 +195,25 @@ def is_valid_signature( ... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403', ... '0x5409ed021d9299bf6814279a6a1411a7e866a631', ... ) - (True, '') + True """ # noqa: E501 (line too long) assert_is_provider(provider, "provider") assert_is_hex_string(data, "data") assert_is_hex_string(signature, "signature") assert_is_address(signer_address, "signer_address") - web3_instance = Web3(provider) - # false positive from pylint: disable=no-member - contract_address = NETWORK_TO_ADDRESSES[ - NetworkId(int(web3_instance.net.version)) - ].exchange - # false positive from pylint: disable=no-member - contract: Contract = web3_instance.eth.contract( - address=to_checksum_address(contract_address), - abi=zero_ex.contract_artifacts.abi_by_name("Exchange"), + return Exchange( + provider, + NETWORK_TO_ADDRESSES[ + NetworkId( + int(Web3(provider).net.version) # pylint: disable=no-member + ) + ].exchange, + ).is_valid_hash_signature.call( + bytes.fromhex(remove_0x_prefix(data)), + to_checksum_address(signer_address), + bytes.fromhex(remove_0x_prefix(signature)), ) - try: - return ( - contract.functions.isValidSignature( - data, to_checksum_address(signer_address), signature - ).call(), - "", - ) - except web3.exceptions.BadFunctionCallOutput as exception: - known_revert_reasons = [ - "LENGTH_GREATER_THAN_0_REQUIRED", - "SIGNATURE_ILLEGAL", - "SIGNATURE_UNSUPPORTED", - "LENGTH_0_REQUIRED", - "LENGTH_65_REQUIRED", - ] - for known_revert_reason in known_revert_reasons: - if known_revert_reason in str(exception): - return (False, known_revert_reason) - return (False, f"Unknown: {exception}") class ECSignature(TypedDict): @@ -319,7 +324,7 @@ def sign_hash( ).hex() ) - (valid, _) = is_valid_signature( + valid = is_valid_signature( provider, hash_hex, signature_as_vrst_hex, signer_address ) @@ -334,7 +339,7 @@ def sign_hash( 1, byteorder="big" ).hex() ) - (valid, _) = is_valid_signature( + valid = is_valid_signature( provider, hash_hex, signature_as_vrst_hex, signer_address ) @@ -342,8 +347,8 @@ def sign_hash( return signature_as_vrst_hex raise RuntimeError( - "Signature returned from web3 provider is in an unknown format." - + " Attempted to parse as RSV and as VRS." + "Signature returned from web3 provider is in an unknown format. " + + "Signature was: {signature}" ) diff --git a/python-packages/order_utils/test/test_generate_order_hash_hex.py b/python-packages/order_utils/test/test_generate_order_hash_hex.py index 21b8dda2db..cb6dca35c7 100644 --- a/python-packages/order_utils/test/test_generate_order_hash_hex.py +++ b/python-packages/order_utils/test/test_generate_order_hash_hex.py @@ -6,7 +6,7 @@ def test_get_order_hash_hex__empty_order(): """Test the hashing of an uninitialized order.""" expected_hash_hex = ( - "faa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422" + "331cb7e07a757bae130702da6646c26531798c92bcfaf671817268fd2c188531" ) actual_hash_hex = generate_order_hash_hex( { @@ -18,6 +18,8 @@ def test_get_order_hash_hex__empty_order(): ), "makerAssetData": (b"\x00") * 20, "takerAssetData": (b"\x00") * 20, + "makerFeeAssetData": (b"\x00") * 20, + "takerFeeAssetData": (b"\x00") * 20, "salt": 0, "makerFee": 0, "takerFee": 0, @@ -25,6 +27,7 @@ def test_get_order_hash_hex__empty_order(): "takerAssetAmount": 0, "expirationTimeSeconds": 0, }, - "0x0000000000000000000000000000000000000000", + "0x1dc4c1cefef38a777b15aa20260a54e584b16c48", + 50, ) assert actual_hash_hex == expected_hash_hex diff --git a/python-packages/order_utils/test/test_signature_utils.py b/python-packages/order_utils/test/test_signature_utils.py index a542f8eba3..7bcd090169 100644 --- a/python-packages/order_utils/test/test_signature_utils.py +++ b/python-packages/order_utils/test/test_signature_utils.py @@ -3,6 +3,10 @@ import pytest from web3 import Web3 +from zero_ex.contract_wrappers.exchange.exceptions import ( + SignatureError, + SignatureErrorCodes, +) from zero_ex.order_utils import is_valid_signature, sign_hash_to_bytes @@ -117,28 +121,49 @@ def test_is_valid_signature__unsupported_sig_types(): To induce this error, the last byte of the signature is tweaked from 03 to ff.""" - (is_valid, reason) = is_valid_signature( - Web3.HTTPProvider("http://127.0.0.1:8545"), - "0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0", - "0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc334" - + "0349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254ff", - "0x5409ed021d9299bf6814279a6a1411a7e866a631", - ) - assert is_valid is False - assert reason == "SIGNATURE_UNSUPPORTED" + try: + is_valid_signature( + Web3.HTTPProvider("http://127.0.0.1:8545"), + "0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222" + + "b0", + "0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351b" + + "c3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace" + + "225401", + "0x5409ed021d9299bf6814279a6a1411a7e866a631", + ) + except SignatureError as signature_error: + assert ( + signature_error.errorCode + == SignatureErrorCodes.INVALID_LENGTH.value + ) + else: + pytest.fail("Expected exception") -def test_sign_hash_to_bytes__golden_path(): +def test_sign_hash_to_bytes_and_validate__golden_path(): """Test the happy path through sign_hash_to_bytes().""" provider = Web3.HTTPProvider("http://127.0.0.1:8545") - signature = sign_hash_to_bytes( - provider, - Web3( # pylint: disable=no-member - provider - ).geth.personal.listAccounts()[0], - "0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004", + + signing_address = Web3( # pylint: disable=no-member + provider + ).geth.personal.listAccounts()[0] + + order_hash_hex = ( + "0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004" ) + + signature = sign_hash_to_bytes(provider, signing_address, order_hash_hex) + assert ( signature == b"1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03" # noqa: E501 (line too long) ) + + is_valid = is_valid_signature( + Web3.HTTPProvider("http://127.0.0.1:8545"), + order_hash_hex, + signature.decode("utf-8"), + signing_address, + ) + + assert is_valid is True From 76e8f1782e92af45cc536ecb02ce3f267ab191a6 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 08:09:08 -0400 Subject: [PATCH 10/28] Fix typo in DevUtils documentation --- python-packages/contract_wrappers/src/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-packages/contract_wrappers/src/index.rst b/python-packages/contract_wrappers/src/index.rst index f3c97a34e5..12ec319378 100644 --- a/python-packages/contract_wrappers/src/index.rst +++ b/python-packages/contract_wrappers/src/index.rst @@ -34,7 +34,7 @@ zero_ex.contract_wrappers.coordinator_registry zero_ex.contract_wrappers.dev_utils -======================================= +=================================== .. automodule:: zero_ex.contract_wrappers.dev_utils :members: From d70289070e5ef714b94a8cde2ff31abe676644cd Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 08:11:45 -0400 Subject: [PATCH 11/28] Include new contracts in docs --- .../contract_wrappers/src/index.rst | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/python-packages/contract_wrappers/src/index.rst b/python-packages/contract_wrappers/src/index.rst index 12ec319378..7a20f701e2 100644 --- a/python-packages/contract_wrappers/src/index.rst +++ b/python-packages/contract_wrappers/src/index.rst @@ -49,6 +49,22 @@ zero_ex.contract_wrappers.dutch_auction :special-members: +zero_ex.contract_wrappers.erc1155_mintable +========================================== + +.. automodule:: zero_ex.contract_wrappers.erc1155_mintable + :members: + :special-members: + + +zero_ex.contract_wrappers.erc1155_proxy +======================================= + +.. automodule:: zero_ex.contract_wrappers.erc1155_proxy + :members: + :special-members: + + zero_ex.contract_wrappers.erc20_proxy ===================================== @@ -145,6 +161,14 @@ zero_ex.contract_wrappers.order_validator :special-members: +zero_ex.contract_wrappers.static_call_proxy +=========================================== + +.. automodule:: zero_ex.contract_wrappers.static_call_proxy + :members: + :special-members: + + zero_ex.contract_wrappers.weth9 =============================== From a04cec5bffbba3824dff43aa4edaafff46998c11 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 08:12:07 -0400 Subject: [PATCH 12/28] Re-enable Python checks in CI --- .circleci/config.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 421c1d9a9d..87fc939706 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -192,7 +192,10 @@ jobs: working_directory: ~/repo docker: - image: nikolaik/python-nodejs:python3.7-nodejs8 - - image: 0xorg/ganache-cli:2.2.2 + - image: 0xorg/ganache-cli:4.4.0-beta.1 + environment: + VERSION: 4.4.0-beta.1 + SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta - image: 0xorg/launch-kit-backend:74bcc39 environment: RPC_URL: http://localhost:8545 @@ -426,12 +429,11 @@ workflows: - test-exchange-ganache-3.0 - test-rest - static-tests - # - test-python: - # requires: - # - build - # - test-rest - # - static-tests-python: - # requires: - # - test-python + - test-python: + requires: + - build + - static-tests-python: + requires: + - build # skip python tox run for now, as we don't yet have multiple test environments to support. # - test-rest-python From 4b02dcc6ec1fb5259eb461fa663d56f8467fecf4 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 18 Oct 2019 20:57:33 -0400 Subject: [PATCH 13/28] Accept strings for bytes --- packages/abi-gen/src/utils.ts | 2 +- .../output/python/abi_gen_dummy/__init__.py | 62 ++++++++++--------- .../src/zero_ex/contract_wrappers/__init__.py | 4 +- .../contract_wrappers/order_conversions.py | 23 +++++-- .../src/zero_ex/order_utils/__init__.py | 19 ++++-- 5 files changed, 67 insertions(+), 43 deletions(-) diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts index bb5a2d7812..8828ce009b 100644 --- a/packages/abi-gen/src/utils.ts +++ b/packages/abi-gen/src/utils.ts @@ -115,7 +115,7 @@ export const utils = { { regex: '^address$', pyType: 'str' }, { regex: '^bool$', pyType: 'bool' }, { regex: '^u?int\\d*$', pyType: 'int' }, - { regex: '^bytes\\d*$', pyType: 'bytes' }, + { regex: '^bytes\\d*$', pyType: 'Union[bytes, str]' }, ]; for (const regexAndTxType of solTypeRegexToPyType) { const { regex, pyType } = regexAndTxType; diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index 98bb551cde..11fdfe76e6 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -100,11 +100,11 @@ class Tuple0xcf8ad995(TypedDict): accomplished via `str.encode("utf_8")`:code: """ - someBytes: bytes + someBytes: Union[bytes, str] anInteger: int - aDynamicArrayOfBytes: List[bytes] + aDynamicArrayOfBytes: List[Union[bytes, str]] aString: str @@ -148,7 +148,7 @@ class Tuple0xf95128ef(TypedDict): foo: int - bar: bytes + bar: Union[bytes, str] car: str @@ -171,9 +171,9 @@ class Tuple0xa057bf41(TypedDict): input: Tuple0xf95128ef - lorem: bytes + lorem: Union[bytes, str] - ipsum: bytes + ipsum: Union[bytes, str] dolor: str @@ -232,7 +232,7 @@ def __init__( super().__init__(provider, contract_address, validator) self.underlying_method = contract_function - def validate_and_normalize_inputs(self, a: List[bytes]): + def validate_and_normalize_inputs(self, a: List[Union[bytes, str]]): """Validate the inputs to the acceptsAnArrayOfBytes method.""" self.validator.assert_valid( method_name="acceptsAnArrayOfBytes", @@ -242,7 +242,7 @@ def validate_and_normalize_inputs(self, a: List[bytes]): return a def call( - self, a: List[bytes], tx_params: Optional[TxParams] = None + self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None ) -> None: """Execute underlying contract method via eth_call. @@ -257,7 +257,7 @@ def call( return self.underlying_method(a).call(tx_params.as_dict()) def send_transaction( - self, a: List[bytes], tx_params: Optional[TxParams] = None + self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None ) -> Union[HexBytes, bytes]: """Execute underlying contract method via eth_sendTransaction. @@ -272,7 +272,7 @@ def send_transaction( return self.underlying_method(a).transact(tx_params.as_dict()) def estimate_gas( - self, a: List[bytes], tx_params: Optional[TxParams] = None + self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None ) -> int: """Estimate gas consumption of method call.""" (a) = self.validate_and_normalize_inputs(a) @@ -410,7 +410,7 @@ def __init__( self.underlying_method = contract_function def validate_and_normalize_inputs( - self, index_0: int, index_1: bytes, index_2: str + self, index_0: int, index_1: Union[bytes, str], index_2: str ): """Validate the inputs to the multiInputMultiOutput method.""" self.validator.assert_valid( @@ -435,10 +435,10 @@ def validate_and_normalize_inputs( def call( self, index_0: int, - index_1: bytes, + index_1: Union[bytes, str], index_2: str, tx_params: Optional[TxParams] = None, - ) -> Tuple[bytes, bytes, str]: + ) -> Tuple[Union[bytes, str], Union[bytes, str], str]: """Execute underlying contract method via eth_call. Tests decoding when the input and output are complex and have more than @@ -458,7 +458,7 @@ def call( def send_transaction( self, index_0: int, - index_1: bytes, + index_1: Union[bytes, str], index_2: str, tx_params: Optional[TxParams] = None, ) -> Union[HexBytes, bytes]: @@ -481,7 +481,7 @@ def send_transaction( def estimate_gas( self, index_0: int, - index_1: bytes, + index_1: Union[bytes, str], index_2: str, tx_params: Optional[TxParams] = None, ) -> int: @@ -510,7 +510,11 @@ def __init__( self.underlying_method = contract_function def validate_and_normalize_inputs( - self, _hash: bytes, v: int, r: bytes, s: bytes + self, + _hash: Union[bytes, str], + v: int, + r: Union[bytes, str], + s: Union[bytes, str], ): """Validate the inputs to the ecrecoverFn method.""" self.validator.assert_valid( @@ -531,10 +535,10 @@ def validate_and_normalize_inputs( def call( self, - _hash: bytes, + _hash: Union[bytes, str], v: int, - r: bytes, - s: bytes, + r: Union[bytes, str], + s: Union[bytes, str], tx_params: Optional[TxParams] = None, ) -> str: """Execute underlying contract method via eth_call. @@ -559,10 +563,10 @@ def call( def send_transaction( self, - _hash: bytes, + _hash: Union[bytes, str], v: int, - r: bytes, - s: bytes, + r: Union[bytes, str], + s: Union[bytes, str], tx_params: Optional[TxParams] = None, ) -> Union[HexBytes, bytes]: """Execute underlying contract method via eth_sendTransaction. @@ -589,10 +593,10 @@ def send_transaction( def estimate_gas( self, - _hash: bytes, + _hash: Union[bytes, str], v: int, - r: bytes, - s: bytes, + r: Union[bytes, str], + s: Union[bytes, str], tx_params: Optional[TxParams] = None, ) -> int: """Estimate gas consumption of method call.""" @@ -617,14 +621,16 @@ def __init__( super().__init__(provider, contract_address, validator) self.underlying_method = contract_function - def validate_and_normalize_inputs(self, a: bytes): + def validate_and_normalize_inputs(self, a: Union[bytes, str]): """Validate the inputs to the acceptsBytes method.""" self.validator.assert_valid( method_name="acceptsBytes", parameter_name="a", argument_value=a ) return a - def call(self, a: bytes, tx_params: Optional[TxParams] = None) -> None: + def call( + self, a: Union[bytes, str], tx_params: Optional[TxParams] = None + ) -> None: """Execute underlying contract method via eth_call. :param tx_params: transaction parameters @@ -635,7 +641,7 @@ def call(self, a: bytes, tx_params: Optional[TxParams] = None) -> None: return self.underlying_method(a).call(tx_params.as_dict()) def send_transaction( - self, a: bytes, tx_params: Optional[TxParams] = None + self, a: Union[bytes, str], tx_params: Optional[TxParams] = None ) -> Union[HexBytes, bytes]: """Execute underlying contract method via eth_sendTransaction. @@ -647,7 +653,7 @@ def send_transaction( return self.underlying_method(a).transact(tx_params.as_dict()) def estimate_gas( - self, a: bytes, tx_params: Optional[TxParams] = None + self, a: Union[bytes, str], tx_params: Optional[TxParams] = None ) -> int: """Estimate gas consumption of method call.""" (a) = self.validate_and_normalize_inputs(a) 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 98be012a96..a80c089c1c 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 @@ -136,8 +136,8 @@ ... expirationTimeSeconds=round( ... (datetime.utcnow() + timedelta(days=1)).timestamp() ... ), -... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), -... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20), +... makerFeeAssetData='0x', +... takerFeeAssetData='0x', ... chain_id=Web3(ganache).eth.chainId, ... ) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py index 42fe5457ca..138e125eeb 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py @@ -1,7 +1,7 @@ """Utilities to convert between JSON and Python-native objects.""" from copy import copy -from typing import cast, Dict +from typing import cast, Dict, Union from eth_utils import remove_0x_prefix @@ -60,11 +60,22 @@ def order_to_jsdict( """ jsdict = cast(Dict, copy(order)) - # encode bytes fields - jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex() - jsdict["makerFeeAssetData"] = "0x" + order["makerFeeAssetData"].hex() - jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex() - jsdict["takerFeeAssetData"] = "0x" + order["takerFeeAssetData"].hex() + def encode_bytes(bytes_or_str: Union[bytes, str]) -> bytes: + def ensure_hex_prefix(hex_str: str): + if hex_str[0:2] != "0x": + hex_str = "0x" + hex_str + return hex_str + + return ensure_hex_prefix( + cast(bytes, bytes_or_str).hex() + if isinstance(bytes_or_str, bytes) + else bytes_or_str + ) + + jsdict["makerAssetData"] = encode_bytes(order["makerAssetData"]) + jsdict["takerAssetData"] = encode_bytes(order["takerAssetData"]) + jsdict["makerFeeAssetData"] = encode_bytes(order["makerFeeAssetData"]) + jsdict["takerFeeAssetData"] = encode_bytes(order["takerFeeAssetData"]) jsdict["exchangeAddress"] = exchange_address 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 174e21dcba..b09e0431de 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 @@ -18,9 +18,9 @@ from enum import auto, Enum import json -from typing import Tuple -from pkg_resources import resource_string +from typing import cast, Tuple, Union +from pkg_resources import resource_string from mypy_extensions import TypedDict from eth_utils import keccak, remove_0x_prefix, to_bytes, to_checksum_address @@ -148,6 +148,13 @@ def int_to_32_big_endian_bytes(i: int): + pad_20_bytes_to_32(to_bytes(hexstr=exchange_address)) ) + def ensure_bytes(str_or_bytes: Union[str, bytes]) -> bytes: + return ( + to_bytes(hexstr=cast(bytes, str_or_bytes)) + if isinstance(str_or_bytes, str) + else str_or_bytes + ) + eip712_order_struct_hash = keccak( _Constants.eip712_order_schema_hash + pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"])) @@ -160,10 +167,10 @@ def int_to_32_big_endian_bytes(i: int): + int_to_32_big_endian_bytes(int(order["takerFee"])) + int_to_32_big_endian_bytes(int(order["expirationTimeSeconds"])) + int_to_32_big_endian_bytes(int(order["salt"])) - + keccak(to_bytes(hexstr=order["makerAssetData"].hex())) - + keccak(to_bytes(hexstr=order["takerAssetData"].hex())) - + keccak(to_bytes(hexstr=order["makerFeeAssetData"].hex())) - + keccak(to_bytes(hexstr=order["takerFeeAssetData"].hex())) + + keccak(ensure_bytes(order["makerAssetData"])) + + keccak(ensure_bytes(order["takerAssetData"])) + + keccak(ensure_bytes(order["makerFeeAssetData"])) + + keccak(ensure_bytes(order["takerFeeAssetData"])) ) return keccak( From 3a8d2a6cf8aedf7e645a8f459eb3a33de82e33d4 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 23 Oct 2019 12:12:52 -0400 Subject: [PATCH 14/28] Fix CircleCI build artifacts for gen'd python I swear the previous way was working before, but it wasn't working now, so this fixes it. --- .circleci/config.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 87fc939706..96e6213f50 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -226,6 +226,12 @@ jobs: cd python-packages ./parallel_without_sra_client coverage run setup.py test ./build_docs + - run: + command: | + # copy generated wrappers into contract_wrappers/build, + # JUST so CircleCI will persist them as build artifacts. + cd python-packages/contract_wrappers/src/zero_ex + for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done - save_cache: key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }} paths: @@ -250,8 +256,6 @@ jobs: key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }} paths: - ~/repo/python-packages/sra_client/.coverage - - store_artifacts: - path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py - store_artifacts: path: ~/repo/python-packages/contract_addresses/build - store_artifacts: From 348a7184fc1a55bb73e98cc0252060644ac92a31 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Wed, 23 Oct 2019 16:53:23 -0400 Subject: [PATCH 15/28] Accept a provider OR a Web3 object In various places. This allows the caller to install middleware (which in web3.py is installed on a Web3 object, not on a provider) before executing any RPC calls, which is important for the case where one wants to produce signatures locally before submitting to a remote node. --- .../templates/Python/contract.handlebars | 34 ++- .../Python/partials/method_class.handlebars | 4 +- .../output/python/abi_gen_dummy/__init__.py | 262 +++++++++++------- .../output/python/lib_dummy/__init__.py | 34 ++- .../output/python/test_lib_dummy/__init__.py | 54 +++- .../src/zero_ex/contract_wrappers/__init__.py | 6 +- .../src/zero_ex/contract_wrappers/bases.py | 25 +- .../contract_wrappers/exchange/validator.py | 28 +- .../test/test_base_contract_method.py | 2 +- .../test/test_exchange_wrapper.py | 19 +- .../test/test_local_message_signer.py | 18 +- .../src/zero_ex/order_utils/__init__.py | 36 ++- .../order_utils/stubs/web3/__init__.pyi | 2 + 13 files changed, 357 insertions(+), 167 deletions(-) diff --git a/packages/abi-gen/templates/Python/contract.handlebars b/packages/abi-gen/templates/Python/contract.handlebars index 38afd29ef0..dbabff2b55 100644 --- a/packages/abi-gen/templates/Python/contract.handlebars +++ b/packages/abi-gen/templates/Python/contract.handlebars @@ -65,13 +65,14 @@ class {{contractName}}: def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, validator: {{contractName}}Validator = None, ): """Get an instance of wrapper for smart contract. - :param provider: instance of :class:`web3.providers.base.BaseProvider` + :param web3_or_provider: Either an instance of `web3.Web3`:code: or + `web3.providers.base.BaseProvider`:code: :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ @@ -80,9 +81,18 @@ class {{contractName}}: self.contract_address = contract_address if not validator: - validator = {{contractName}}Validator(provider, contract_address) - - web3 = Web3(provider) + validator = {{contractName}}Validator(web3_or_provider, contract_address) + + web3 = None + if isinstance(web3_or_provider, BaseProvider): + web3 = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3 = web3_or_provider + if web3 is None: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) # if any middleware was imported, inject it try: @@ -90,10 +100,14 @@ class {{contractName}}: except NameError: pass else: - for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore - middleware['function'], layer=middleware['layer'], - ) + try: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware['function'], layer=middleware['layer'], + ) + except ValueError as value_error: + if value_error.args == ("You can't add the same un-named instance twice",): + pass self._web3_eth = ( web3.eth # type: ignore # pylint: disable=no-member @@ -103,7 +117,7 @@ class {{contractName}}: functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions {{#each methods}} - self.{{toPythonIdentifier this.languageSpecificName}} = {{toPythonClassname this.languageSpecificName}}Method(provider, contract_address, functions.{{this.name}}, validator) + self.{{toPythonIdentifier this.languageSpecificName}} = {{toPythonClassname this.languageSpecificName}}Method(web3_or_provider, contract_address, functions.{{this.name}}, validator) {{/each}} {{/if}} diff --git a/packages/abi-gen/templates/Python/partials/method_class.handlebars b/packages/abi-gen/templates/Python/partials/method_class.handlebars index 6bbbb07684..2586142662 100644 --- a/packages/abi-gen/templates/Python/partials/method_class.handlebars +++ b/packages/abi-gen/templates/Python/partials/method_class.handlebars @@ -2,9 +2,9 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod): """Various interfaces to the {{this.name}} method.""" - def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + def __init__(self, web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator=None): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function {{#if inputs}} diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index 11fdfe76e6..d2536618cb 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -183,13 +183,13 @@ class SimpleRequireMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: @@ -223,13 +223,13 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, a: List[Union[bytes, str]]): @@ -285,13 +285,13 @@ class SimpleInputSimpleOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, index_0: int): @@ -345,13 +345,13 @@ class WithdrawMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, wad: int): @@ -400,13 +400,13 @@ class MultiInputMultiOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs( @@ -500,13 +500,13 @@ class EcrecoverFnMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs( @@ -612,13 +612,13 @@ class AcceptsBytesMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, a: Union[bytes, str]): @@ -666,13 +666,13 @@ class NoInputSimpleOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> int: @@ -710,13 +710,13 @@ class RevertWithConstantMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: @@ -750,13 +750,13 @@ class SimpleRevertMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: @@ -792,13 +792,13 @@ class MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod( def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> Tuple0x1b9da225: @@ -832,13 +832,13 @@ class NestedStructOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5: @@ -872,13 +872,13 @@ class RequireWithConstantMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: @@ -912,13 +912,13 @@ class WithAddressInputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs( @@ -1020,13 +1020,13 @@ class StructInputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, s: Tuple0xcf8ad995): @@ -1074,13 +1074,13 @@ class NonPureMethodMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call( @@ -1115,13 +1115,13 @@ class ComplexInputComplexOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, complex_input: Tuple0xf95128ef): @@ -1185,13 +1185,13 @@ class NoInputNoOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: @@ -1229,13 +1229,13 @@ class SimplePureFunctionWithInputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, x: int): @@ -1285,13 +1285,13 @@ class NonPureMethodThatReturnsNothingMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call( @@ -1326,13 +1326,13 @@ class SimplePureFunctionMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> int: @@ -1366,13 +1366,13 @@ class NestedStructInputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, n: Tuple0xc9bdd2d5): @@ -1422,13 +1422,13 @@ class MethodReturningMultipleValuesMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> Tuple[int, str]: @@ -1462,13 +1462,13 @@ class MethodReturningArrayOfStructsMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call( @@ -1504,13 +1504,13 @@ class EmitSimpleEventMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call( @@ -1545,13 +1545,13 @@ class StructOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995: @@ -1589,13 +1589,13 @@ class PureFunctionWithConstantMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> int: @@ -1629,13 +1629,13 @@ class SimpleInputNoOutputMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, index_0: int): @@ -1689,13 +1689,13 @@ class OverloadedMethod2Method(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, a: str): @@ -1743,13 +1743,13 @@ class OverloadedMethod1Method(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, a: int): @@ -1952,13 +1952,14 @@ class AbiGenDummy: def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, validator: AbiGenDummyValidator = None, ): """Get an instance of wrapper for smart contract. - :param provider: instance of :class:`web3.providers.base.BaseProvider` + :param web3_or_provider: Either an instance of `web3.Web3`:code: or + `web3.providers.base.BaseProvider`:code: :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ @@ -1967,9 +1968,20 @@ def __init__( self.contract_address = contract_address if not validator: - validator = AbiGenDummyValidator(provider, contract_address) + validator = AbiGenDummyValidator( + web3_or_provider, contract_address + ) - web3 = Web3(provider) + web3 = None + if isinstance(web3_or_provider, BaseProvider): + web3 = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3 = web3_or_provider + if web3 is None: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) # if any middleware was imported, inject it try: @@ -1977,10 +1989,16 @@ def __init__( except NameError: pass else: - for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore - middleware["function"], layer=middleware["layer"] - ) + try: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware["function"], layer=middleware["layer"] + ) + except ValueError as value_error: + if value_error.args == ( + "You can't add the same un-named instance twice", + ): + pass self._web3_eth = ( web3.eth # type: ignore # pylint: disable=no-member @@ -1992,162 +2010,210 @@ def __init__( ).functions self.simple_require = SimpleRequireMethod( - provider, contract_address, functions.simpleRequire, validator + web3_or_provider, + contract_address, + functions.simpleRequire, + validator, ) self.accepts_an_array_of_bytes = AcceptsAnArrayOfBytesMethod( - provider, + web3_or_provider, contract_address, functions.acceptsAnArrayOfBytes, validator, ) self.simple_input_simple_output = SimpleInputSimpleOutputMethod( - provider, + web3_or_provider, contract_address, functions.simpleInputSimpleOutput, validator, ) self.withdraw = WithdrawMethod( - provider, contract_address, functions.withdraw, validator + web3_or_provider, contract_address, functions.withdraw, validator ) self.multi_input_multi_output = MultiInputMultiOutputMethod( - provider, + web3_or_provider, contract_address, functions.multiInputMultiOutput, validator, ) self.ecrecover_fn = EcrecoverFnMethod( - provider, contract_address, functions.ecrecoverFn, validator + web3_or_provider, + contract_address, + functions.ecrecoverFn, + validator, ) self.accepts_bytes = AcceptsBytesMethod( - provider, contract_address, functions.acceptsBytes, validator + web3_or_provider, + contract_address, + functions.acceptsBytes, + validator, ) self.no_input_simple_output = NoInputSimpleOutputMethod( - provider, + web3_or_provider, contract_address, functions.noInputSimpleOutput, validator, ) self.revert_with_constant = RevertWithConstantMethod( - provider, contract_address, functions.revertWithConstant, validator + web3_or_provider, + contract_address, + functions.revertWithConstant, + validator, ) self.simple_revert = SimpleRevertMethod( - provider, contract_address, functions.simpleRevert, validator + web3_or_provider, + contract_address, + functions.simpleRevert, + validator, ) self.method_using_nested_struct_with_inner_struct_not_used_elsewhere = MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod( - provider, + web3_or_provider, contract_address, functions.methodUsingNestedStructWithInnerStructNotUsedElsewhere, validator, ) self.nested_struct_output = NestedStructOutputMethod( - provider, contract_address, functions.nestedStructOutput, validator + web3_or_provider, + contract_address, + functions.nestedStructOutput, + validator, ) self.require_with_constant = RequireWithConstantMethod( - provider, + web3_or_provider, contract_address, functions.requireWithConstant, validator, ) self.with_address_input = WithAddressInputMethod( - provider, contract_address, functions.withAddressInput, validator + web3_or_provider, + contract_address, + functions.withAddressInput, + validator, ) self.struct_input = StructInputMethod( - provider, contract_address, functions.structInput, validator + web3_or_provider, + contract_address, + functions.structInput, + validator, ) self.non_pure_method = NonPureMethodMethod( - provider, contract_address, functions.nonPureMethod, validator + web3_or_provider, + contract_address, + functions.nonPureMethod, + validator, ) self.complex_input_complex_output = ComplexInputComplexOutputMethod( - provider, + web3_or_provider, contract_address, functions.complexInputComplexOutput, validator, ) self.no_input_no_output = NoInputNoOutputMethod( - provider, contract_address, functions.noInputNoOutput, validator + web3_or_provider, + contract_address, + functions.noInputNoOutput, + validator, ) self.simple_pure_function_with_input = SimplePureFunctionWithInputMethod( - provider, + web3_or_provider, contract_address, functions.simplePureFunctionWithInput, validator, ) self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod( - provider, + web3_or_provider, contract_address, functions.nonPureMethodThatReturnsNothing, validator, ) self.simple_pure_function = SimplePureFunctionMethod( - provider, contract_address, functions.simplePureFunction, validator + web3_or_provider, + contract_address, + functions.simplePureFunction, + validator, ) self.nested_struct_input = NestedStructInputMethod( - provider, contract_address, functions.nestedStructInput, validator + web3_or_provider, + contract_address, + functions.nestedStructInput, + validator, ) self.method_returning_multiple_values = MethodReturningMultipleValuesMethod( - provider, + web3_or_provider, contract_address, functions.methodReturningMultipleValues, validator, ) self.method_returning_array_of_structs = MethodReturningArrayOfStructsMethod( - provider, + web3_or_provider, contract_address, functions.methodReturningArrayOfStructs, validator, ) self.emit_simple_event = EmitSimpleEventMethod( - provider, contract_address, functions.emitSimpleEvent, validator + web3_or_provider, + contract_address, + functions.emitSimpleEvent, + validator, ) self.struct_output = StructOutputMethod( - provider, contract_address, functions.structOutput, validator + web3_or_provider, + contract_address, + functions.structOutput, + validator, ) self.pure_function_with_constant = PureFunctionWithConstantMethod( - provider, + web3_or_provider, contract_address, functions.pureFunctionWithConstant, validator, ) self.simple_input_no_output = SimpleInputNoOutputMethod( - provider, + web3_or_provider, contract_address, functions.simpleInputNoOutput, validator, ) self.overloaded_method2 = OverloadedMethod2Method( - provider, contract_address, functions.overloadedMethod, validator + web3_or_provider, + contract_address, + functions.overloadedMethod, + validator, ) self.overloaded_method1 = OverloadedMethod1Method( - provider, contract_address, functions.overloadedMethod, validator + web3_or_provider, + contract_address, + functions.overloadedMethod, + validator, ) def get_withdrawal_event( diff --git a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py index e5ec5d76cd..8a7e70e454 100644 --- a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py @@ -52,13 +52,14 @@ class LibDummy: def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, validator: LibDummyValidator = None, ): """Get an instance of wrapper for smart contract. - :param provider: instance of :class:`web3.providers.base.BaseProvider` + :param web3_or_provider: Either an instance of `web3.Web3`:code: or + `web3.providers.base.BaseProvider`:code: :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ @@ -67,9 +68,18 @@ def __init__( self.contract_address = contract_address if not validator: - validator = LibDummyValidator(provider, contract_address) - - web3 = Web3(provider) + validator = LibDummyValidator(web3_or_provider, contract_address) + + web3 = None + if isinstance(web3_or_provider, BaseProvider): + web3 = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3 = web3_or_provider + if web3 is None: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) # if any middleware was imported, inject it try: @@ -77,10 +87,16 @@ def __init__( except NameError: pass else: - for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore - middleware["function"], layer=middleware["layer"] - ) + try: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware["function"], layer=middleware["layer"] + ) + except ValueError as value_error: + if value_error.args == ( + "You can't add the same un-named instance twice", + ): + pass self._web3_eth = ( web3.eth # type: ignore # pylint: disable=no-member diff --git a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py index 7af4d6b224..ee16b23642 100644 --- a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py @@ -51,13 +51,13 @@ class PublicAddConstantMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, x: int): @@ -107,13 +107,13 @@ class PublicAddOneMethod(ContractMethod): def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator = None, ): """Persist instance data.""" - super().__init__(provider, contract_address, validator) + super().__init__(web3_or_provider, contract_address, validator) self.underlying_method = contract_function def validate_and_normalize_inputs(self, x: int): @@ -172,13 +172,14 @@ class TestLibDummy: def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, validator: TestLibDummyValidator = None, ): """Get an instance of wrapper for smart contract. - :param provider: instance of :class:`web3.providers.base.BaseProvider` + :param web3_or_provider: Either an instance of `web3.Web3`:code: or + `web3.providers.base.BaseProvider`:code: :param contract_address: where the contract has been deployed :param validator: for validation of method inputs. """ @@ -187,9 +188,20 @@ def __init__( self.contract_address = contract_address if not validator: - validator = TestLibDummyValidator(provider, contract_address) - - web3 = Web3(provider) + validator = TestLibDummyValidator( + web3_or_provider, contract_address + ) + + web3 = None + if isinstance(web3_or_provider, BaseProvider): + web3 = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3 = web3_or_provider + if web3 is None: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) # if any middleware was imported, inject it try: @@ -197,10 +209,16 @@ def __init__( except NameError: pass else: - for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore - middleware["function"], layer=middleware["layer"] - ) + try: + for middleware in MIDDLEWARE: + web3.middleware_onion.inject( # type: ignore + middleware["function"], layer=middleware["layer"] + ) + except ValueError as value_error: + if value_error.args == ( + "You can't add the same un-named instance twice", + ): + pass self._web3_eth = ( web3.eth # type: ignore # pylint: disable=no-member @@ -212,11 +230,17 @@ def __init__( ).functions self.public_add_constant = PublicAddConstantMethod( - provider, contract_address, functions.publicAddConstant, validator + web3_or_provider, + contract_address, + functions.publicAddConstant, + validator, ) self.public_add_one = PublicAddOneMethod( - provider, contract_address, functions.publicAddOne, validator + web3_or_provider, + contract_address, + functions.publicAddOne, + validator, ) @staticmethod 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 a80c089c1c..558588f179 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 @@ -92,11 +92,11 @@ >>> from zero_ex.contract_wrappers.erc20_token import ERC20Token >>> zrx_token = ERC20Token( -... provider=ganache, +... web3_or_provider=ganache, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token, ... ) >>> weth_token = ERC20Token( -... provider=ganache, +... web3_or_provider=ganache, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, ... ) @@ -165,7 +165,7 @@ >>> from zero_ex.contract_wrappers.exchange import Exchange >>> exchange = Exchange( -... provider=ganache, +... web3_or_provider=ganache, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, ... ) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py index 39280f4efe..a3fe5c6ff3 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py @@ -1,6 +1,6 @@ """Base wrapper class for accessing ethereum smart contracts.""" -from typing import Any +from typing import Any, Union from eth_utils import is_address, to_checksum_address from web3 import Web3 @@ -12,7 +12,11 @@ class Validator: """Base class for validating inputs to methods.""" - def __init__(self, provider: BaseProvider, contract_address: str): + def __init__( + self, + web3_or_provider: Union[Web3, BaseProvider], + contract_address: str, + ): """Initialize the instance.""" def assert_valid( @@ -32,7 +36,7 @@ class ContractMethod: def __init__( self, - provider: BaseProvider, + web3_or_provider: Union[Web3, BaseProvider], contract_address: str, validator: Validator = None, ): @@ -42,9 +46,20 @@ def __init__( :param contract_address: Where the contract has been deployed to. :param validator: Used to validate method inputs. """ - self._web3_eth = Web3(provider).eth # pylint: disable=no-member + web3 = None + if isinstance(web3_or_provider, BaseProvider): + web3 = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3 = web3_or_provider + if web3 is None: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) + + self._web3_eth = web3.eth # pylint: disable=no-member if validator is None: - validator = Validator(provider, contract_address) + validator = Validator(web3_or_provider, contract_address) self.validator = validator @staticmethod diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py index 5f56340ffd..a7264ab3d4 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py @@ -1,6 +1,6 @@ """Validate inputs to the Exchange contract.""" -from typing import Any +from typing import Any, Union from web3 import Web3 from web3.providers.base import BaseProvider @@ -14,13 +14,29 @@ class ExchangeValidator(Validator): """Validate inputs to Exchange methods.""" - def __init__(self, provider: BaseProvider, contract_address: str): + def __init__( + self, + web3_or_provider: Union[Web3, BaseProvider], + contract_address: str, + ): """Initialize the class.""" - super().__init__(provider, contract_address) + super().__init__(web3_or_provider, contract_address) + + web3 = None + if isinstance(web3_or_provider, BaseProvider): + web3 = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3 = web3_or_provider + if web3 is None: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) + self.contract_address = contract_address - self.chain_id = Web3( # type: ignore # pylint: disable=no-member - provider - ).eth.chainId + self.chain_id = ( + web3.eth.chainId # type: ignore # pylint: disable=no-member + ) def assert_valid( self, method_name: str, parameter_name: str, argument_value: Any diff --git a/python-packages/contract_wrappers/test/test_base_contract_method.py b/python-packages/contract_wrappers/test/test_base_contract_method.py index 6b558b9fad..fd6e4d1731 100644 --- a/python-packages/contract_wrappers/test/test_base_contract_method.py +++ b/python-packages/contract_wrappers/test/test_base_contract_method.py @@ -10,6 +10,6 @@ def contract_wrapper(ganache_provider): """Get a ContractMethod instance for testing.""" return ContractMethod( - provider=ganache_provider, + web3_or_provider=ganache_provider, contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, ) diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index 582d35f005..1da326d793 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -21,7 +21,7 @@ def exchange_wrapper(ganache_provider): """Get an Exchange wrapper instance.""" return Exchange( - provider=ganache_provider, + web3_or_provider=ganache_provider, contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, ) @@ -149,3 +149,20 @@ def test_exchange_wrapper__batch_fill_orders( assert_fill_log( fill_events[index].args, maker, taker, order, order_hashes[index] ) + + +def test_two_instantiations_with_web3_objects(web3_instance): + """Test that instantiating two Exchange objects doesn't raise. + + When instantiating an Exchange object with a web3 client (rather than a + provider) there was a bug encountered where web3.py was giving an error + when trying to install the rich-revert-handling middleware on the web3 + client, an error saying "can't install this same middleware instance + again." Test that that bug isn't occurring. + """ + exchange = Exchange( # pylint: disable=unused-variable + web3_instance, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange + ) + exchange2 = Exchange( # pylint: disable=unused-variable + web3_instance, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange + ) diff --git a/python-packages/middlewares/test/test_local_message_signer.py b/python-packages/middlewares/test/test_local_message_signer.py index 6fbe0ba819..c49f667f0d 100644 --- a/python-packages/middlewares/test/test_local_message_signer.py +++ b/python-packages/middlewares/test/test_local_message_signer.py @@ -7,11 +7,7 @@ from zero_ex.middlewares.local_message_signer import ( construct_local_message_signer, ) -from zero_ex.order_utils import ( - generate_order_hash_hex, - is_valid_signature, - sign_hash, -) +from zero_ex.order_utils import generate_order_hash_hex, sign_hash def test_local_message_signer__sign_order(): @@ -46,7 +42,11 @@ def test_local_message_signer__sign_order(): "takerAssetAmount": 0, "expirationTimeSeconds": 0, } - order_hash = generate_order_hash_hex(order, exchange, chain_id=1337) - signature = sign_hash(ganache, to_checksum_address(address), order_hash) - assert signature == expected_signature - assert is_valid_signature(ganache, order_hash, signature, address) + assert ( + sign_hash( + web3_instance, + to_checksum_address(address), + generate_order_hash_hex(order, exchange, chain_id=1337), + ) + == expected_signature + ) 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 b09e0431de..f14dd3570d 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 @@ -284,11 +284,14 @@ def _convert_ec_signature_to_vrs_hex(signature: ECSignature) -> str: def sign_hash( - provider: BaseProvider, signer_address: str, hash_hex: str + web3_or_provider: Union[Web3, BaseProvider], + signer_address: str, + hash_hex: str, ) -> str: """Sign a message with the given hash, and return the signature. - :param provider: A Web3 provider. + :param web3_or_provider: Either an instance of `web3.Web3`:code: or + `web3.providers.base.BaseProvider`:code: :param signer_address: The address of the signing account. :param hash_hex: A hex string representing the hash, like that returned from `generate_order_hash_hex()`:code:. @@ -302,11 +305,20 @@ def sign_hash( ... ) '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03' """ # noqa: E501 (line too long) - assert_is_provider(provider, "provider") + web3_instance = None + if isinstance(web3_or_provider, BaseProvider): + web3_instance = Web3(web3_or_provider) + elif isinstance(web3_or_provider, Web3): + web3_instance = web3_or_provider + else: + raise TypeError( + "Expected parameter 'web3_or_provider' to be an instance of either" + + " Web3 or BaseProvider" + ) + assert_is_address(signer_address, "signer_address") assert_is_hex_string(hash_hex, "hash_hex") - web3_instance = Web3(provider) # false positive from pylint: disable=no-member signature = web3_instance.eth.sign( # type: ignore signer_address, hexstr=hash_hex.replace("0x", "") @@ -332,7 +344,10 @@ def sign_hash( ) valid = is_valid_signature( - provider, hash_hex, signature_as_vrst_hex, signer_address + web3_instance.provider, + hash_hex, + signature_as_vrst_hex, + signer_address, ) if valid is True: @@ -347,7 +362,10 @@ def sign_hash( ).hex() ) valid = is_valid_signature( - provider, hash_hex, signature_as_vrst_hex, signer_address + web3_instance.provider, + hash_hex, + signature_as_vrst_hex, + signer_address, ) if valid is True: @@ -360,7 +378,9 @@ def sign_hash( def sign_hash_to_bytes( - provider: BaseProvider, signer_address: str, hash_hex: str + web3_or_provider: Union[Web3, BaseProvider], + signer_address: str, + hash_hex: str, ) -> bytes: """Sign a message with the given hash, and return the signature. @@ -373,5 +393,5 @@ def sign_hash_to_bytes( '1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03' """ # noqa: E501 (line too long) return remove_0x_prefix( - sign_hash(provider, signer_address, hash_hex) + sign_hash(web3_or_provider, signer_address, hash_hex) ).encode(encoding="utf_8") diff --git a/python-packages/order_utils/stubs/web3/__init__.pyi b/python-packages/order_utils/stubs/web3/__init__.pyi index a1dd77adb4..a23a5141a8 100644 --- a/python-packages/order_utils/stubs/web3/__init__.pyi +++ b/python-packages/order_utils/stubs/web3/__init__.pyi @@ -34,4 +34,6 @@ class Web3: ... ... + provider: BaseProvider + ... From b2fbbe785cfe723c2474ea3291e26669aae70bcd Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 25 Oct 2019 15:59:19 -0400 Subject: [PATCH 16/28] wrapper base: don't assume there are accounts --- .../src/zero_ex/contract_wrappers/bases.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py index a3fe5c6ff3..75878c77e0 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py @@ -74,8 +74,13 @@ def normalize_tx_params(self, tx_params) -> TxParams: if not tx_params: tx_params = TxParams() if not tx_params.from_: - tx_params.from_ = ( - self._web3_eth.defaultAccount or self._web3_eth.accounts[0] + tx_params.from_ = self._web3_eth.defaultAccount or ( + self._web3_eth.accounts[0] + if len(self._web3_eth.accounts) > 0 + else None + ) + if tx_params.from_: + tx_params.from_ = self.validate_and_checksum_address( + tx_params.from_ ) - tx_params.from_ = self.validate_and_checksum_address(tx_params.from_) return tx_params From ea7760983d5f1b9d4e683d580b8e8704e2910d98 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Sun, 27 Oct 2019 13:27:53 -0400 Subject: [PATCH 17/28] Eliminate some inline linter directives --- .../abi-gen/templates/Python/contract.handlebars | 6 ++---- .../output/python/abi_gen_dummy/__init__.py | 6 ++---- .../test-cli/output/python/lib_dummy/__init__.py | 6 ++---- .../output/python/test_lib_dummy/__init__.py | 6 ++---- .../zero_ex/contract_wrappers/exchange/validator.py | 4 +--- .../contract_wrappers/stubs/web3/__init__.pyi | 13 ++++++++++++- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/abi-gen/templates/Python/contract.handlebars b/packages/abi-gen/templates/Python/contract.handlebars index dbabff2b55..5fa8dcfa51 100644 --- a/packages/abi-gen/templates/Python/contract.handlebars +++ b/packages/abi-gen/templates/Python/contract.handlebars @@ -102,16 +102,14 @@ class {{contractName}}: else: try: for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore + web3.middleware_onion.inject( middleware['function'], layer=middleware['layer'], ) except ValueError as value_error: if value_error.args == ("You can't add the same un-named instance twice",): pass - self._web3_eth = ( - web3.eth # type: ignore # pylint: disable=no-member - ) + self._web3_eth = web3.eth {{#if methods}} functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index d2536618cb..3982f02477 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -1991,7 +1991,7 @@ def __init__( else: try: for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore + web3.middleware_onion.inject( middleware["function"], layer=middleware["layer"] ) except ValueError as value_error: @@ -2000,9 +2000,7 @@ def __init__( ): pass - self._web3_eth = ( - web3.eth # type: ignore # pylint: disable=no-member - ) + self._web3_eth = web3.eth functions = self._web3_eth.contract( address=to_checksum_address(contract_address), diff --git a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py index 8a7e70e454..11d4a39bef 100644 --- a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py @@ -89,7 +89,7 @@ def __init__( else: try: for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore + web3.middleware_onion.inject( middleware["function"], layer=middleware["layer"] ) except ValueError as value_error: @@ -98,9 +98,7 @@ def __init__( ): pass - self._web3_eth = ( - web3.eth # type: ignore # pylint: disable=no-member - ) + self._web3_eth = web3.eth @staticmethod def abi(): diff --git a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py index ee16b23642..321893ee3a 100644 --- a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py @@ -211,7 +211,7 @@ def __init__( else: try: for middleware in MIDDLEWARE: - web3.middleware_onion.inject( # type: ignore + web3.middleware_onion.inject( middleware["function"], layer=middleware["layer"] ) except ValueError as value_error: @@ -220,9 +220,7 @@ def __init__( ): pass - self._web3_eth = ( - web3.eth # type: ignore # pylint: disable=no-member - ) + self._web3_eth = web3.eth functions = self._web3_eth.contract( address=to_checksum_address(contract_address), diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py index a7264ab3d4..bf9ec40a3b 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py @@ -34,9 +34,7 @@ def __init__( ) self.contract_address = contract_address - self.chain_id = ( - web3.eth.chainId # type: ignore # pylint: disable=no-member - ) + self.chain_id = web3.eth.chainId def assert_valid( self, method_name: str, parameter_name: str, argument_value: Any diff --git a/python-packages/contract_wrappers/stubs/web3/__init__.pyi b/python-packages/contract_wrappers/stubs/web3/__init__.pyi index ab531c1e73..1dfa4bb5f5 100644 --- a/python-packages/contract_wrappers/stubs/web3/__init__.pyi +++ b/python-packages/contract_wrappers/stubs/web3/__init__.pyi @@ -26,16 +26,24 @@ class Web3: class middleware_stack: @staticmethod def get(key: str) -> Callable: ... + + def inject( + self, middleware_func: object, layer: object + ) -> None: ... + ... + middleware_onion: middleware_stack + class net: version: str ... - class eth: + class Eth: defaultAccount: str accounts: List[str] + chainId: int ... class account: @@ -53,4 +61,7 @@ class Web3: @staticmethod def isAddress(address: str) -> bool: ... ... + + eth: Eth + ... From 6eb5ca00e5163cac421a99c46de66b1da658c5e6 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 19:46:53 -0400 Subject: [PATCH 18/28] make CHANGELOGs be REVERSE chronological --- python-packages/contract_artifacts/CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python-packages/contract_artifacts/CHANGELOG.md b/python-packages/contract_artifacts/CHANGELOG.md index 3a4a5cca80..16afedf6e6 100644 --- a/python-packages/contract_artifacts/CHANGELOG.md +++ b/python-packages/contract_artifacts/CHANGELOG.md @@ -1,9 +1,9 @@ # Changelog -## 2.0.0 - 2019-01-09 - -- Initial release. - ## 2.0.1 - 2019-04-30 - Expanded documentation. + +## 2.0.0 - 2019-01-09 + +- Initial release. From bc354e341b3a1c7c06f819fc06eb2413569edada Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 28 Oct 2019 20:06:02 -0400 Subject: [PATCH 19/28] Update CHANGELOG entries and bump version numbers --- packages/abi-gen/CHANGELOG.json | 21 +++++++++++++++++++ .../contract_artifacts/CHANGELOG.md | 4 ++++ python-packages/contract_artifacts/setup.py | 2 +- .../contract_wrappers/CHANGELOG.md | 9 ++++++++ python-packages/contract_wrappers/setup.py | 2 +- python-packages/json_schemas/CHANGELOG.md | 4 ++++ python-packages/json_schemas/setup.py | 2 +- python-packages/middlewares/CHANGELOG.md | 2 +- python-packages/order_utils/CHANGELOG.md | 6 ++++++ python-packages/order_utils/setup.py | 2 +- 10 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 73b8d81211..079e1f902b 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,4 +1,25 @@ [ + { + "version": "4.5.0-beta.0", + "changes": [ + { + "note": "In Python wrappers, accept string arguments to bytes parameters", + "pr": 2284 + }, + { + "note": "In Python wrappers, support module-local, Web3.py-compatible middleware", + "pr": 2284 + }, + { + "note": "In Python wrappers, allow contracts to be instantiated with EITHER a Web3.py BaseProvider OR a Web3 client object", + "pr": 2284 + }, + { + "note": "In Python wrappers, fix bug with casting some bytes objects using bytes.fromhex()", + "pr": 2284 + } + ] + }, { "version": "4.4.0-beta.0", "changes": [ diff --git a/python-packages/contract_artifacts/CHANGELOG.md b/python-packages/contract_artifacts/CHANGELOG.md index 16afedf6e6..0a67e1b4fe 100644 --- a/python-packages/contract_artifacts/CHANGELOG.md +++ b/python-packages/contract_artifacts/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.0.0 - TBD + +- Updated with artifacts for version 3 of the protocol. + ## 2.0.1 - 2019-04-30 - Expanded documentation. diff --git a/python-packages/contract_artifacts/setup.py b/python-packages/contract_artifacts/setup.py index 60cd883979..d4ea9b5577 100755 --- a/python-packages/contract_artifacts/setup.py +++ b/python-packages/contract_artifacts/setup.py @@ -152,7 +152,7 @@ def run_tests(self): setup( name="0x-contract-artifacts", - version="2.0.1", + version="3.0.0", description="0x smart contract compilation artifacts", long_description=README_MD, long_description_content_type="text/markdown", diff --git a/python-packages/contract_wrappers/CHANGELOG.md b/python-packages/contract_wrappers/CHANGELOG.md index 69ed9b8caa..24161a8b93 100644 --- a/python-packages/contract_wrappers/CHANGELOG.md +++ b/python-packages/contract_wrappers/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 2.0.0 - TBD + +- Updated for version 3 of the protocol. +- Allow wrappers to be instantiated with EITHER a Web3.py `BaseProvider` OR an already-instantiated `Web3` client object. +- Accept `str`ing arguments to `bytes` contract method parameters. +- Expanded documentation examples. +- Moved methods `jsdict_to_order()` and `order_to_jsdict()` from `zero_ex.contract_wrappers.exchange.types` to `zero_ex.contract_wrappers.order_conversions`. +- Changed field name `zero_ex.contract_wrappers.tx_params.TxParams.gasPrice` to `.gas_price`. + ## 1.1.0 - 2019-08-14 - Added wrapper for DevUtils contract. diff --git a/python-packages/contract_wrappers/setup.py b/python-packages/contract_wrappers/setup.py index a3c32fa126..301fb6f514 100755 --- a/python-packages/contract_wrappers/setup.py +++ b/python-packages/contract_wrappers/setup.py @@ -196,7 +196,7 @@ def run(self): setup( name="0x-contract-wrappers", - version="1.1.0", + version="2.0.0", description="Python wrappers for 0x smart contracts", long_description=README_MD, long_description_content_type="text/markdown", diff --git a/python-packages/json_schemas/CHANGELOG.md b/python-packages/json_schemas/CHANGELOG.md index 88d9babcd2..b06c98ec8c 100644 --- a/python-packages/json_schemas/CHANGELOG.md +++ b/python-packages/json_schemas/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.1.1 - TBD + +- Removed dev dependency on package `0x-contract-wrappers` + ## 1.1.0 - 2019-08-09 - Added `verifyingContractAddress` to `/zeroExTransactionSchema`. diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index 34b92c924c..f943260847 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -143,7 +143,7 @@ def run(self): setup( name="0x-json-schemas", - version="1.1.0", + version="1.1.1", description="JSON schemas for 0x applications", long_description=README_MD, long_description_content_type="text/markdown", diff --git a/python-packages/middlewares/CHANGELOG.md b/python-packages/middlewares/CHANGELOG.md index 1639a8f546..c1ebd2f6ed 100644 --- a/python-packages/middlewares/CHANGELOG.md +++ b/python-packages/middlewares/CHANGELOG.md @@ -1,5 +1,5 @@ # Changelog -## 1.0.0 - 2019-04-30 +## 1.0.0 - TBD - Initial release. diff --git a/python-packages/order_utils/CHANGELOG.md b/python-packages/order_utils/CHANGELOG.md index 8a329b289f..afa979ae92 100644 --- a/python-packages/order_utils/CHANGELOG.md +++ b/python-packages/order_utils/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 4.0.0 - TBD + +- Upgraded to protocol version 3. +- `is_valid_signature()` now returns just a boolean. (Formerly, it returned a tuple consisting of the boolean and a reason string.) +- Allow `sign_hash()` to be called with EITHER a Web3.py `BaseProvider` OR an already-instantiated `Web3` client object. + ## 3.0.1 - 2019-08-09 - Fixed dependencies: changed `deprecated` from being an extras_require["dev"] dependency to being an install_requires dependency, since it's required not just for doc generation but also just to import the package. diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 2732639521..f54c1defde 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -156,7 +156,7 @@ def run(self): setup( name="0x-order-utils", - version="3.0.1", + version="4.0.0", description="Order utilities for 0x applications", long_description=README_MD, long_description_content_type="text/markdown", From 0ef343534b0d841710b36781cf454e767793f66b Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 29 Oct 2019 11:08:40 -0400 Subject: [PATCH 20/28] @0x/contract-addresses: Put addr's in JSON, not TS This allows easier consumption by other languages. (Specifically, it eliminates the overhead of keeping the Python addresses package in sync with the TypeScript one.) --- .gitignore | 3 +- packages/contract-addresses/addresses.json | 117 ++++++++++++ packages/contract-addresses/src/index.ts | 125 +----------- packages/contract-addresses/tsconfig.json | 7 +- python-packages/contract_addresses/setup.py | 41 +++- .../contract_addresses/src/index.rst | 2 +- .../zero_ex/contract_addresses/__init__.py | 179 +++++++++--------- .../src/zero_ex/contract_wrappers/__init__.py | 16 +- .../contract_wrappers/test/conftest.py | 10 +- .../test/test_base_contract_method.py | 4 +- .../test/test_erc20_wrapper.py | 4 +- .../test/test_exchange_wrapper.py | 8 +- .../src/zero_ex/json_schemas/__init__.py | 6 +- .../test/test_local_message_signer.py | 4 +- .../src/zero_ex/order_utils/__init__.py | 6 +- python-packages/pre_install | 1 + .../src/zero_ex/sra_client/__init__.py | 6 +- 17 files changed, 294 insertions(+), 245 deletions(-) create mode 100644 packages/contract-addresses/addresses.json diff --git a/.gitignore b/.gitignore index 14898c770c..7313764d8c 100644 --- a/.gitignore +++ b/.gitignore @@ -168,8 +168,9 @@ __pycache__ python-packages/*/src/*.egg-info python-packages/*/.coverage -# python keeps package-local copies of json schemas +# python keeps package-local copies of json schemas and contract addresses python-packages/json_schemas/src/zero_ex/json_schemas/schemas +python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json # Doc README copy packages/*/docs/README.md diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json new file mode 100644 index 0000000000..9eeb90f921 --- /dev/null +++ b/packages/contract-addresses/addresses.json @@ -0,0 +1,117 @@ +{ + "1": { + "exchangeV2": "0x080bf510fcbf18b91105470639e9561022937712", + "exchange": "0x0000000000000000000000000000000000000000", + "erc20Proxy": "0x95e6f48254609a6ee006f7d493c8e5fb97094cef", + "erc721Proxy": "0xefc70a1b18c432bdc64b596838b4d138f6bc6cad", + "forwarder": "0x0000000000000000000000000000000000000000", + "orderValidator": "0x0000000000000000000000000000000000000000", + "zrxToken": "0xe41d2489571d322189246dafa5ebde1f4699f498", + "etherToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "assetProxyOwner": "0xdffe798c7172dd6deb32baee68af322e8f495ce0", + "zeroExGovernor": "0x0000000000000000000000000000000000000000", + "dutchAuction": "0x0000000000000000000000000000000000000000", + "coordinatorRegistry": "0x45797531b873fd5e519477a070a955764c1a5b07", + "coordinator": "0x0000000000000000000000000000000000000000", + "multiAssetProxy": "0xef701d5389ae74503d633396c4d654eabedc9d78", + "staticCallProxy": "0x3517b88c19508c08650616019062b898ab65ed29", + "erc1155Proxy": "0x7eefbd48fd63d441ec7435d024ec7c5131019add", + "zrxVault": "0x0000000000000000000000000000000000000000", + "staking": "0x0000000000000000000000000000000000000000", + "stakingProxy": "0x0000000000000000000000000000000000000000", + "devUtils": "0x0000000000000000000000000000000000000000", + "erc20BridgeProxy": "0x0000000000000000000000000000000000000000" + }, + "3": { + "erc20Proxy": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa", + "erc721Proxy": "0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4", + "zrxToken": "0xff67881f8d12f372d91baae9752eb3631ff0ed00", + "etherToken": "0xc778417e063141139fce010982780140aa0cd5ab", + "exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831", + "exchange": "0xc56388332ddfc98701fefed94535100c6166956c", + "assetProxyOwner": "0x0000000000000000000000000000000000000000", + "zeroExGovernor": "0xdcf20f7b447d51f2b3e5499b7f6cbbf7295a5d26", + "forwarder": "0xe66ae6162b3e9067d6ce9e5b9799cca1ba0d09f8", + "orderValidator": "0x0000000000000000000000000000000000000000", + "dutchAuction": "0x0000000000000000000000000000000000000000", + "coordinatorRegistry": "0x403cc23e88c17c4652fb904784d1af640a6722d9", + "coordinator": "0xad8464022213a618c96a1178a927a5ed15ad6949", + "multiAssetProxy": "0xab8fbd189c569ccdee3a4d929bb7f557be4028f6", + "staticCallProxy": "0xe1b97e47aa3796276033a5341e884d2ba46b6ac1", + "erc1155Proxy": "0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d", + "devUtils": "0x9a8590eebcfc53f0cc7ab5ebb8c079e9e7d4e0f5", + "zrxVault": "0xffd161026865ad8b4ab28a76840474935eec4dfa", + "staking": "0x3f46b98061a3e1e1f41dff296ec19402c298f8a9", + "stakingProxy": "0xfaabcee42ab6b9c649794ac6c133711071897ee9", + "erc20BridgeProxy": "0x599b340b5045436a99b1f0c718d30f5a0c8519dd" + }, + "4": { + "exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831", + "exchange": "0x3afe8aa355e086d898447732cfa5d931cfb2a792", + "erc20Proxy": "0x2f5ae4f6106e89b4147651688a92256885c5f410", + "erc721Proxy": "0x7656d773e11ff7383a14dcf09a9c50990481cd10", + "zrxToken": "0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa", + "etherToken": "0xc778417e063141139fce010982780140aa0cd5ab", + "assetProxyOwner": "0x0000000000000000000000000000000000000000", + "zeroExGovernor": "0x5d751aa855a1aee5fe44cf5350ed25b5727b66ae", + "forwarder": "0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291", + "orderValidator": "0x0000000000000000000000000000000000000000", + "dutchAuction": "0x0000000000000000000000000000000000000000", + "coordinatorRegistry": "0x1084b6a398e47907bae43fec3ff4b677db6e4fee", + "coordinator": "0x9ae7a6e4e4d58c36b7aa573fc06ce46dd3cb0d44", + "multiAssetProxy": "0xb34cde0ad3a83d04abebc0b66e75196f22216621", + "staticCallProxy": "0xe1b97e47aa3796276033a5341e884d2ba46b6ac1", + "erc1155Proxy": "0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d", + "devUtils": "0xfcbb258112485f18dd68f4b1016e48c23542fdc5", + "zrxVault": "0xa5bf6ac73bc40790fc6ffc9dbbbce76c9176e224", + "staking": "0x344d4f661a82afdd84d31456c291822d90d5dc3a", + "stakingProxy": "0xc6ad5277ea225ac05e271eb14a7ebb480cd9dd9f", + "erc20BridgeProxy": "0x31b8653642110f17bdb1f719901d7e7d49b08141" + }, + "42": { + "erc20Proxy": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e", + "erc721Proxy": "0x2a9127c745688a165106c11cd4d647d2220af821", + "zrxToken": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa", + "etherToken": "0xd0a1e359811322d97991e03f863a0c30c2cf029c", + "exchangeV2": "0x30589010550762d2f0d06f650d8e8b6ade6dbf4b", + "exchange": "0xca8b1626b3b7a0da722ca9f264c4630c7d34d3b8", + "assetProxyOwner": "0x0000000000000000000000000000000000000000", + "zeroExGovernor": "0x3654e5363cd75c8974c76208137df9691e820e97", + "forwarder": "0xd6330f9d2073e1889a295dd1dd2e28d42dec4bff", + "orderValidator": "0x0000000000000000000000000000000000000000", + "dutchAuction": "0x0000000000000000000000000000000000000000", + "coordinatorRegistry": "0x09fb99968c016a3ff537bf58fb3d9fe55a7975d5", + "coordinator": "0x10e0b1c2e6065ec7f290c7e3731264f9a2bf2b2d", + "multiAssetProxy": "0xf6313a772c222f51c28f2304c0703b8cf5428fd8", + "staticCallProxy": "0x48e94bdb9033640d45ea7c721e25f380f8bffa43", + "erc1155Proxy": "0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f", + "devUtils": "0x58c4fbdf9222f10ad2bef8f4d374f209135e71a5", + "zrxVault": "0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291", + "staking": "0x89150f5eed50b3528f79bfb539f29d727f92821c", + "stakingProxy": "0xbab9145f1d57cd4bb0c9aa2d1ece0a5b6e734d34", + "erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64" + }, + "50": { + "erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48", + "erc721Proxy": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401", + "erc1155Proxy": "0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8", + "zrxToken": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c", + "etherToken": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082", + "exchangeV2": "0x48bacb9266a570d521063ef5dd96e61686dbe788", + "exchange": "0x48bacb9266a570d521063ef5dd96e61686dbe788", + "zeroExGovernor": "0x0000000000000000000000000000000000000000", + "assetProxyOwner": "0x0000000000000000000000000000000000000000", + "forwarder": "0x0000000000000000000000000000000000000000", + "orderValidator": "0x0000000000000000000000000000000000000000", + "dutchAuction": "0x0000000000000000000000000000000000000000", + "coordinatorRegistry": "0x1941ff73d1154774d87521d2d0aaad5d19c8df60", + "coordinator": "0x0000000000000000000000000000000000000000", + "multiAssetProxy": "0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db", + "staticCallProxy": "0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f", + "devUtils": "0x38ef19fdf8e8415f18c307ed71967e19aac28ba1", + "zrxVault": "0x0000000000000000000000000000000000000000", + "staking": "0x0000000000000000000000000000000000000000", + "stakingProxy": "0x0000000000000000000000000000000000000000", + "erc20BridgeProxy": "0x0000000000000000000000000000000000000000" + } +} diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 1687c0d311..f2c12e6458 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -1,5 +1,7 @@ import * as _ from 'lodash'; +import addresses from '../addresses.json'; + export interface ContractAddresses { erc20Proxy: string; erc721Proxy: string; @@ -32,127 +34,6 @@ export enum NetworkId { Ganache = 50, } -const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; - -const networkToAddresses: { [networkId: number]: ContractAddresses } = { - 1: { - exchangeV2: '0x080bf510fcbf18b91105470639e9561022937712', - exchange: NULL_ADDRESS, - erc20Proxy: '0x95e6f48254609a6ee006f7d493c8e5fb97094cef', - erc721Proxy: '0xefc70a1b18c432bdc64b596838b4d138f6bc6cad', - forwarder: NULL_ADDRESS, - orderValidator: NULL_ADDRESS, - zrxToken: '0xe41d2489571d322189246dafa5ebde1f4699f498', - etherToken: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - assetProxyOwner: '0xdffe798c7172dd6deb32baee68af322e8f495ce0', - zeroExGovernor: NULL_ADDRESS, - dutchAuction: NULL_ADDRESS, - coordinatorRegistry: '0x45797531b873fd5e519477a070a955764c1a5b07', - coordinator: NULL_ADDRESS, - multiAssetProxy: '0xef701d5389ae74503d633396c4d654eabedc9d78', - staticCallProxy: '0x3517b88c19508c08650616019062b898ab65ed29', - erc1155Proxy: '0x7eefbd48fd63d441ec7435d024ec7c5131019add', - zrxVault: NULL_ADDRESS, - staking: NULL_ADDRESS, - stakingProxy: NULL_ADDRESS, - devUtils: NULL_ADDRESS, - erc20BridgeProxy: NULL_ADDRESS, - }, - 3: { - erc20Proxy: '0xb1408f4c245a23c31b98d2c626777d4c0d766caa', - erc721Proxy: '0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4', - zrxToken: '0xff67881f8d12f372d91baae9752eb3631ff0ed00', - etherToken: '0xc778417e063141139fce010982780140aa0cd5ab', - exchangeV2: '0xbff9493f92a3df4b0429b6d00743b3cfb4c85831', - exchange: '0xc56388332ddfc98701fefed94535100c6166956c', - assetProxyOwner: NULL_ADDRESS, - zeroExGovernor: '0xdcf20f7b447d51f2b3e5499b7f6cbbf7295a5d26', - forwarder: '0xe66ae6162b3e9067d6ce9e5b9799cca1ba0d09f8', - orderValidator: NULL_ADDRESS, - dutchAuction: NULL_ADDRESS, - coordinatorRegistry: '0x403cc23e88c17c4652fb904784d1af640a6722d9', - coordinator: '0xad8464022213a618c96a1178a927a5ed15ad6949', - multiAssetProxy: '0xab8fbd189c569ccdee3a4d929bb7f557be4028f6', - staticCallProxy: '0xe1b97e47aa3796276033a5341e884d2ba46b6ac1', - erc1155Proxy: '0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d', - devUtils: '0x9a8590eebcfc53f0cc7ab5ebb8c079e9e7d4e0f5', - zrxVault: '0xffd161026865ad8b4ab28a76840474935eec4dfa', - staking: '0x3f46b98061a3e1e1f41dff296ec19402c298f8a9', - stakingProxy: '0xfaabcee42ab6b9c649794ac6c133711071897ee9', - erc20BridgeProxy: '0x599b340b5045436a99b1f0c718d30f5a0c8519dd', - }, - 4: { - exchangeV2: '0xbff9493f92a3df4b0429b6d00743b3cfb4c85831', - exchange: '0x3afe8aa355e086d898447732cfa5d931cfb2a792', - erc20Proxy: '0x2f5ae4f6106e89b4147651688a92256885c5f410', - erc721Proxy: '0x7656d773e11ff7383a14dcf09a9c50990481cd10', - zrxToken: '0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa', - etherToken: '0xc778417e063141139fce010982780140aa0cd5ab', - assetProxyOwner: NULL_ADDRESS, - zeroExGovernor: '0x5d751aa855a1aee5fe44cf5350ed25b5727b66ae', - forwarder: '0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291', - orderValidator: NULL_ADDRESS, - dutchAuction: NULL_ADDRESS, - coordinatorRegistry: '0x1084b6a398e47907bae43fec3ff4b677db6e4fee', - coordinator: '0x9ae7a6e4e4d58c36b7aa573fc06ce46dd3cb0d44', - multiAssetProxy: '0xb34cde0ad3a83d04abebc0b66e75196f22216621', - staticCallProxy: '0xe1b97e47aa3796276033a5341e884d2ba46b6ac1', - erc1155Proxy: '0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d', - devUtils: '0xfcbb258112485f18dd68f4b1016e48c23542fdc5', - zrxVault: '0xa5bf6ac73bc40790fc6ffc9dbbbce76c9176e224', - staking: '0x344d4f661a82afdd84d31456c291822d90d5dc3a', - stakingProxy: '0xc6ad5277ea225ac05e271eb14a7ebb480cd9dd9f', - erc20BridgeProxy: '0x31b8653642110f17bdb1f719901d7e7d49b08141', - }, - 42: { - erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e', - erc721Proxy: '0x2a9127c745688a165106c11cd4d647d2220af821', - zrxToken: '0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa', - etherToken: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', - exchangeV2: '0x30589010550762d2f0d06f650d8e8b6ade6dbf4b', - exchange: '0xca8b1626b3b7a0da722ca9f264c4630c7d34d3b8', - assetProxyOwner: NULL_ADDRESS, - zeroExGovernor: '0x3654e5363cd75c8974c76208137df9691e820e97', - forwarder: '0xd6330f9d2073e1889a295dd1dd2e28d42dec4bff', - orderValidator: NULL_ADDRESS, - dutchAuction: NULL_ADDRESS, - coordinatorRegistry: '0x09fb99968c016a3ff537bf58fb3d9fe55a7975d5', - coordinator: '0x10e0b1c2e6065ec7f290c7e3731264f9a2bf2b2d', - multiAssetProxy: '0xf6313a772c222f51c28f2304c0703b8cf5428fd8', - staticCallProxy: '0x48e94bdb9033640d45ea7c721e25f380f8bffa43', - erc1155Proxy: '0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f', - devUtils: '0x58c4fbdf9222f10ad2bef8f4d374f209135e71a5', - zrxVault: '0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291', - staking: '0x89150f5eed50b3528f79bfb539f29d727f92821c', - stakingProxy: '0xbab9145f1d57cd4bb0c9aa2d1ece0a5b6e734d34', - erc20BridgeProxy: '0xfb2dd2a1366de37f7241c83d47da58fd503e2c64', - }, - // NetworkId 50 represents our Ganache snapshot generated from migrations. - 50: { - erc20Proxy: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', - erc721Proxy: '0x1d7022f5b17d2f8b695918fb48fa1089c9f85401', - erc1155Proxy: '0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8', - zrxToken: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c', - etherToken: '0x0b1ba0af832d7c05fd64161e0db78e85978e8082', - exchangeV2: '0x48bacb9266a570d521063ef5dd96e61686dbe788', - exchange: '0x48bacb9266a570d521063ef5dd96e61686dbe788', - zeroExGovernor: NULL_ADDRESS, - assetProxyOwner: NULL_ADDRESS, - forwarder: NULL_ADDRESS, - orderValidator: NULL_ADDRESS, - dutchAuction: NULL_ADDRESS, - coordinatorRegistry: '0x1941ff73d1154774d87521d2d0aaad5d19c8df60', - coordinator: NULL_ADDRESS, - multiAssetProxy: '0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db', - staticCallProxy: '0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f', - devUtils: '0x38ef19fdf8e8415f18c307ed71967e19aac28ba1', - zrxVault: NULL_ADDRESS, - staking: NULL_ADDRESS, - stakingProxy: NULL_ADDRESS, - erc20BridgeProxy: NULL_ADDRESS, - }, -}; - /** * Used to get addresses of contracts that have been deployed to either the * Ethereum mainnet or a supported testnet. Throws if there are no known @@ -162,6 +43,8 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { * given networkId. */ export function getContractAddressesForNetworkOrThrow(networkId: NetworkId): ContractAddresses { + const networkToAddresses: { [networkId: number]: ContractAddresses } = addresses; + if (networkToAddresses[networkId] === undefined) { throw new Error(`Unknown network id (${networkId}). No known 0x contracts have been deployed on this network.`); } diff --git a/packages/contract-addresses/tsconfig.json b/packages/contract-addresses/tsconfig.json index 233008d61c..3d191a3c01 100644 --- a/packages/contract-addresses/tsconfig.json +++ b/packages/contract-addresses/tsconfig.json @@ -2,7 +2,10 @@ "extends": "../../tsconfig", "compilerOptions": { "outDir": "lib", - "rootDir": "." + "rootDir": ".", + "resolveJsonModule": true, + "esModuleInterop": true }, - "include": ["./src/**/*"] + "include": ["./src/**/*"], + "files": ["./addresses.json"] } diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index a84ee3551c..e5916386a0 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -7,8 +7,8 @@ # installed when you invoke this script import subprocess # nosec -from shutil import rmtree -from os import environ, path +from shutil import copyfile, rmtree +from os import environ, path, remove from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean @@ -17,6 +17,38 @@ from setuptools.command.test import test as TestCommand +class PreInstallCommand(distutils.command.build_py.build_py): + """Custom setuptools command class for pulling in addresses.json.""" + + description = ( + "Pull in addresses.json from ../../packages/contract-addresses" + ) + + def run(self): + """Copy over addresses.json.""" + pkgdir = path.dirname(path.realpath(argv[0])) + + destination_path = path.join( + pkgdir, "src", "zero_ex", "contract_addresses" + ) + + try: + remove(path.join(destination_path, "addresses.json")) + except FileNotFoundError: + pass + copyfile( + path.join( + pkgdir, + "..", + "..", + "packages", + "contract-addresses", + "addresses.json", + ), + path.join(destination_path, "addresses.json"), + ) + + class LintCommand(distutils.command.build_py.build_py): """Custom setuptools command class for running linters.""" @@ -135,6 +167,7 @@ def run_tests(self): author_email="feuGeneA@users.noreply.github.com", cmdclass={ "clean": CleanCommandExtension, + "pre_install": PreInstallCommand, "lint": LintCommand, "test": TestCommandExtension, "test_publish": TestPublishCommand, @@ -160,7 +193,9 @@ def run_tests(self): ] }, python_requires=">=3.6, <4", - package_data={"zero_ex.contract_addresses": ["py.typed"]}, + package_data={ + "zero_ex.contract_addresses": ["py.typed", "addresses.json"] + }, package_dir={"": "src"}, license="Apache 2.0", keywords=( diff --git a/python-packages/contract_addresses/src/index.rst b/python-packages/contract_addresses/src/index.rst index 5dd096b16a..62a2f4bf0e 100644 --- a/python-packages/contract_addresses/src/index.rst +++ b/python-packages/contract_addresses/src/index.rst @@ -19,7 +19,7 @@ Python zero_ex.contract_addresses :members: :show-inheritance: -.. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES +.. autodata:: zero_ex.contract_addresses.network_to_addresses :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 1a252bfbd2..7e267485d0 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 @@ -10,8 +10,11 @@ """ from enum import Enum +import json from typing import Dict, NamedTuple +from pkg_resources import resource_string + class ContractAddresses(NamedTuple): """An abstract record listing all the contracts that have addresses.""" @@ -20,10 +23,7 @@ class ContractAddresses(NamedTuple): """Address of the ERC20Proxy contract.""" erc721_proxy: str - """Address of the ERC20Proxy contract.""" - - erc1155_proxy: str - """Address of the ERC1155Proxy contract.""" + """Address of the ERC721Proxy contract.""" zrx_token: str """Address of the ZRX token contract.""" @@ -31,27 +31,57 @@ class ContractAddresses(NamedTuple): ether_token: str """Address of the WETH token contract.""" + exchange_v2: str + """Address of the v2 Exchange contract.""" + exchange: str - """Address of the Exchange contract.""" + """Address of the v3 Exchange contract.""" asset_proxy_owner: str """Address of the AssetProxyOwner contract.""" + zero_ex_governor: str + """Address of the ZeroExGovernor contract.""" + forwarder: str """Address of the Forwarder contract.""" order_validator: str """Address of the OrderValidator contract.""" + dutch_auction: str + """Address of the DutchAuction contract.""" + coordinator_registry: str """Address of the CoordinatorRegistry contract.""" coordinator: str """Address of the Coordinator contract.""" + multi_asset_proxy: str + """Address of the MultiAssetProxy contract.""" + + static_call_proxy: str + """Address of the StaticCallProxy contract.""" + + erc1155_proxy: str + """Address of the ERC1155Proxy contract.""" + dev_utils: str """Address of the DevUtils contract.""" + zrx_vault: str + """Address of the ZRXVault contract.""" + + staking: str + """Address of the Staking contract.""" + + staking_proxy: str + """Address of the StakingProxy contract.""" + + erc20_bridge_proxy: str + """Address of the ERC20BridgeProxy contract.""" + class NetworkId(Enum): """Network names correlated to their network identification numbers. @@ -70,83 +100,62 @@ class NetworkId(Enum): GANACHE = 50 -NETWORK_TO_ADDRESSES: Dict[NetworkId, ContractAddresses] = { - NetworkId.MAINNET: ContractAddresses( # nosec - erc20_proxy="0x95e6f48254609a6ee006f7d493c8e5fb97094cef", - erc721_proxy="0xefc70a1b18c432bdc64b596838b4d138f6bc6cad", - zrx_token="0xe41d2489571d322189246dafa5ebde1f4699f498", - ether_token="0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - exchange="0x080bf510fcbf18b91105470639e9561022937712", - asset_proxy_owner="0xdffe798c7172dd6deb32baee68af322e8f495ce0", - forwarder="0x76481caa104b5f6bccb540dae4cefaf1c398ebea", - order_validator="0xa09329c6003c9a5402102e226417738ee22cf1f2", - coordinator_registry="0x45797531b873fd5e519477a070a955764c1a5b07", - coordinator="0xa14857e8930acd9a882d33ec20559beb5479c8a6", - erc1155_proxy="0x7eefbd48fd63d441ec7435d024ec7c5131019add", - dev_utils="0x92d9a4d50190ae04e03914db2ee650124af844e6", - ), - NetworkId.ROPSTEN: ContractAddresses( # nosec - erc20_proxy="0xb1408f4c245a23c31b98d2c626777d4c0d766caa", - erc721_proxy="0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4", - zrx_token="0xff67881f8d12f372d91baae9752eb3631ff0ed00", - ether_token="0xc778417e063141139fce010982780140aa0cd5ab", - exchange="0xbff9493f92a3df4b0429b6d00743b3cfb4c85831", - asset_proxy_owner="0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b", - forwarder="0x1ebdc9758e85c1c6a85af06cc96cf89000a31913", - order_validator="0x90431a90516ab49af23a0530e04e8c7836e7122f", - coordinator_registry="0x403cc23e88c17c4652fb904784d1af640a6722d9", - coordinator="0x2ba02e03ee0029311e0f43715307870a3e701b53", - erc1155_proxy="0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d", - dev_utils="0x3e0b46bad8e374e4a110c12b832cb120dbe4a479", - ), - NetworkId.RINKEBY: ContractAddresses( # nosec - exchange="0xbff9493f92a3df4b0429b6d00743b3cfb4c85831", - erc20_proxy="0x2f5ae4f6106e89b4147651688a92256885c5f410", - erc721_proxy="0x7656d773e11ff7383a14dcf09a9c50990481cd10", - zrx_token="0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa", - ether_token="0xc778417e063141139fce010982780140aa0cd5ab", - asset_proxy_owner="0xe1703da878afcebff5b7624a826902af475b9c03", - forwarder="0x1ebdc9758e85c1c6a85af06cc96cf89000a31913", - order_validator="0x0c5173a51e26b29d6126c686756fb9fbef71f762", - coordinator_registry="0x1084b6a398e47907bae43fec3ff4b677db6e4fee", - coordinator="0x2ba02e03ee0029311e0f43715307870a3e701b53", - erc1155_proxy="0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d", - dev_utils="0x2d4a9abda7b8b3605c8dbd34e3550a7467c78287'", - ), - NetworkId.KOVAN: ContractAddresses( # nosec - erc20_proxy="0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e", - erc721_proxy="0x2a9127c745688a165106c11cd4d647d2220af821", - zrx_token="0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa", - ether_token="0xd0a1e359811322d97991e03f863a0c30c2cf029c", - exchange="0x30589010550762d2f0d06f650d8e8b6ade6dbf4b", - asset_proxy_owner="0x2c824d2882baa668e0d5202b1e7f2922278703f8", - forwarder="0x1ebdc9758e85c1c6a85af06cc96cf89000a31913", - order_validator="0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d", - coordinator_registry="0x09fb99968c016a3ff537bf58fb3d9fe55a7975d5", - coordinator="0x2ba02e03ee0029311e0f43715307870a3e701b53", - erc1155_proxy="0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f", - dev_utils="0x1e3616bc5144362f95d72de41874395567697e93", - ), - NetworkId.GANACHE: ContractAddresses( # nosec - exchange="0x48bacb9266a570d521063ef5dd96e61686dbe788", - erc20_proxy="0x1dc4c1cefef38a777b15aa20260a54e584b16c48", - erc721_proxy="0x1d7022f5b17d2f8b695918fb48fa1089c9f85401", - erc1155_proxy="0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8", - zrx_token="0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c", - ether_token="0x0b1ba0af832d7c05fd64161e0db78e85978e8082", - asset_proxy_owner="0x8d42e38980ce74736c21c059b2240df09958d3c8", - forwarder="0xaa86dda78e9434aca114b6676fc742a18d15a1cc", - order_validator="0x4d3d5c850dd5bd9d6f4adda3dd039a3c8054ca29", - coordinator_registry="0x1941ff73d1154774d87521d2d0aaad5d19c8df60", - coordinator="0x0d8b0dd11f5d34ed41d556def5f841900d5b1c6b", - dev_utils="0x38ef19fdf8e8415f18c307ed71967e19aac28ba1", - ), -} -"""A mapping from instances of NetworkId to instances of ContractAddresses. - -Addresses under NetworkId.Ganache are from our Ganache snapshot generated from -npm package @0x/migrations. - ->>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange -0x4f833a24e1f95d70f028921e27040ca56e09ab0b -""" +class _AddressCache: + """A cache to facilitate lazy & singular loading of contract addresses.""" + + # pylint: disable=too-few-public-methods + + # class data, not instance: + _network_to_addresses: Dict[str, ContractAddresses] = {} + + @classmethod + def network_to_addresses(cls, network_id: NetworkId): + """Return the addresses for the given network ID. + + First tries to get data from the class level storage + `_network_to_addresses`. If it's not there, loads it from disk, stores + it in the class data (for the next caller), and then returns it. + """ + try: + return cls._network_to_addresses[str(network_id.value)] + except KeyError: + cls._network_to_addresses = json.loads( + resource_string("zero_ex.contract_addresses", "addresses.json") + ) + return cls._network_to_addresses[str(network_id.value)] + + +def network_to_addresses(network_id: NetworkId) -> ContractAddresses: + """Map a NetworkId to an instance of ContractAddresses. + + Addresses under NetworkId.Ganache are from our Ganache snapshot generated + from npm package @0x/migrations. + + >>> network_to_addresses(NetworkId.MAINNET).exchange + '0x...' + """ + addresses = _AddressCache.network_to_addresses(network_id) + + return ContractAddresses( + erc20_proxy=addresses["erc20Proxy"], + erc721_proxy=addresses["erc721Proxy"], + zrx_token=addresses["zrxToken"], + ether_token=addresses["etherToken"], + exchange_v2=addresses["exchangeV2"], + exchange=addresses["exchange"], + asset_proxy_owner=addresses["assetProxyOwner"], + zero_ex_governor=addresses["zeroExGovernor"], + forwarder=addresses["forwarder"], + order_validator=addresses["orderValidator"], + dutch_auction=addresses["dutchAuction"], + coordinator_registry=addresses["coordinatorRegistry"], + coordinator=addresses["coordinator"], + multi_asset_proxy=addresses["multiAssetProxy"], + static_call_proxy=addresses["staticCallProxy"], + erc1155_proxy=addresses["erc1155Proxy"], + dev_utils=addresses["devUtils"], + zrx_vault=addresses["zrxVault"], + staking=addresses["staking"], + staking_proxy=addresses["stakingProxy"], + erc20_bridge_proxy=addresses["erc20BridgeProxy"], + ) 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 558588f179..538607c6fa 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 @@ -52,10 +52,10 @@ pre-deployed deployed in the `0xorg/ganache-cli`:code: docker image. Let's capture the addresses we'll use throughout the examples below: ->>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId ->>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token ->>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token ->>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange +>>> from zero_ex.contract_addresses import network_to_addresses, NetworkId +>>> 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 Wrapping ETH ------------ @@ -93,14 +93,14 @@ >>> from zero_ex.contract_wrappers.erc20_token import ERC20Token >>> zrx_token = ERC20Token( ... web3_or_provider=ganache, -... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token, +... contract_address=network_to_addresses(NetworkId.GANACHE).zrx_token, ... ) >>> weth_token = ERC20Token( ... web3_or_provider=ganache, -... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, +... contract_address=network_to_addresses(NetworkId.GANACHE).ether_token, ... ) ->>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy +>>> erc20_proxy_addr = network_to_addresses(NetworkId.GANACHE).erc20_proxy >>> tx = zrx_token.approve.send_transaction( ... erc20_proxy_addr, @@ -166,7 +166,7 @@ >>> from zero_ex.contract_wrappers.exchange import Exchange >>> exchange = Exchange( ... web3_or_provider=ganache, -... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, +... contract_address=network_to_addresses(NetworkId.GANACHE).exchange, ... ) But before filling an order, one may wish to check that it's actually fillable: diff --git a/python-packages/contract_wrappers/test/conftest.py b/python-packages/contract_wrappers/test/conftest.py index 9c0fb47097..040593a373 100644 --- a/python-packages/contract_wrappers/test/conftest.py +++ b/python-packages/contract_wrappers/test/conftest.py @@ -5,7 +5,7 @@ from web3 import Web3 from zero_ex.order_utils import asset_data_utils -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_addresses import network_to_addresses, NetworkId from zero_ex.contract_artifacts import abi_by_name @@ -36,14 +36,14 @@ def accounts(web3_eth): # pylint: disable=redefined-outer-name @pytest.fixture(scope="module") def erc20_proxy_address(): """Get the 0x ERC20 Proxy address.""" - return NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy + return network_to_addresses(NetworkId.GANACHE).erc20_proxy @pytest.fixture(scope="module") def weth_asset_data(): # pylint: disable=redefined-outer-name """Get 0x asset data for Wrapped Ether (WETH) token.""" return asset_data_utils.encode_erc20( - NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token + network_to_addresses(NetworkId.GANACHE).ether_token ) @@ -52,7 +52,7 @@ def weth_instance(web3_eth): # pylint: disable=redefined-outer-name """Get an instance of the WrapperEther contract.""" return web3_eth.contract( address=to_checksum_address( - NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token + network_to_addresses(NetworkId.GANACHE).ether_token ), abi=abi_by_name("WETH9"), ) @@ -61,7 +61,7 @@ def weth_instance(web3_eth): # pylint: disable=redefined-outer-name @pytest.fixture(scope="module") def zrx_address(): """Get address of ZRX token for Ganache network.""" - return NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token + return network_to_addresses(NetworkId.GANACHE).zrx_token @pytest.fixture(scope="module") diff --git a/python-packages/contract_wrappers/test/test_base_contract_method.py b/python-packages/contract_wrappers/test/test_base_contract_method.py index fd6e4d1731..e437f55aec 100644 --- a/python-packages/contract_wrappers/test/test_base_contract_method.py +++ b/python-packages/contract_wrappers/test/test_base_contract_method.py @@ -2,7 +2,7 @@ import pytest -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_addresses import network_to_addresses, NetworkId from zero_ex.contract_wrappers.bases import ContractMethod @@ -11,5 +11,5 @@ def contract_wrapper(ganache_provider): """Get a ContractMethod instance for testing.""" return ContractMethod( web3_or_provider=ganache_provider, - contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, + contract_address=network_to_addresses(NetworkId.GANACHE).ether_token, ) diff --git a/python-packages/contract_wrappers/test/test_erc20_wrapper.py b/python-packages/contract_wrappers/test/test_erc20_wrapper.py index da80356ed0..8e13528e50 100644 --- a/python-packages/contract_wrappers/test/test_erc20_wrapper.py +++ b/python-packages/contract_wrappers/test/test_erc20_wrapper.py @@ -4,7 +4,7 @@ import pytest -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_addresses import network_to_addresses, NetworkId from zero_ex.contract_wrappers import TxParams from zero_ex.contract_wrappers.erc20_token import ERC20Token @@ -16,7 +16,7 @@ def erc20_wrapper(ganache_provider): """Get an instance of ERC20Token wrapper class for testing.""" return ERC20Token( - ganache_provider, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token + ganache_provider, network_to_addresses(NetworkId.GANACHE).ether_token ) diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index 1da326d793..c299eeac9d 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -5,7 +5,7 @@ import pytest from eth_utils import remove_0x_prefix -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_addresses import network_to_addresses, NetworkId from zero_ex.contract_wrappers import TxParams from zero_ex.contract_wrappers.exchange import Exchange from zero_ex.contract_wrappers.exchange.types import Order @@ -22,7 +22,7 @@ def exchange_wrapper(ganache_provider): """Get an Exchange wrapper instance.""" return Exchange( web3_or_provider=ganache_provider, - contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, + contract_address=network_to_addresses(NetworkId.GANACHE).exchange, ) @@ -161,8 +161,8 @@ def test_two_instantiations_with_web3_objects(web3_instance): again." Test that that bug isn't occurring. """ exchange = Exchange( # pylint: disable=unused-variable - web3_instance, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange + web3_instance, network_to_addresses(NetworkId.GANACHE).exchange ) exchange2 = Exchange( # pylint: disable=unused-variable - web3_instance, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange + web3_instance, network_to_addresses(NetworkId.GANACHE).exchange ) 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 01f6ccb4bd..9314f4d194 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 @@ -60,7 +60,7 @@ def assert_valid(data: Mapping, schema_id: str) -> None: Raises an exception if validation fails. >>> from zero_ex.json_schemas import assert_valid - >>> 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 >>> from eth_utils import remove_0x_prefix >>> import random @@ -74,10 +74,10 @@ def assert_valid(data: Mapping, schema_id: str) -> None: ... '0x0000000000000000000000000000000000000000' ... ), ... 'makerAssetData': ( - ... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token + ... network_to_addresses(NetworkId.MAINNET).zrx_token ... ), ... 'takerAssetData': ( - ... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token + ... network_to_addresses(NetworkId.MAINNET).ether_token ... ), ... 'salt': random.randint(1, 100000000000000000), ... 'makerFee': 0, diff --git a/python-packages/middlewares/test/test_local_message_signer.py b/python-packages/middlewares/test/test_local_message_signer.py index c49f667f0d..2eb87997a8 100644 --- a/python-packages/middlewares/test/test_local_message_signer.py +++ b/python-packages/middlewares/test/test_local_message_signer.py @@ -3,7 +3,7 @@ from eth_utils import to_checksum_address from web3 import Web3, HTTPProvider -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_addresses import network_to_addresses, NetworkId from zero_ex.middlewares.local_message_signer import ( construct_local_message_signer, ) @@ -17,7 +17,7 @@ def test_local_message_signer__sign_order(): "d6db0157d9dfe9f9fadb8dedabb7786352843357f4ec8d0fbcbeeb619b1091f5803" ) address = "0x5409ED021D9299bf6814279A6A1411A7e866A631" - exchange = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange + exchange = network_to_addresses(NetworkId.GANACHE).exchange private_key = ( "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d" ) 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 f14dd3570d..7cc7ef277e 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 @@ -29,7 +29,7 @@ from web3.providers.base import BaseProvider from web3.contract import Contract -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_addresses import network_to_addresses, NetworkId import zero_ex.contract_artifacts from zero_ex.contract_wrappers.exchange import Exchange from zero_ex.contract_wrappers.exchange.types import Order @@ -211,11 +211,11 @@ def is_valid_signature( return Exchange( provider, - NETWORK_TO_ADDRESSES[ + network_to_addresses( NetworkId( int(Web3(provider).net.version) # pylint: disable=no-member ) - ].exchange, + ).exchange, ).is_valid_hash_signature.call( bytes.fromhex(remove_0x_prefix(data)), to_checksum_address(signer_address), diff --git a/python-packages/pre_install b/python-packages/pre_install index 42e561ca88..24ed40a633 100755 --- a/python-packages/pre_install +++ b/python-packages/pre_install @@ -12,6 +12,7 @@ from sys import argv PACKAGES = [ "contract_wrappers", + "contract_addresses", "contract_artifacts", "json_schemas", ] 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 index 31aeb30f57..c9af8d5c25 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -83,8 +83,8 @@ 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_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( @@ -321,7 +321,7 @@ >>> from zero_ex.order_utils import Order >>> exchange = Exchange( ... provider=eth_node, -... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange +... contract_address=network_to_addresses(NetworkId.GANACHE).exchange ... ) (Due to `an Issue with the Launch Kit Backend From 5873b5653a78f6022564258a9ce9b44624242714 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Fri, 18 Oct 2019 20:30:48 -0400 Subject: [PATCH 21/28] sra_client.py: incl. docker in `./setup.py clean` --- python-packages/sra_client/setup.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index ec784d4b4c..b06ae38bf3 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -46,6 +46,12 @@ def run(self): rmtree("0x_sra_client.egg-info", ignore_errors=True) rmtree("build", ignore_errors=True) rmtree("dist", ignore_errors=True) + subprocess.check_call( # nosec + ("docker-compose -f test/relayer/docker-compose.yml down").split() + ) + subprocess.check_call( # nosec + ("docker-compose -f test/relayer/docker-compose.yml rm").split() + ) class TestCommandExtension(TestCommand): From 8941145f2d34eaf5e4b9415ee8b7e5ba74e4c69f Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 21 Oct 2019 19:57:09 -0400 Subject: [PATCH 22/28] sra_client.py: Migrate to protocol v3 Removed script that existed only to exclude runs of sra_client builds (parallel_without_sra_client). Now `parallel` is used by CI, re-including sra_client in CI checks. --- .circleci/config.yml | 24 ++++- python-packages/parallel_without_sra_client | 57 ----------- python-packages/sra_client/setup.py | 7 +- .../src/zero_ex/sra_client/__init__.py | 63 ++++++++---- .../src/zero_ex/sra_client/api/default_api.py | 96 +++++++++---------- .../test/relayer/docker-compose.yml | 36 ++++++- 6 files changed, 147 insertions(+), 136 deletions(-) delete mode 100755 python-packages/parallel_without_sra_client diff --git a/.circleci/config.yml b/.circleci/config.yml index 96e6213f50..61e62d9eab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -196,13 +196,29 @@ jobs: environment: VERSION: 4.4.0-beta.1 SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta - - image: 0xorg/launch-kit-backend:74bcc39 + - image: 0xorg/mesh:6.0.0-beta-0xv3 environment: - RPC_URL: http://localhost:8545 + ETHEREUM_RPC_URL: 'http://localhost:8545' + ETHEREUM_NETWORK_ID: '50' + ETHEREUM_CHAIN_ID: '1337' + USE_BOOTSTRAP_LIST: 'true' + VERBOSITY: 3 + PRIVATE_KEY_PATH: '' + BLOCK_POLLING_INTERVAL: '5s' + P2P_LISTEN_PORT: '60557' + command: | + sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh" + - image: 0xorg/launch-kit-backend:v3 + environment: + RPC_URL: 'http://localhost:8545' NETWORK_ID: 50 WHITELIST_ALL_TOKENS: True + FEE_RECIPIENT: '0x0000000000000000000000000000000000000001' + MAKER_FEE_UNIT_AMOUNT: 0 + TAKER_FEE_UNIT_AMOUNT: 0 + MESH_ENDPOINT: 'ws://localhost:60557' command: | - sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js" + sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js" steps: - checkout - restore_cache: @@ -224,7 +240,7 @@ jobs: - run: command: | cd python-packages - ./parallel_without_sra_client coverage run setup.py test + ./parallel coverage run setup.py test ./build_docs - run: command: | diff --git a/python-packages/parallel_without_sra_client b/python-packages/parallel_without_sra_client deleted file mode 100755 index 206415b758..0000000000 --- a/python-packages/parallel_without_sra_client +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python - -"""Run the given command in all packages in parallel. - -Handy for quick verification test runs, but annoying in that all of the output -is interleaved. - -$ ./parallel ./setup.py lint - -This will `cd` into each package, run `./setup.py lint`, then `cd ..`, all in -parallel, in a separate process for each package. The number of processes is -decided by ProcessPoolExecutor. Replace "lint" with any of "test", "clean", -"build_sphinx" (for docs), etc. - -Also consider: - -$ ./parallel pip install -e .[dev] # install all the packages in editable mode - -$ ./parallel pip uninstall $(basename $(pwd)) - ->>>""" - -from concurrent.futures import ProcessPoolExecutor, wait -from os import chdir -from subprocess import CalledProcessError, check_output, STDOUT -from sys import argv - -PACKAGES = [ - "contract_addresses", - "contract_artifacts", - "contract_wrappers", - "json_schemas", - "order_utils", - "middlewares", -] - -def run_cmd_on_package(package: str): - """cd to the package dir, ./setup.py lint, cd ..""" - chdir(package) - command = f"{' '.join(argv[1:])}" - try: - check_output(command.split(), stderr=STDOUT) - except CalledProcessError as error: - raise RuntimeError( - f"Failure return code received from command `{command}` in package" - + f" {package}, which produced the following output:\n" - + f"{error.output.decode('utf-8')}") from error - finally: - chdir("..") - -with ProcessPoolExecutor() as executor: - for future in executor.map(run_cmd_on_package, PACKAGES): - # iterate over map()'s return value, to resolve the futures. - # but we don't actually care what the return values are, so just `pass`. - # if any exceptions were raised by the underlying task, they'll be - # raised as the iteration encounters them. - pass diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index b06ae38bf3..b143467009 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -96,12 +96,15 @@ def run(self): ("docker-compose -f test/relayer/docker-compose.yml up -d").split() ) launch_kit_ready = False - print("Waiting for relayer to start accepting connections...", end="") + print( + "Waiting for Launch Kit Backend to start accepting connections...", + flush=True, + ) while not launch_kit_ready: try: launch_kit_ready = ( urlopen( # nosec - "http://localhost:3000/v2/asset_pairs" + "http://localhost:3000/v3/asset_pairs" ).getcode() == 200 ) 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 index c9af8d5c25..dd15ab7ffb 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -19,7 +19,7 @@ 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). +like to connect to (eg https://api.radarrelay.com/0x/v3). For testing one can use the `0x-launch-kit-backend `_ to host @@ -121,9 +121,11 @@ ... makerAssetData=asset_data_utils.encode_erc20( ... contract_addresses.zrx_token ... ), +... makerFeeAssetData=asset_data_utils.encode_erc20('0x'+'00'*20), ... takerAssetData=asset_data_utils.encode_erc20( ... contract_addresses.ether_token ... ), +... takerFeeAssetData=asset_data_utils.encode_erc20('0x'+'00'*20), ... salt=random.randint(1, 100000000000000000), ... makerFee=0, ... takerFee=0, @@ -136,7 +138,7 @@ >>> from zero_ex.order_utils import generate_order_hash_hex >>> order_hash_hex = generate_order_hash_hex( -... order, contract_addresses.exchange +... order, contract_addresses.exchange, Web3(eth_node).eth.chainId ... ) >>> relayer.post_order_with_http_info( ... network_id=network_id.value, @@ -145,7 +147,8 @@ ... exchange_address=contract_addresses.exchange, ... signature=sign_hash( ... eth_node, Web3.toChecksumAddress(maker_address), order_hash_hex -... ) +... ), +... chain_id=Web3(eth_node).eth.chainId, ... ) ... )[1] 200 @@ -153,24 +156,35 @@ Get Order --------- +(But first sleep for a moment, to give the test relayer a chance to start up. + +>>> from time import sleep +>>> sleep(0.2) + +This is necessary for automated verification of these examples.) + Retrieve the order we just posted: >>> relayer.get_order("0x" + order_hash_hex) -{'meta_data': {}, - 'order': {'exchangeAddress': '0x...', +{'meta_data': {'orderHash': '0x...', + 'remainingFillableTakerAssetAmount': '2'}, + 'order': {'chainId': 50, + 'exchangeAddress': '0x...', 'expirationTimeSeconds': '...', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x...', 'makerAssetAmount': '2', 'makerAssetData': '0xf47261b0000000000000000000000000...', 'makerFee': '0', + 'makerFeeAssetData': '0xf47261b0000000000000000000000000...', 'salt': '...', 'senderAddress': '0x0000000000000000000000000000000000000000', 'signature': '0x...', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': '2', 'takerAssetData': '0xf47261b0000000000000000000000000...', - 'takerFee': '0'}} + 'takerFee': '0', + 'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}} Get Orders ----------- @@ -179,21 +193,25 @@ of the one we just posted: >>> relayer.get_orders() -{'records': [{'meta_data': {}, - 'order': {'exchangeAddress': '0x...', +{'records': [{'meta_data': {'orderHash': '0x...', + 'remainingFillableTakerAssetAmount': '2'}, + 'order': {'chainId': 50, + 'exchangeAddress': '0x...', 'expirationTimeSeconds': '...', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x...', 'makerAssetAmount': '2', 'makerAssetData': '0xf47261b000000000000000000000000...', 'makerFee': '0', + 'makerFeeAssetData': '0xf47261b000000000000000000000000...', 'salt': '...', 'senderAddress': '0x0000000000000000000000000000000000000000', 'signature': '0x...', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': '2', 'takerAssetData': '0xf47261b0000000000000000000000000...', - 'takerFee': '0'}}]} + 'takerFee': '0', + 'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}...]} Get Asset Pairs --------------- @@ -234,43 +252,50 @@ ... ).hex(), ... ) >>> orderbook -{'asks': {'records': []}, - 'bids': {'records': [{'meta_data': {}, - 'order': {'exchangeAddress': '0x...', +{'asks': {'records': [...]}, + 'bids': {'records': [{'meta_data': {'orderHash': '0x...', + 'remainingFillableTakerAssetAmount': '2'}, + 'order': {'chainId': 50, + 'exchangeAddress': '0x...', 'expirationTimeSeconds': '...', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x...', 'makerAssetAmount': '2', 'makerAssetData': '0xf47261b0000000000000000000000000...', 'makerFee': '0', + 'makerFeeAssetData': '0xf47261b0000000000000000000000000...', 'salt': '...', 'senderAddress': '0x0000000000000000000000000000000000000000', 'signature': '0x...', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': '2', 'takerAssetData': '0xf47261b0000000000000000000000000...', - 'takerFee': '0'}}]}} + 'takerFee': '0', + 'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}...]}} Select an order from the orderbook ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->>> from zero_ex.contract_wrappers.exchange.types import jsdict_to_order +>>> from zero_ex.contract_wrappers.order_conversions import jsdict_to_order >>> order = jsdict_to_order(orderbook.bids.records[0].order) >>> from pprint import pprint >>> pprint(order) -{'expirationTimeSeconds': ..., +{'chainId': 50, + 'expirationTimeSeconds': ..., 'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x...', 'makerAssetAmount': 2, 'makerAssetData': b... 'makerFee': 0, + 'makerFeeAssetData': b... 'salt': ..., 'senderAddress': '0x0000000000000000000000000000000000000000', 'signature': '0x...', 'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAssetAmount': 2, - 'takerAssetData': b... - 'takerFee': 0} + 'takerAssetData': b..., + 'takerFee': 0, + 'takerFeeAssetData': b...} Filling or Cancelling an Order ------------------------------ @@ -320,7 +345,7 @@ >>> from zero_ex.contract_wrappers.exchange import Exchange >>> from zero_ex.order_utils import Order >>> exchange = Exchange( -... provider=eth_node, +... web3_or_provider=eth_node, ... contract_address=network_to_addresses(NetworkId.GANACHE).exchange ... ) @@ -332,7 +357,7 @@ >>> exchange.fill_order.send_transaction( ... order=order, ... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill -... signature=order['signature'].replace('0x', '').encode('utf-8'), +... signature=bytes.fromhex(order['signature'].replace('0x', '')), ... tx_params=TxParams(from_=taker_address) ... ) HexBytes('0x...') diff --git a/python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py b/python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py index e097f6c7c9..03e0316979 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/api/default_api.py @@ -139,7 +139,7 @@ def get_asset_pairs_with_http_info(self, **kwargs): auth_settings = [] return self.api_client.call_api( - "/v2/asset_pairs", + "/v3/asset_pairs", "GET", path_params, query_params, @@ -250,7 +250,7 @@ def get_fee_recipients_with_http_info(self, **kwargs): auth_settings = [] return self.api_client.call_api( - "/v2/fee_recipients", + "/v3/fee_recipients", "GET", path_params, query_params, @@ -363,7 +363,7 @@ def get_order_with_http_info(self, order_hash, **kwargs): auth_settings = [] return self.api_client.call_api( - "/v2/order/{orderHash}", + "/v3/order/{orderHash}", "GET", path_params, query_params, @@ -497,7 +497,7 @@ def get_order_config_with_http_info(self, **kwargs): auth_settings = [] return self.api_client.call_api( - "/v2/order_config", + "/v3/order_config", "POST", path_params, query_params, @@ -680,7 +680,7 @@ def get_orderbook_with_http_info( auth_settings = [] return self.api_client.call_api( - "/v2/orderbook", + "/v3/orderbook", "GET", path_params, query_params, @@ -718,50 +718,48 @@ def get_orders(self, **kwargs): :param bool async_req: Whether request should be asynchronous. :param str maker_asset_proxy_id: The maker `asset proxy id - `__ + `__ (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). :param str taker_asset_proxy_id: The taker asset `asset proxy id - `__ + `__ (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). :param str maker_asset_address: The contract address for the maker asset. :param str taker_asset_address: The contract address for the taker asset. - :param str exchange_address: Same as exchangeAddress in the - `0x Protocol v2 Specification - `__ + :param str exchange_address: Contract address for the exchange + contract. :param str sender_address: Same as senderAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str maker_asset_data: Same as makerAssetData in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str taker_asset_data: Same as takerAssetData in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str trader_asset_data: Same as traderAssetData in the [0x - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str maker_address: Same as makerAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str taker_address: Same as takerAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str trader_address: Same as traderAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str fee_recipient_address: Same as feeRecipientAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :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. @@ -795,50 +793,50 @@ def get_orders_with_http_info(self, **kwargs): :param bool async_req: Whether request should be asynchronous. :param str maker_asset_proxy_id: The maker `asset proxy id - `__ + `__ (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). :param str taker_asset_proxy_id: The taker asset `asset proxy id - `__ + `__ (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). :param str maker_asset_address: The contract address for the maker asset. :param str taker_asset_address: The contract address for the taker asset. :param str exchange_address: Same as exchangeAddress in the [0x - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str sender_address: Same as senderAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str maker_asset_data: Same as makerAssetData in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str taker_asset_data: Same as takerAssetData in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str trader_asset_data: Same as traderAssetData in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str maker_address: Same as makerAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str taker_address: Same as takerAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str trader_address: Same as traderAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :param str fee_recipient_address: Same as feeRecipientAddress in the - `0x Protocol v2 Specification + `0x Protocol v3 Specification `__ + master/v3/v3-specification.md#order-message-format>`__ :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. @@ -965,7 +963,7 @@ def get_orders_with_http_info(self, **kwargs): auth_settings = [] return self.api_client.call_api( - "/v2/orders", + "/v3/orders", "GET", path_params, query_params, @@ -1077,7 +1075,7 @@ def post_order_with_http_info(self, **kwargs): auth_settings = [] return self.api_client.call_api( - "/v2/order", + "/v3/order", "POST", path_params, query_params, diff --git a/python-packages/sra_client/test/relayer/docker-compose.yml b/python-packages/sra_client/test/relayer/docker-compose.yml index 6a67dcea71..e5b6657ab4 100644 --- a/python-packages/sra_client/test/relayer/docker-compose.yml +++ b/python-packages/sra_client/test/relayer/docker-compose.yml @@ -1,14 +1,36 @@ -# Run Launch Kit with Ganache as the backing node +# Run Launch Kit Backend with Ganache and Mesh instances backing it. version: '3' services: ganache: - image: "0xorg/ganache-cli:2.2.2" + image: "0xorg/ganache-cli:4.4.0-beta.1" ports: - "8545:8545" - launchkit: - image: "0xorg/launch-kit-backend:74bcc39" + environment: + - VERSION=4.4.0-beta.1 + - SNAPSHOT_NAME=0x_ganache_snapshot-v3-beta + mesh: + image: 0xorg/mesh:6.0.0-beta-0xv3 + depends_on: + - ganache + environment: + ETHEREUM_RPC_URL: 'http://localhost:8545' + ETHEREUM_NETWORK_ID: '50' + ETHEREUM_CHAIN_ID: '1337' + USE_BOOTSTRAP_LIST: 'true' + VERBOSITY: 3 + PRIVATE_KEY_PATH: '' + BLOCK_POLLING_INTERVAL: '5s' + P2P_LISTEN_PORT: '60557' + ports: + - '60557:60557' + network_mode: "host" # to connect to ganache + command: | + sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh" + launch-kit-backend: + image: "0xorg/launch-kit-backend:v3" depends_on: - ganache + - mesh ports: - "3000:3000" network_mode: "host" # to connect to ganache @@ -16,5 +38,9 @@ services: - NETWORK_ID=50 - RPC_URL=http://localhost:8545 - WHITELIST_ALL_TOKENS=True + - FEE_RECIPIENT=0x0000000000000000000000000000000000000001 + - MAKER_FEE_UNIT_AMOUNT=0 + - TAKER_FEE_UNIT_AMOUNT=0 + - MESH_ENDPOINT=ws://localhost:60557 command: | - sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js" + sh -c "waitForMesh () { sleep 3; }; waitForMesh && sleep 5 && node_modules/.bin/forever ts/lib/index.js" From 86756633380c067f5589c6485fb045b88ce7b246 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 4 Nov 2019 21:18:59 -0500 Subject: [PATCH 23/28] abi-gen/templates/Py: clarify if/else logic In response to https://github.com/0xProject/0x-monorepo/pull/2284#discussion_r342200906 --- packages/abi-gen/templates/Python/contract.handlebars | 2 +- .../abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py | 2 +- packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py | 2 +- .../abi-gen/test-cli/output/python/test_lib_dummy/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/abi-gen/templates/Python/contract.handlebars b/packages/abi-gen/templates/Python/contract.handlebars index 5fa8dcfa51..ab6e9682da 100644 --- a/packages/abi-gen/templates/Python/contract.handlebars +++ b/packages/abi-gen/templates/Python/contract.handlebars @@ -88,7 +88,7 @@ class {{contractName}}: web3 = Web3(web3_or_provider) elif isinstance(web3_or_provider, Web3): web3 = web3_or_provider - if web3 is None: + else: raise TypeError( "Expected parameter 'web3_or_provider' to be an instance of either" + " Web3 or BaseProvider" diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index 3982f02477..73d062aefc 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -1977,7 +1977,7 @@ def __init__( web3 = Web3(web3_or_provider) elif isinstance(web3_or_provider, Web3): web3 = web3_or_provider - if web3 is None: + else: raise TypeError( "Expected parameter 'web3_or_provider' to be an instance of either" + " Web3 or BaseProvider" diff --git a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py index 11d4a39bef..6c1375710e 100644 --- a/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/lib_dummy/__init__.py @@ -75,7 +75,7 @@ def __init__( web3 = Web3(web3_or_provider) elif isinstance(web3_or_provider, Web3): web3 = web3_or_provider - if web3 is None: + else: raise TypeError( "Expected parameter 'web3_or_provider' to be an instance of either" + " Web3 or BaseProvider" diff --git a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py index 321893ee3a..b49c62b7f2 100644 --- a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py @@ -197,7 +197,7 @@ def __init__( web3 = Web3(web3_or_provider) elif isinstance(web3_or_provider, Web3): web3 = web3_or_provider - if web3 is None: + else: raise TypeError( "Expected parameter 'web3_or_provider' to be an instance of either" + " Web3 or BaseProvider" From e472ceea49cb0d7f62d33807b66baa63d65605c0 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Mon, 4 Nov 2019 21:51:09 -0500 Subject: [PATCH 24/28] sra_client.py: Update CHANGELOG and bump version --- python-packages/sra_client/CHANGELOG.md | 4 ++++ python-packages/sra_client/setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/python-packages/sra_client/CHANGELOG.md b/python-packages/sra_client/CHANGELOG.md index 1220c1a349..2b68b91b91 100644 --- a/python-packages/sra_client/CHANGELOG.md +++ b/python-packages/sra_client/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 4.0.0 - TBD + +- Migrated from v2 to v3 of the 0x protocol. + ## 3.0.0 - 2019-08-08 - Migrated from v4 to v5 of Web3.py. diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index b143467009..4e2d6a78a8 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -19,7 +19,7 @@ from setuptools.command.test import test as TestCommand NAME = "0x-sra-client" -VERSION = "3.0.0" +VERSION = "4.0.0" # To install the library, run the following # # python setup.py install From 8d7f47f5df832d0f7cb3131d0d5f73386d0dded1 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 5 Nov 2019 12:35:56 -0500 Subject: [PATCH 25/28] contract_addresses/setup.py: rm unnecessary rm --- python-packages/contract_addresses/setup.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index e5916386a0..0d4dee62c3 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -8,7 +8,7 @@ import subprocess # nosec from shutil import copyfile, rmtree -from os import environ, path, remove +from os import environ, path from sys import argv, exit # pylint: disable=redefined-builtin from distutils.command.clean import clean @@ -32,10 +32,6 @@ def run(self): pkgdir, "src", "zero_ex", "contract_addresses" ) - try: - remove(path.join(destination_path, "addresses.json")) - except FileNotFoundError: - pass copyfile( path.join( pkgdir, From 6d454d1cedc5d2cc737e67d9d35e30ff4e71dec6 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 5 Nov 2019 16:40:11 -0500 Subject: [PATCH 26/28] json_schemas.py: corrections to dev dependencies --- python-packages/json_schemas/setup.py | 1 + .../json_schemas/src/zero_ex/json_schemas/__init__.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index f943260847..1a2d5d6836 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -170,6 +170,7 @@ def run(self): "black", "coverage", "coveralls", + "eth_utils", "mypy", "mypy_extensions", "pycodestyle", 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 9314f4d194..5036b6fa38 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 @@ -61,7 +61,6 @@ def assert_valid(data: Mapping, schema_id: str) -> None: >>> 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 >>> from eth_utils import remove_0x_prefix >>> import random >>> from datetime import datetime, timedelta From a14a4d3d0762a49a17c016f53f97665b241cb7df Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 29 Oct 2019 17:23:32 -0400 Subject: [PATCH 27/28] In tests against deployment, also run doctests --- python-packages/contract_addresses/tox.ini | 6 ++++++ python-packages/contract_artifacts/tox.ini | 6 ++++++ python-packages/contract_wrappers/tox.ini | 5 +++-- python-packages/json_schemas/tox.ini | 5 +++-- python-packages/order_utils/tox.ini | 5 +++-- python-packages/sra_client/tox.ini | 6 +++--- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/python-packages/contract_addresses/tox.ini b/python-packages/contract_addresses/tox.ini index 1cce32b5f0..944049af60 100644 --- a/python-packages/contract_addresses/tox.ini +++ b/python-packages/contract_addresses/tox.ini @@ -10,3 +10,9 @@ envlist = py37 commands = pip install -e .[dev] python setup.py test + +[testenv:run_tests_against_deployment] +setenv = PY_IGNORE_IMPORTMISMATCH = 1 +commands= + pip install 0x-contract-addresses[dev] + pytest --doctest-modules src diff --git a/python-packages/contract_artifacts/tox.ini b/python-packages/contract_artifacts/tox.ini index 1cce32b5f0..4fd5f65639 100644 --- a/python-packages/contract_artifacts/tox.ini +++ b/python-packages/contract_artifacts/tox.ini @@ -10,3 +10,9 @@ envlist = py37 commands = pip install -e .[dev] python setup.py test + +[testenv:run_tests_against_deployment] +setenv = PY_IGNORE_IMPORTMISMATCH = 1 +commands= + pip install 0x-contract-artifacts[dev] + pytest --doctest-modules src diff --git a/python-packages/contract_wrappers/tox.ini b/python-packages/contract_wrappers/tox.ini index cd847a5bb8..c3e7029329 100644 --- a/python-packages/contract_wrappers/tox.ini +++ b/python-packages/contract_wrappers/tox.ini @@ -20,6 +20,7 @@ commands = pytest test [testenv:run_tests_against_deployment] +setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-contract-wrappers - pytest test + pip install 0x-contract-wrappers[dev] + pytest --doctest-modules src test diff --git a/python-packages/json_schemas/tox.ini b/python-packages/json_schemas/tox.ini index 1d5de646ef..87ec5e8473 100644 --- a/python-packages/json_schemas/tox.ini +++ b/python-packages/json_schemas/tox.ini @@ -20,6 +20,7 @@ commands = pytest test [testenv:run_tests_against_deployment] +setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-json-schemas - pytest test + pip install 0x-json-schemas[dev] + pytest --doctest-modules src test diff --git a/python-packages/order_utils/tox.ini b/python-packages/order_utils/tox.ini index ba7d55b569..c51a7e14f0 100644 --- a/python-packages/order_utils/tox.ini +++ b/python-packages/order_utils/tox.ini @@ -20,6 +20,7 @@ commands = pytest test [testenv:run_tests_against_deployment] +setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-order-utils - pytest test + pip install 0x-order-utils[dev] + pytest --doctest-modules src test diff --git a/python-packages/sra_client/tox.ini b/python-packages/sra_client/tox.ini index 0d776c7b07..2f40a07c89 100644 --- a/python-packages/sra_client/tox.ini +++ b/python-packages/sra_client/tox.ini @@ -18,7 +18,7 @@ commands = pytest test [testenv:run_tests_against_deployment] -deps=pytest +setenv = PY_IGNORE_IMPORTMISMATCH = 1 commands = - pip install 0x-sra-client - pytest test + pip install 0x-sra-client[dev] + pytest --doctest-modules src test From 19fac9206cf760566d67b13a1d6ea2eeed7ee3b8 Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Tue, 5 Nov 2019 17:02:00 -0500 Subject: [PATCH 28/28] contract_wrappers example: rm xtra Order attribute Thanks to @steveklebanoff for catching this. https://github.com/0xProject/0x-monorepo/pull/2284#pullrequestreview-312065368 --- .../contract_wrappers/src/zero_ex/contract_wrappers/__init__.py | 1 - 1 file changed, 1 deletion(-) 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 538607c6fa..538d378689 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 @@ -138,7 +138,6 @@ ... ), ... makerFeeAssetData='0x', ... takerFeeAssetData='0x', -... chain_id=Web3(ganache).eth.chainId, ... ) For this order to be valid, our Maker must sign a hash of it: