Migrate Python libraries to v3 (#2284)

* .gitignore migrations/0x_ganache_snapshot

* .gitignore new-ish Python contract wrappers

These should have been added back when we started generating these
wrappers.

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

* Eliminate circular dependency

This was preventing the Exchange wrapper from ever importing its
validator!

* 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).

* Silence new versions of linters

Newer versions care about this stuff.  Old versions didn't, and we don't
either.

* Support Rich Reverts via Web3.py middleware

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

* Migrate to Exchange v3

* Fix typo in DevUtils documentation

* Include new contracts in docs

* Re-enable Python checks in CI

* Accept strings for bytes

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

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

* wrapper base: don't assume there are accounts

* Eliminate some inline linter directives

* make CHANGELOGs be REVERSE chronological

* Update CHANGELOG entries and bump version numbers

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

* sra_client.py: incl. docker in `./setup.py clean`

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

* abi-gen/templates/Py: clarify if/else logic

In response to
https://github.com/0xProject/0x-monorepo/pull/2284#discussion_r342200906

* sra_client.py: Update CHANGELOG and bump version

* contract_addresses/setup.py: rm unnecessary rm

* json_schemas.py: corrections to dev dependencies

* In tests against deployment, also run doctests

* contract_wrappers example: rm xtra Order attribute

Thanks to @steveklebanoff for catching this.
https://github.com/0xProject/0x-monorepo/pull/2284#pullrequestreview-312065368
This commit is contained in:
F. Eugene Aumson 2019-11-05 23:04:29 -05:00 committed by GitHub
parent cbe4c4fbf9
commit e61f23d001
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 1996 additions and 941 deletions

View File

@ -192,14 +192,33 @@ jobs:
working_directory: ~/repo working_directory: ~/repo
docker: docker:
- image: nikolaik/python-nodejs:python3.7-nodejs8 - image: nikolaik/python-nodejs:python3.7-nodejs8
- image: 0xorg/ganache-cli:2.2.2 - image: 0xorg/ganache-cli:4.4.0-beta.1
- image: 0xorg/launch-kit-backend:74bcc39
environment: environment:
RPC_URL: http://localhost:8545 VERSION: 4.4.0-beta.1
SNAPSHOT_NAME: 0x_ganache_snapshot-v3-beta
- image: 0xorg/mesh:6.0.0-beta-0xv3
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'
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 NETWORK_ID: 50
WHITELIST_ALL_TOKENS: True WHITELIST_ALL_TOKENS: True
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
MAKER_FEE_UNIT_AMOUNT: 0
TAKER_FEE_UNIT_AMOUNT: 0
MESH_ENDPOINT: 'ws://localhost:60557'
command: | 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: steps:
- checkout - checkout
- restore_cache: - restore_cache:
@ -221,8 +240,14 @@ jobs:
- run: - run:
command: | command: |
cd python-packages cd python-packages
./parallel_without_sra_client coverage run setup.py test ./parallel coverage run setup.py test
./build_docs ./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: - save_cache:
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }} key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
paths: paths:
@ -247,8 +272,6 @@ jobs:
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }} key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
paths: paths:
- ~/repo/python-packages/sra_client/.coverage - ~/repo/python-packages/sra_client/.coverage
- store_artifacts:
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
- store_artifacts: - store_artifacts:
path: ~/repo/python-packages/contract_addresses/build path: ~/repo/python-packages/contract_addresses/build
- store_artifacts: - store_artifacts:
@ -426,12 +449,11 @@ workflows:
- test-exchange-ganache-3.0 - test-exchange-ganache-3.0
- test-rest - test-rest
- static-tests - static-tests
# - test-python: - test-python:
# requires: requires:
# - build - build
# - test-rest - static-tests-python:
# - static-tests-python: requires:
# requires: - build
# - test-python
# skip python tox run for now, as we don't yet have multiple test environments to support. # skip python tox run for now, as we don't yet have multiple test environments to support.
# - test-rest-python # - test-rest-python

10
.gitignore vendored
View File

@ -130,6 +130,7 @@ contracts/erc1155/generated-wrappers/
contracts/extensions/generated-wrappers/ contracts/extensions/generated-wrappers/
contracts/exchange-forwarder/generated-wrappers/ contracts/exchange-forwarder/generated-wrappers/
contracts/dev-utils/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/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/exchange/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__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_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/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/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/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_proxy/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__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/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/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/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/weth9/__init__.py
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
@ -164,10 +168,14 @@ __pycache__
python-packages/*/src/*.egg-info python-packages/*/src/*.egg-info
python-packages/*/.coverage 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/json_schemas/src/zero_ex/json_schemas/schemas
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
# Doc README copy # Doc README copy
packages/*/docs/README.md packages/*/docs/README.md
.DS_Store .DS_Store
# the snapshot that gets built for migrations sure does have a ton of files
packages/migrations/0x_ganache_snapshot*

View File

@ -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", "version": "4.4.0-beta.0",
"changes": [ "changes": [

View File

@ -115,7 +115,7 @@ export const utils = {
{ regex: '^address$', pyType: 'str' }, { regex: '^address$', pyType: 'str' },
{ regex: '^bool$', pyType: 'bool' }, { regex: '^bool$', pyType: 'bool' },
{ regex: '^u?int\\d*$', pyType: 'int' }, { regex: '^u?int\\d*$', pyType: 'int' },
{ regex: '^bytes\\d*$', pyType: 'bytes' }, { regex: '^bytes\\d*$', pyType: 'Union[bytes, str]' },
]; ];
for (const regexAndTxType of solTypeRegexToPyType) { for (const regexAndTxType of solTypeRegexToPyType) {
const { regex, pyType } = regexAndTxType; const { regex, pyType } = regexAndTxType;

View File

@ -40,6 +40,12 @@ except ImportError:
"""No-op input validator.""" """No-op input validator."""
try:
from .middleware import MIDDLEWARE # type: ignore
except ImportError:
pass
{{tupleDefinitions ABIString}} {{tupleDefinitions ABIString}}
{{#each methods}} {{#each methods}}
@ -59,30 +65,57 @@ class {{contractName}}:
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
validator: {{contractName}}Validator = None, validator: {{contractName}}Validator = None,
): ):
"""Get an instance of wrapper for smart contract. """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 contract_address: where the contract has been deployed
:param validator: for validation of method inputs. :param validator: for validation of method inputs.
""" """
# pylint: disable=too-many-statements
self.contract_address = contract_address self.contract_address = contract_address
if not validator: if not validator:
validator = {{contractName}}Validator(provider, contract_address) validator = {{contractName}}Validator(web3_or_provider, contract_address)
self._web3_eth = Web3( # type: ignore # pylint: disable=no-member web3 = None
provider if isinstance(web3_or_provider, BaseProvider):
).eth web3 = Web3(web3_or_provider)
elif isinstance(web3_or_provider, Web3):
web3 = web3_or_provider
else:
raise TypeError(
"Expected parameter 'web3_or_provider' to be an instance of either"
+ " Web3 or BaseProvider"
)
# if any middleware was imported, inject it
try:
MIDDLEWARE
except NameError:
pass
else:
try:
for middleware in MIDDLEWARE:
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
{{#if methods}} {{#if methods}}
functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions
{{#each methods}} {{#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}} {{/each}}
{{/if}} {{/if}}

View File

@ -2,9 +2,9 @@
class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod): class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod):
"""Various interfaces to the {{this.name}} method.""" """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.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
{{#if inputs}} {{#if inputs}}
@ -21,13 +21,6 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod):
{{else if (equal type 'uint256')}} {{else if (equal type 'uint256')}}
# safeguard against fractional inputs # safeguard against fractional inputs
{{toPythonIdentifier this.name}} = int({{toPythonIdentifier this.name}}) {{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}} {{/if}}
{{/each}} {{/each}}
return ({{> params }}) return ({{> params }})

View File

@ -40,6 +40,12 @@ except ImportError:
"""No-op input validator.""" """No-op input validator."""
try:
from .middleware import MIDDLEWARE # type: ignore
except ImportError:
pass
class Tuple0x246f9407(TypedDict): class Tuple0x246f9407(TypedDict):
"""Python representation of a tuple or struct. """Python representation of a tuple or struct.
@ -94,11 +100,11 @@ class Tuple0xcf8ad995(TypedDict):
accomplished via `str.encode("utf_8")`:code: accomplished via `str.encode("utf_8")`:code:
""" """
someBytes: bytes someBytes: Union[bytes, str]
anInteger: int anInteger: int
aDynamicArrayOfBytes: List[bytes] aDynamicArrayOfBytes: List[Union[bytes, str]]
aString: str aString: str
@ -142,7 +148,7 @@ class Tuple0xf95128ef(TypedDict):
foo: int foo: int
bar: bytes bar: Union[bytes, str]
car: str car: str
@ -165,9 +171,9 @@ class Tuple0xa057bf41(TypedDict):
input: Tuple0xf95128ef input: Tuple0xf95128ef
lorem: bytes lorem: Union[bytes, str]
ipsum: bytes ipsum: Union[bytes, str]
dolor: str dolor: str
@ -177,13 +183,13 @@ class SimpleRequireMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> None: def call(self, tx_params: Optional[TxParams] = None) -> None:
@ -217,27 +223,26 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function 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.""" """Validate the inputs to the acceptsAnArrayOfBytes method."""
self.validator.assert_valid( self.validator.assert_valid(
method_name="acceptsAnArrayOfBytes", method_name="acceptsAnArrayOfBytes",
parameter_name="a", parameter_name="a",
argument_value=a, argument_value=a,
) )
a = [bytes.fromhex(a_element.decode("utf-8")) for a_element in a]
return a return a
def call( def call(
self, a: List[bytes], tx_params: Optional[TxParams] = None self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None
) -> None: ) -> None:
"""Execute underlying contract method via eth_call. """Execute underlying contract method via eth_call.
@ -252,7 +257,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod):
return self.underlying_method(a).call(tx_params.as_dict()) return self.underlying_method(a).call(tx_params.as_dict())
def send_transaction( 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]: ) -> Union[HexBytes, bytes]:
"""Execute underlying contract method via eth_sendTransaction. """Execute underlying contract method via eth_sendTransaction.
@ -267,7 +272,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod):
return self.underlying_method(a).transact(tx_params.as_dict()) return self.underlying_method(a).transact(tx_params.as_dict())
def estimate_gas( def estimate_gas(
self, a: List[bytes], tx_params: Optional[TxParams] = None self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None
) -> int: ) -> int:
"""Estimate gas consumption of method call.""" """Estimate gas consumption of method call."""
(a) = self.validate_and_normalize_inputs(a) (a) = self.validate_and_normalize_inputs(a)
@ -280,13 +285,13 @@ class SimpleInputSimpleOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, index_0: int): def validate_and_normalize_inputs(self, index_0: int):
@ -340,13 +345,13 @@ class WithdrawMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, wad: int): def validate_and_normalize_inputs(self, wad: int):
@ -395,17 +400,17 @@ class MultiInputMultiOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs( 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.""" """Validate the inputs to the multiInputMultiOutput method."""
self.validator.assert_valid( self.validator.assert_valid(
@ -420,7 +425,6 @@ class MultiInputMultiOutputMethod(ContractMethod):
parameter_name="index_1", parameter_name="index_1",
argument_value=index_1, argument_value=index_1,
) )
index_1 = bytes.fromhex(index_1.decode("utf-8"))
self.validator.assert_valid( self.validator.assert_valid(
method_name="multiInputMultiOutput", method_name="multiInputMultiOutput",
parameter_name="index_2", parameter_name="index_2",
@ -431,10 +435,10 @@ class MultiInputMultiOutputMethod(ContractMethod):
def call( def call(
self, self,
index_0: int, index_0: int,
index_1: bytes, index_1: Union[bytes, str],
index_2: str, index_2: str,
tx_params: Optional[TxParams] = None, tx_params: Optional[TxParams] = None,
) -> Tuple[bytes, bytes, str]: ) -> Tuple[Union[bytes, str], Union[bytes, str], str]:
"""Execute underlying contract method via eth_call. """Execute underlying contract method via eth_call.
Tests decoding when the input and output are complex and have more than Tests decoding when the input and output are complex and have more than
@ -454,7 +458,7 @@ class MultiInputMultiOutputMethod(ContractMethod):
def send_transaction( def send_transaction(
self, self,
index_0: int, index_0: int,
index_1: bytes, index_1: Union[bytes, str],
index_2: str, index_2: str,
tx_params: Optional[TxParams] = None, tx_params: Optional[TxParams] = None,
) -> Union[HexBytes, bytes]: ) -> Union[HexBytes, bytes]:
@ -477,7 +481,7 @@ class MultiInputMultiOutputMethod(ContractMethod):
def estimate_gas( def estimate_gas(
self, self,
index_0: int, index_0: int,
index_1: bytes, index_1: Union[bytes, str],
index_2: str, index_2: str,
tx_params: Optional[TxParams] = None, tx_params: Optional[TxParams] = None,
) -> int: ) -> int:
@ -496,17 +500,21 @@ class EcrecoverFnMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs( 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.""" """Validate the inputs to the ecrecoverFn method."""
self.validator.assert_valid( self.validator.assert_valid(
@ -527,10 +535,10 @@ class EcrecoverFnMethod(ContractMethod):
def call( def call(
self, self,
_hash: bytes, _hash: Union[bytes, str],
v: int, v: int,
r: bytes, r: Union[bytes, str],
s: bytes, s: Union[bytes, str],
tx_params: Optional[TxParams] = None, tx_params: Optional[TxParams] = None,
) -> str: ) -> str:
"""Execute underlying contract method via eth_call. """Execute underlying contract method via eth_call.
@ -555,10 +563,10 @@ class EcrecoverFnMethod(ContractMethod):
def send_transaction( def send_transaction(
self, self,
_hash: bytes, _hash: Union[bytes, str],
v: int, v: int,
r: bytes, r: Union[bytes, str],
s: bytes, s: Union[bytes, str],
tx_params: Optional[TxParams] = None, tx_params: Optional[TxParams] = None,
) -> Union[HexBytes, bytes]: ) -> Union[HexBytes, bytes]:
"""Execute underlying contract method via eth_sendTransaction. """Execute underlying contract method via eth_sendTransaction.
@ -585,10 +593,10 @@ class EcrecoverFnMethod(ContractMethod):
def estimate_gas( def estimate_gas(
self, self,
_hash: bytes, _hash: Union[bytes, str],
v: int, v: int,
r: bytes, r: Union[bytes, str],
s: bytes, s: Union[bytes, str],
tx_params: Optional[TxParams] = None, tx_params: Optional[TxParams] = None,
) -> int: ) -> int:
"""Estimate gas consumption of method call.""" """Estimate gas consumption of method call."""
@ -604,24 +612,25 @@ class AcceptsBytesMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function 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.""" """Validate the inputs to the acceptsBytes method."""
self.validator.assert_valid( self.validator.assert_valid(
method_name="acceptsBytes", parameter_name="a", argument_value=a method_name="acceptsBytes", parameter_name="a", argument_value=a
) )
a = bytes.fromhex(a.decode("utf-8"))
return 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. """Execute underlying contract method via eth_call.
:param tx_params: transaction parameters :param tx_params: transaction parameters
@ -632,7 +641,7 @@ class AcceptsBytesMethod(ContractMethod):
return self.underlying_method(a).call(tx_params.as_dict()) return self.underlying_method(a).call(tx_params.as_dict())
def send_transaction( def send_transaction(
self, a: bytes, tx_params: Optional[TxParams] = None self, a: Union[bytes, str], tx_params: Optional[TxParams] = None
) -> Union[HexBytes, bytes]: ) -> Union[HexBytes, bytes]:
"""Execute underlying contract method via eth_sendTransaction. """Execute underlying contract method via eth_sendTransaction.
@ -644,7 +653,7 @@ class AcceptsBytesMethod(ContractMethod):
return self.underlying_method(a).transact(tx_params.as_dict()) return self.underlying_method(a).transact(tx_params.as_dict())
def estimate_gas( def estimate_gas(
self, a: bytes, tx_params: Optional[TxParams] = None self, a: Union[bytes, str], tx_params: Optional[TxParams] = None
) -> int: ) -> int:
"""Estimate gas consumption of method call.""" """Estimate gas consumption of method call."""
(a) = self.validate_and_normalize_inputs(a) (a) = self.validate_and_normalize_inputs(a)
@ -657,13 +666,13 @@ class NoInputSimpleOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> int: def call(self, tx_params: Optional[TxParams] = None) -> int:
@ -701,13 +710,13 @@ class RevertWithConstantMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> None: def call(self, tx_params: Optional[TxParams] = None) -> None:
@ -741,13 +750,13 @@ class SimpleRevertMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> None: def call(self, tx_params: Optional[TxParams] = None) -> None:
@ -783,13 +792,13 @@ class MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod(
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> Tuple0x1b9da225: def call(self, tx_params: Optional[TxParams] = None) -> Tuple0x1b9da225:
@ -823,13 +832,13 @@ class NestedStructOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5: def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5:
@ -863,13 +872,13 @@ class RequireWithConstantMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> None: def call(self, tx_params: Optional[TxParams] = None) -> None:
@ -903,13 +912,13 @@ class WithAddressInputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs( def validate_and_normalize_inputs(
@ -1011,13 +1020,13 @@ class StructInputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, s: Tuple0xcf8ad995): def validate_and_normalize_inputs(self, s: Tuple0xcf8ad995):
@ -1065,13 +1074,13 @@ class NonPureMethodMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call( def call(
@ -1106,13 +1115,13 @@ class ComplexInputComplexOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, complex_input: Tuple0xf95128ef): def validate_and_normalize_inputs(self, complex_input: Tuple0xf95128ef):
@ -1176,13 +1185,13 @@ class NoInputNoOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> None: def call(self, tx_params: Optional[TxParams] = None) -> None:
@ -1220,13 +1229,13 @@ class SimplePureFunctionWithInputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, x: int): def validate_and_normalize_inputs(self, x: int):
@ -1276,13 +1285,13 @@ class NonPureMethodThatReturnsNothingMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call( def call(
@ -1317,13 +1326,13 @@ class SimplePureFunctionMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> int: def call(self, tx_params: Optional[TxParams] = None) -> int:
@ -1357,13 +1366,13 @@ class NestedStructInputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, n: Tuple0xc9bdd2d5): def validate_and_normalize_inputs(self, n: Tuple0xc9bdd2d5):
@ -1413,13 +1422,13 @@ class MethodReturningMultipleValuesMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> Tuple[int, str]: def call(self, tx_params: Optional[TxParams] = None) -> Tuple[int, str]:
@ -1453,13 +1462,13 @@ class MethodReturningArrayOfStructsMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call( def call(
@ -1495,13 +1504,13 @@ class EmitSimpleEventMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call( def call(
@ -1536,13 +1545,13 @@ class StructOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995: def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995:
@ -1580,13 +1589,13 @@ class PureFunctionWithConstantMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def call(self, tx_params: Optional[TxParams] = None) -> int: def call(self, tx_params: Optional[TxParams] = None) -> int:
@ -1620,13 +1629,13 @@ class SimpleInputNoOutputMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, index_0: int): def validate_and_normalize_inputs(self, index_0: int):
@ -1680,13 +1689,13 @@ class OverloadedMethod2Method(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, a: str): def validate_and_normalize_inputs(self, a: str):
@ -1734,13 +1743,13 @@ class OverloadedMethod1Method(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, a: int): def validate_and_normalize_inputs(self, a: int):
@ -1943,24 +1952,55 @@ class AbiGenDummy:
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
validator: AbiGenDummyValidator = None, validator: AbiGenDummyValidator = None,
): ):
"""Get an instance of wrapper for smart contract. """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 contract_address: where the contract has been deployed
:param validator: for validation of method inputs. :param validator: for validation of method inputs.
""" """
# pylint: disable=too-many-statements
self.contract_address = contract_address self.contract_address = contract_address
if not validator: if not validator:
validator = AbiGenDummyValidator(provider, contract_address) validator = AbiGenDummyValidator(
web3_or_provider, contract_address
)
self._web3_eth = Web3( # type: ignore # pylint: disable=no-member web3 = None
provider if isinstance(web3_or_provider, BaseProvider):
).eth web3 = Web3(web3_or_provider)
elif isinstance(web3_or_provider, Web3):
web3 = web3_or_provider
else:
raise TypeError(
"Expected parameter 'web3_or_provider' to be an instance of either"
+ " Web3 or BaseProvider"
)
# if any middleware was imported, inject it
try:
MIDDLEWARE
except NameError:
pass
else:
try:
for middleware in MIDDLEWARE:
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
functions = self._web3_eth.contract( functions = self._web3_eth.contract(
address=to_checksum_address(contract_address), address=to_checksum_address(contract_address),
@ -1968,162 +2008,210 @@ class AbiGenDummy:
).functions ).functions
self.simple_require = SimpleRequireMethod( 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( self.accepts_an_array_of_bytes = AcceptsAnArrayOfBytesMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.acceptsAnArrayOfBytes, functions.acceptsAnArrayOfBytes,
validator, validator,
) )
self.simple_input_simple_output = SimpleInputSimpleOutputMethod( self.simple_input_simple_output = SimpleInputSimpleOutputMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.simpleInputSimpleOutput, functions.simpleInputSimpleOutput,
validator, validator,
) )
self.withdraw = WithdrawMethod( self.withdraw = WithdrawMethod(
provider, contract_address, functions.withdraw, validator web3_or_provider, contract_address, functions.withdraw, validator
) )
self.multi_input_multi_output = MultiInputMultiOutputMethod( self.multi_input_multi_output = MultiInputMultiOutputMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.multiInputMultiOutput, functions.multiInputMultiOutput,
validator, validator,
) )
self.ecrecover_fn = EcrecoverFnMethod( self.ecrecover_fn = EcrecoverFnMethod(
provider, contract_address, functions.ecrecoverFn, validator web3_or_provider,
contract_address,
functions.ecrecoverFn,
validator,
) )
self.accepts_bytes = AcceptsBytesMethod( self.accepts_bytes = AcceptsBytesMethod(
provider, contract_address, functions.acceptsBytes, validator web3_or_provider,
contract_address,
functions.acceptsBytes,
validator,
) )
self.no_input_simple_output = NoInputSimpleOutputMethod( self.no_input_simple_output = NoInputSimpleOutputMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.noInputSimpleOutput, functions.noInputSimpleOutput,
validator, validator,
) )
self.revert_with_constant = RevertWithConstantMethod( self.revert_with_constant = RevertWithConstantMethod(
provider, contract_address, functions.revertWithConstant, validator web3_or_provider,
contract_address,
functions.revertWithConstant,
validator,
) )
self.simple_revert = SimpleRevertMethod( 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( self.method_using_nested_struct_with_inner_struct_not_used_elsewhere = MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.methodUsingNestedStructWithInnerStructNotUsedElsewhere, functions.methodUsingNestedStructWithInnerStructNotUsedElsewhere,
validator, validator,
) )
self.nested_struct_output = NestedStructOutputMethod( self.nested_struct_output = NestedStructOutputMethod(
provider, contract_address, functions.nestedStructOutput, validator web3_or_provider,
contract_address,
functions.nestedStructOutput,
validator,
) )
self.require_with_constant = RequireWithConstantMethod( self.require_with_constant = RequireWithConstantMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.requireWithConstant, functions.requireWithConstant,
validator, validator,
) )
self.with_address_input = WithAddressInputMethod( self.with_address_input = WithAddressInputMethod(
provider, contract_address, functions.withAddressInput, validator web3_or_provider,
contract_address,
functions.withAddressInput,
validator,
) )
self.struct_input = StructInputMethod( self.struct_input = StructInputMethod(
provider, contract_address, functions.structInput, validator web3_or_provider,
contract_address,
functions.structInput,
validator,
) )
self.non_pure_method = NonPureMethodMethod( 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( self.complex_input_complex_output = ComplexInputComplexOutputMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.complexInputComplexOutput, functions.complexInputComplexOutput,
validator, validator,
) )
self.no_input_no_output = NoInputNoOutputMethod( 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( self.simple_pure_function_with_input = SimplePureFunctionWithInputMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.simplePureFunctionWithInput, functions.simplePureFunctionWithInput,
validator, validator,
) )
self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod( self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.nonPureMethodThatReturnsNothing, functions.nonPureMethodThatReturnsNothing,
validator, validator,
) )
self.simple_pure_function = SimplePureFunctionMethod( self.simple_pure_function = SimplePureFunctionMethod(
provider, contract_address, functions.simplePureFunction, validator web3_or_provider,
contract_address,
functions.simplePureFunction,
validator,
) )
self.nested_struct_input = NestedStructInputMethod( 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( self.method_returning_multiple_values = MethodReturningMultipleValuesMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.methodReturningMultipleValues, functions.methodReturningMultipleValues,
validator, validator,
) )
self.method_returning_array_of_structs = MethodReturningArrayOfStructsMethod( self.method_returning_array_of_structs = MethodReturningArrayOfStructsMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.methodReturningArrayOfStructs, functions.methodReturningArrayOfStructs,
validator, validator,
) )
self.emit_simple_event = EmitSimpleEventMethod( self.emit_simple_event = EmitSimpleEventMethod(
provider, contract_address, functions.emitSimpleEvent, validator web3_or_provider,
contract_address,
functions.emitSimpleEvent,
validator,
) )
self.struct_output = StructOutputMethod( self.struct_output = StructOutputMethod(
provider, contract_address, functions.structOutput, validator web3_or_provider,
contract_address,
functions.structOutput,
validator,
) )
self.pure_function_with_constant = PureFunctionWithConstantMethod( self.pure_function_with_constant = PureFunctionWithConstantMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.pureFunctionWithConstant, functions.pureFunctionWithConstant,
validator, validator,
) )
self.simple_input_no_output = SimpleInputNoOutputMethod( self.simple_input_no_output = SimpleInputNoOutputMethod(
provider, web3_or_provider,
contract_address, contract_address,
functions.simpleInputNoOutput, functions.simpleInputNoOutput,
validator, validator,
) )
self.overloaded_method2 = OverloadedMethod2Method( self.overloaded_method2 = OverloadedMethod2Method(
provider, contract_address, functions.overloadedMethod, validator web3_or_provider,
contract_address,
functions.overloadedMethod,
validator,
) )
self.overloaded_method1 = OverloadedMethod1Method( self.overloaded_method1 = OverloadedMethod1Method(
provider, contract_address, functions.overloadedMethod, validator web3_or_provider,
contract_address,
functions.overloadedMethod,
validator,
) )
def get_withdrawal_event( def get_withdrawal_event(

View File

@ -40,30 +40,65 @@ except ImportError:
"""No-op input validator.""" """No-op input validator."""
try:
from .middleware import MIDDLEWARE # type: ignore
except ImportError:
pass
# pylint: disable=too-many-public-methods,too-many-instance-attributes # pylint: disable=too-many-public-methods,too-many-instance-attributes
class LibDummy: class LibDummy:
"""Wrapper class for LibDummy Solidity contract.""" """Wrapper class for LibDummy Solidity contract."""
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
validator: LibDummyValidator = None, validator: LibDummyValidator = None,
): ):
"""Get an instance of wrapper for smart contract. """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 contract_address: where the contract has been deployed
:param validator: for validation of method inputs. :param validator: for validation of method inputs.
""" """
# pylint: disable=too-many-statements
self.contract_address = contract_address self.contract_address = contract_address
if not validator: if not validator:
validator = LibDummyValidator(provider, contract_address) validator = LibDummyValidator(web3_or_provider, contract_address)
self._web3_eth = Web3( # type: ignore # pylint: disable=no-member web3 = None
provider if isinstance(web3_or_provider, BaseProvider):
).eth web3 = Web3(web3_or_provider)
elif isinstance(web3_or_provider, Web3):
web3 = web3_or_provider
else:
raise TypeError(
"Expected parameter 'web3_or_provider' to be an instance of either"
+ " Web3 or BaseProvider"
)
# if any middleware was imported, inject it
try:
MIDDLEWARE
except NameError:
pass
else:
try:
for middleware in MIDDLEWARE:
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
@staticmethod @staticmethod
def abi(): def abi():

View File

@ -40,18 +40,24 @@ except ImportError:
"""No-op input validator.""" """No-op input validator."""
try:
from .middleware import MIDDLEWARE # type: ignore
except ImportError:
pass
class PublicAddConstantMethod(ContractMethod): class PublicAddConstantMethod(ContractMethod):
"""Various interfaces to the publicAddConstant method.""" """Various interfaces to the publicAddConstant method."""
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, x: int): def validate_and_normalize_inputs(self, x: int):
@ -101,13 +107,13 @@ class PublicAddOneMethod(ContractMethod):
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
contract_function: ContractFunction, contract_function: ContractFunction,
validator: Validator = None, validator: Validator = None,
): ):
"""Persist instance data.""" """Persist instance data."""
super().__init__(provider, contract_address, validator) super().__init__(web3_or_provider, contract_address, validator)
self.underlying_method = contract_function self.underlying_method = contract_function
def validate_and_normalize_inputs(self, x: int): def validate_and_normalize_inputs(self, x: int):
@ -166,24 +172,55 @@ class TestLibDummy:
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
validator: TestLibDummyValidator = None, validator: TestLibDummyValidator = None,
): ):
"""Get an instance of wrapper for smart contract. """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 contract_address: where the contract has been deployed
:param validator: for validation of method inputs. :param validator: for validation of method inputs.
""" """
# pylint: disable=too-many-statements
self.contract_address = contract_address self.contract_address = contract_address
if not validator: if not validator:
validator = TestLibDummyValidator(provider, contract_address) validator = TestLibDummyValidator(
web3_or_provider, contract_address
)
self._web3_eth = Web3( # type: ignore # pylint: disable=no-member web3 = None
provider if isinstance(web3_or_provider, BaseProvider):
).eth web3 = Web3(web3_or_provider)
elif isinstance(web3_or_provider, Web3):
web3 = web3_or_provider
else:
raise TypeError(
"Expected parameter 'web3_or_provider' to be an instance of either"
+ " Web3 or BaseProvider"
)
# if any middleware was imported, inject it
try:
MIDDLEWARE
except NameError:
pass
else:
try:
for middleware in MIDDLEWARE:
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
functions = self._web3_eth.contract( functions = self._web3_eth.contract(
address=to_checksum_address(contract_address), address=to_checksum_address(contract_address),
@ -191,11 +228,17 @@ class TestLibDummy:
).functions ).functions
self.public_add_constant = PublicAddConstantMethod( self.public_add_constant = PublicAddConstantMethod(
provider, contract_address, functions.publicAddConstant, validator web3_or_provider,
contract_address,
functions.publicAddConstant,
validator,
) )
self.public_add_one = PublicAddOneMethod( self.public_add_one = PublicAddOneMethod(
provider, contract_address, functions.publicAddOne, validator web3_or_provider,
contract_address,
functions.publicAddOne,
validator,
) )
@staticmethod @staticmethod

View File

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

View File

@ -1,5 +1,7 @@
import * as _ from 'lodash'; import * as _ from 'lodash';
import addresses from '../addresses.json';
export interface ContractAddresses { export interface ContractAddresses {
erc20Proxy: string; erc20Proxy: string;
erc721Proxy: string; erc721Proxy: string;
@ -32,127 +34,6 @@ export enum NetworkId {
Ganache = 50, 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 * 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 * Ethereum mainnet or a supported testnet. Throws if there are no known
@ -162,6 +43,8 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
* given networkId. * given networkId.
*/ */
export function getContractAddressesForNetworkOrThrow(networkId: NetworkId): ContractAddresses { export function getContractAddressesForNetworkOrThrow(networkId: NetworkId): ContractAddresses {
const networkToAddresses: { [networkId: number]: ContractAddresses } = addresses;
if (networkToAddresses[networkId] === undefined) { if (networkToAddresses[networkId] === undefined) {
throw new Error(`Unknown network id (${networkId}). No known 0x contracts have been deployed on this network.`); throw new Error(`Unknown network id (${networkId}). No known 0x contracts have been deployed on this network.`);
} }

View File

@ -2,7 +2,10 @@
"extends": "../../tsconfig", "extends": "../../tsconfig",
"compilerOptions": { "compilerOptions": {
"outDir": "lib", "outDir": "lib",
"rootDir": "." "rootDir": ".",
"resolveJsonModule": true,
"esModuleInterop": true
}, },
"include": ["./src/**/*"] "include": ["./src/**/*"],
"files": ["./addresses.json"]
} }

View File

@ -2,10 +2,14 @@
"""setuptools module for contract_addresses package.""" """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 import subprocess # nosec
from shutil import rmtree from shutil import copyfile, rmtree
from os import environ, path from os import environ, path
from sys import argv from sys import argv, exit # pylint: disable=redefined-builtin
from distutils.command.clean import clean from distutils.command.clean import clean
import distutils.command.build_py import distutils.command.build_py
@ -13,6 +17,34 @@ from setuptools import find_packages, setup
from setuptools.command.test import test as TestCommand 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"
)
copyfile(
path.join(
pkgdir,
"..",
"..",
"packages",
"contract-addresses",
"addresses.json",
),
path.join(destination_path, "addresses.json"),
)
class LintCommand(distutils.command.build_py.build_py): class LintCommand(distutils.command.build_py.build_py):
"""Custom setuptools command class for running linters.""" """Custom setuptools command class for running linters."""
@ -131,6 +163,7 @@ setup(
author_email="feuGeneA@users.noreply.github.com", author_email="feuGeneA@users.noreply.github.com",
cmdclass={ cmdclass={
"clean": CleanCommandExtension, "clean": CleanCommandExtension,
"pre_install": PreInstallCommand,
"lint": LintCommand, "lint": LintCommand,
"test": TestCommandExtension, "test": TestCommandExtension,
"test_publish": TestPublishCommand, "test_publish": TestPublishCommand,
@ -156,7 +189,9 @@ setup(
] ]
}, },
python_requires=">=3.6, <4", 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"}, package_dir={"": "src"},
license="Apache 2.0", license="Apache 2.0",
keywords=( keywords=(

View File

@ -19,7 +19,7 @@ Python zero_ex.contract_addresses
:members: :members:
:show-inheritance: :show-inheritance:
.. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES .. autodata:: zero_ex.contract_addresses.network_to_addresses
:annotation: : Dict[NetworkId, ContractAddresses] :annotation: : Dict[NetworkId, ContractAddresses]
Indices and tables Indices and tables

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -10,8 +10,11 @@ Install the package with pip::
""" """
from enum import Enum from enum import Enum
import json
from typing import Dict, NamedTuple from typing import Dict, NamedTuple
from pkg_resources import resource_string
class ContractAddresses(NamedTuple): class ContractAddresses(NamedTuple):
"""An abstract record listing all the contracts that have addresses.""" """An abstract record listing all the contracts that have addresses."""
@ -20,10 +23,7 @@ class ContractAddresses(NamedTuple):
"""Address of the ERC20Proxy contract.""" """Address of the ERC20Proxy contract."""
erc721_proxy: str erc721_proxy: str
"""Address of the ERC20Proxy contract.""" """Address of the ERC721Proxy contract."""
erc1155_proxy: str
"""Address of the ERC1155Proxy contract."""
zrx_token: str zrx_token: str
"""Address of the ZRX token contract.""" """Address of the ZRX token contract."""
@ -31,27 +31,57 @@ class ContractAddresses(NamedTuple):
ether_token: str ether_token: str
"""Address of the WETH token contract.""" """Address of the WETH token contract."""
exchange_v2: str
"""Address of the v2 Exchange contract."""
exchange: str exchange: str
"""Address of the Exchange contract.""" """Address of the v3 Exchange contract."""
asset_proxy_owner: str asset_proxy_owner: str
"""Address of the AssetProxyOwner contract.""" """Address of the AssetProxyOwner contract."""
zero_ex_governor: str
"""Address of the ZeroExGovernor contract."""
forwarder: str forwarder: str
"""Address of the Forwarder contract.""" """Address of the Forwarder contract."""
order_validator: str order_validator: str
"""Address of the OrderValidator contract.""" """Address of the OrderValidator contract."""
dutch_auction: str
"""Address of the DutchAuction contract."""
coordinator_registry: str coordinator_registry: str
"""Address of the CoordinatorRegistry contract.""" """Address of the CoordinatorRegistry contract."""
coordinator: str coordinator: str
"""Address of the Coordinator contract.""" """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 dev_utils: str
"""Address of the DevUtils contract.""" """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): class NetworkId(Enum):
"""Network names correlated to their network identification numbers. """Network names correlated to their network identification numbers.
@ -70,83 +100,62 @@ class NetworkId(Enum):
GANACHE = 50 GANACHE = 50
NETWORK_TO_ADDRESSES: Dict[NetworkId, ContractAddresses] = { class _AddressCache:
NetworkId.MAINNET: ContractAddresses( # nosec """A cache to facilitate lazy & singular loading of contract addresses."""
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 # pylint: disable=too-few-public-methods
npm package @0x/migrations.
>>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange # class data, not instance:
0x4f833a24e1f95d70f028921e27040ca56e09ab0b _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"],
)

View File

@ -10,3 +10,9 @@ envlist = py37
commands = commands =
pip install -e .[dev] pip install -e .[dev]
python setup.py test 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

View File

@ -1,9 +1,13 @@
# Changelog # Changelog
## 2.0.0 - 2019-01-09 ## 3.0.0 - TBD
- Initial release. - Updated with artifacts for version 3 of the protocol.
## 2.0.1 - 2019-04-30 ## 2.0.1 - 2019-04-30
- Expanded documentation. - Expanded documentation.
## 2.0.0 - 2019-01-09
- Initial release.

View File

@ -2,10 +2,14 @@
"""setuptools module for contract_artifacts package.""" """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 import subprocess # nosec
from shutil import copytree, rmtree from shutil import copytree, rmtree
from os import environ, path from os import environ, path
from sys import argv from sys import argv, exit # pylint: disable=redefined-builtin
from distutils.command.clean import clean from distutils.command.clean import clean
import distutils.command.build_py import distutils.command.build_py
@ -148,7 +152,7 @@ with open("README.md", "r") as file_handle:
setup( setup(
name="0x-contract-artifacts", name="0x-contract-artifacts",
version="2.0.1", version="3.0.0",
description="0x smart contract compilation artifacts", description="0x smart contract compilation artifacts",
long_description=README_MD, long_description=README_MD,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -1,59 +0,0 @@
{
"schemaVersion": "2.0.0",
"contractName": "EthBalanceChecker",
"compilerOutput": {
"abi": [
{
"constant": true,
"inputs": [{ "internalType": "address[]", "name": "addresses", "type": "address[]" }],
"name": "getEthBalances",
"outputs": [{ "internalType": "uint256[]", "name": "", "type": "uint256[]" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
"devdoc": {
"methods": {
"getEthBalances(address[])": {
"details": "Batch fetches ETH balances",
"params": { "addresses": "Array of addresses." },
"return": "Array of ETH balances."
}
}
},
"evm": {
"bytecode": {
"object": "0x608060405234801561001057600080fd5b506101e5806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a0901e5114610030575b600080fd5b6100d36004803603602081101561004657600080fd5b81019060208101813564010000000081111561006157600080fd5b82018360208201111561007357600080fd5b8035906020019184602083028401116401000000008311171561009557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610123945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561010f5781810151838201526020016100f7565b505050509050019250505060405180910390f35b6060808251604051908082528060200260200182016040528015610151578160200160208202803883390190505b50905060005b835181146101a95783818151811061016b57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff163182828151811061019657fe5b6020908102919091010152600101610157565b509291505056fea265627a7a7231582094309783f0b63086d85d9cb4f6e5be253699056ac1580a863367c5076ecb5c1864736f6c634300050b0032"
},
"deployedBytecode": {
"object": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063a0901e5114610030575b600080fd5b6100d36004803603602081101561004657600080fd5b81019060208101813564010000000081111561006157600080fd5b82018360208201111561007357600080fd5b8035906020019184602083028401116401000000008311171561009557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610123945050505050565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561010f5781810151838201526020016100f7565b505050509050019250505060405180910390f35b6060808251604051908082528060200260200182016040528015610151578160200160208202803883390190505b50905060005b835181146101a95783818151811061016b57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff163182828151811061019657fe5b6020908102919091010152600101610157565b509291505056fea265627a7a7231582094309783f0b63086d85d9cb4f6e5be253699056ac1580a863367c5076ecb5c1864736f6c634300050b0032"
}
}
},
"compiler": {
"name": "solc",
"version": "soljson-v0.5.11+commit.c082d0b4.js",
"settings": {
"optimizer": {
"enabled": true,
"runs": 10000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
},
"evmVersion": "constantinople"
}
},
"networks": {}
}

View File

@ -10,3 +10,9 @@ envlist = py37
commands = commands =
pip install -e .[dev] pip install -e .[dev]
python setup.py test 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

View File

@ -1,5 +1,14 @@
# Changelog # 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 ## 1.1.0 - 2019-08-14
- Added wrapper for DevUtils contract. - Added wrapper for DevUtils contract.

View File

@ -2,11 +2,15 @@
"""setuptools module for contract_wrappers package.""" """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 import subprocess # nosec
from shutil import rmtree from shutil import rmtree
from os import environ, path, remove from os import environ, path, remove
from pathlib import Path from pathlib import Path
from sys import argv from sys import argv, exit # pylint: disable=redefined-builtin
from distutils.command.clean import clean from distutils.command.clean import clean
import distutils.command.build_py import distutils.command.build_py
@ -192,7 +196,7 @@ with open("README.md", "r") as file_handle:
setup( setup(
name="0x-contract-wrappers", name="0x-contract-wrappers",
version="1.1.0", version="2.0.0",
description="Python wrappers for 0x smart contracts", description="Python wrappers for 0x smart contracts",
long_description=README_MD, long_description=README_MD,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@ -34,7 +34,7 @@ zero_ex.contract_wrappers.coordinator_registry
zero_ex.contract_wrappers.dev_utils zero_ex.contract_wrappers.dev_utils
======================================= ===================================
.. automodule:: zero_ex.contract_wrappers.dev_utils .. automodule:: zero_ex.contract_wrappers.dev_utils
:members: :members:
@ -49,6 +49,22 @@ zero_ex.contract_wrappers.dutch_auction
:special-members: :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 zero_ex.contract_wrappers.erc20_proxy
===================================== =====================================
@ -145,6 +161,14 @@ zero_ex.contract_wrappers.order_validator
:special-members: :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 zero_ex.contract_wrappers.weth9
=============================== ===============================
@ -180,18 +204,20 @@ zero_ex.contract_wrappers.exchange.types
.. autoclass:: zero_ex.contract_wrappers.exchange.types.MatchedFillResults .. autoclass:: zero_ex.contract_wrappers.exchange.types.MatchedFillResults
.. autoclass:: zero_ex.contract_wrappers.exchange.types.ZeroExTransaction
zero_ex.contract_wrappers.exchange: Generated Tuples 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 <https://0x.org/docs/contracts#structs-Order>`_. This is the generated class representing `the Order struct <https://0x.org/docs/contracts#structs-Order>`_.
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xbb41e5b3 .. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x735c43e3
This is the generated class representing `the FillResults struct <https://0x.org/docs/contracts#structs-FillResults>`_. This is the generated class representing `the FillResults struct <https://0x.org/docs/contracts#structs-FillResults>`_.
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x054ca44e .. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b
This is the generated class representing `the MatchedFillResults struct <https://0x.org/docs/contracts#structs-MatchedFillResults>`_. This is the generated class representing `the MatchedFillResults struct <https://0x.org/docs/contracts#structs-MatchedFillResults>`_.
@ -199,6 +225,10 @@ zero_ex.contract_wrappers.exchange: Generated Tuples
This is the generated class representing `the OrderInfo struct <https://0x.org/docs/contracts#structs-OrderInfo>`_. This is the generated class representing `the OrderInfo struct <https://0x.org/docs/contracts#structs-OrderInfo>`_.
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe
This is the generated class representing `the ZeroExTransaction struct <https://0x.org/docs/contracts#structs-ZeroExTransaction>`_.
Indices and tables Indices and tables
================== ==================

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -52,10 +52,10 @@ the addresses of the 0x contracts on each network, including those that come
pre-deployed deployed in the `0xorg/ganache-cli`:code: docker image. Let's pre-deployed deployed in the `0xorg/ganache-cli`:code: docker image. Let's
capture the addresses we'll use throughout the examples below: capture the addresses we'll use throughout the examples below:
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId >>> from zero_ex.contract_addresses import network_to_addresses, NetworkId
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token >>> weth_address = network_to_addresses(NetworkId.GANACHE).ether_token
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token >>> zrx_address = network_to_addresses(NetworkId.GANACHE).zrx_token
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange >>> exchange_address = network_to_addresses(NetworkId.GANACHE).exchange
Wrapping ETH Wrapping ETH
------------ ------------
@ -92,15 +92,15 @@ balance:
>>> from zero_ex.contract_wrappers.erc20_token import ERC20Token >>> from zero_ex.contract_wrappers.erc20_token import ERC20Token
>>> zrx_token = ERC20Token( >>> zrx_token = ERC20Token(
... provider=ganache, ... 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( >>> weth_token = ERC20Token(
... provider=ganache, ... 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( >>> tx = zrx_token.approve.send_transaction(
... erc20_proxy_addr, ... erc20_proxy_addr,
@ -135,16 +135,20 @@ Constructing an order
... takerAssetAmount=to_wei(0.1, 'ether'), ... takerAssetAmount=to_wei(0.1, 'ether'),
... expirationTimeSeconds=round( ... expirationTimeSeconds=round(
... (datetime.utcnow() + timedelta(days=1)).timestamp() ... (datetime.utcnow() + timedelta(days=1)).timestamp()
... ) ... ),
... makerFeeAssetData='0x',
... takerFeeAssetData='0x',
... ) ... )
For this order to be valid, our Maker must sign a hash of it: For this order to be valid, our Maker must sign a hash of it:
>>> from zero_ex.order_utils import generate_order_hash_hex >>> 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 >>> from zero_ex.order_utils import sign_hash
>>> maker_signature = sign_hash_to_bytes( >>> maker_signature = sign_hash(
... ganache, Web3.toChecksumAddress(maker_address), order_hash_hex ... ganache, Web3.toChecksumAddress(maker_address), order_hash_hex
... ) ... )
@ -156,16 +160,37 @@ more information on working with Relayers, see `the documentation for
Filling an order Filling an order
---------------- ----------------
Now our Taker will fill the order. The `takerAssetAmount`:code: parameter Now we'll have our Taker fill the order.
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.
>>> from zero_ex.contract_wrappers.exchange import Exchange >>> from zero_ex.contract_wrappers.exchange import Exchange
>>> exchange = Exchange( >>> exchange = Exchange(
... provider=ganache, ... 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:
>>> from zero_ex.contract_wrappers.exchange.types import OrderStatus
>>> OrderStatus(exchange.get_order_info.call(order)[0])
<OrderStatus.FILLABLE: 3>
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( >>> tx_hash = exchange.fill_order.send_transaction(
... order=order, ... order=order,
... taker_asset_fill_amount=order["takerAssetAmount"], ... taker_asset_fill_amount=order["takerAssetAmount"],
@ -184,12 +209,15 @@ the exchange wrapper:
'makerAddress': '0x...', 'makerAddress': '0x...',
'makerAssetData': b..., 'makerAssetData': b...,
'makerAssetFilledAmount': 100000000000000000, 'makerAssetFilledAmount': 100000000000000000,
'makerFeeAssetData': b...,
'makerFeePaid': 0, 'makerFeePaid': 0,
'orderHash': b..., 'orderHash': b...,
'protocolFeePaid': ...,
'senderAddress': '0x...', 'senderAddress': '0x...',
'takerAddress': '0x...', 'takerAddress': '0x...',
'takerAssetData': b..., 'takerAssetData': b...,
'takerAssetFilledAmount': 100000000000000000, 'takerAssetFilledAmount': 100000000000000000,
'takerFeeAssetData': b...,
'takerFeePaid': 0} 'takerFeePaid': 0}
>>> exchange.get_fill_event(tx_hash)[0].args.takerAssetFilledAmount >>> exchange.get_fill_event(tx_hash)[0].args.takerAssetFilledAmount
100000000000000000 100000000000000000
@ -206,7 +234,9 @@ A Maker can cancel an order that has yet to be filled.
... senderAddress='0x0000000000000000000000000000000000000000', ... senderAddress='0x0000000000000000000000000000000000000000',
... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000',
... makerAssetData=asset_data_utils.encode_erc20(weth_address), ... 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), ... takerAssetData=asset_data_utils.encode_erc20(weth_address),
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
... salt=random.randint(1, 100000000000000000), ... salt=random.randint(1, 100000000000000000),
... makerFee=0, ... makerFee=0,
... takerFee=0, ... takerFee=0,
@ -248,7 +278,9 @@ is an example where the taker fills two orders in one transaction:
... senderAddress='0x0000000000000000000000000000000000000000', ... senderAddress='0x0000000000000000000000000000000000000000',
... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000',
... makerAssetData=asset_data_utils.encode_erc20(zrx_address), ... 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), ... takerAssetData=asset_data_utils.encode_erc20(weth_address),
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
... salt=random.randint(1, 100000000000000000), ... salt=random.randint(1, 100000000000000000),
... makerFee=0, ... makerFee=0,
... takerFee=0, ... takerFee=0,
@ -258,10 +290,12 @@ is an example where the taker fills two orders in one transaction:
... (datetime.utcnow() + timedelta(days=1)).timestamp() ... (datetime.utcnow() + timedelta(days=1)).timestamp()
... ) ... )
... ) ... )
>>> signature_1 = sign_hash_to_bytes( >>> signature_1 = sign_hash(
... ganache, ... ganache,
... Web3.toChecksumAddress(maker_address), ... 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( >>> order_2 = Order(
... makerAddress=maker_address, ... makerAddress=maker_address,
@ -269,7 +303,9 @@ is an example where the taker fills two orders in one transaction:
... senderAddress='0x0000000000000000000000000000000000000000', ... senderAddress='0x0000000000000000000000000000000000000000',
... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000',
... makerAssetData=asset_data_utils.encode_erc20(zrx_address), ... 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), ... takerAssetData=asset_data_utils.encode_erc20(weth_address),
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
... salt=random.randint(1, 100000000000000000), ... salt=random.randint(1, 100000000000000000),
... makerFee=0, ... makerFee=0,
... takerFee=0, ... takerFee=0,
@ -279,10 +315,12 @@ is an example where the taker fills two orders in one transaction:
... (datetime.utcnow() + timedelta(days=1)).timestamp() ... (datetime.utcnow() + timedelta(days=1)).timestamp()
... ) ... )
... ) ... )
>>> signature_2 = sign_hash_to_bytes( >>> signature_2 = sign_hash(
... ganache, ... ganache,
... Web3.toChecksumAddress(maker_address), ... 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: Fill order_1 and order_2 together:
@ -308,7 +346,9 @@ will be consumed.
... senderAddress='0x0000000000000000000000000000000000000000', ... senderAddress='0x0000000000000000000000000000000000000000',
... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... feeRecipientAddress='0x0000000000000000000000000000000000000000',
... makerAssetData=asset_data_utils.encode_erc20(weth_address), ... 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), ... takerAssetData=asset_data_utils.encode_erc20(weth_address),
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
... salt=random.randint(1, 100000000000000000), ... salt=random.randint(1, 100000000000000000),
... makerFee=0, ... makerFee=0,
... takerFee=0, ... takerFee=0,
@ -320,7 +360,7 @@ will be consumed.
... ), ... ),
... tx_params=TxParams(from_=maker_address), ... tx_params=TxParams(from_=maker_address),
... ) ... )
73... 74...
""" """
from .tx_params import TxParams from .tx_params import TxParams

View File

@ -1,6 +1,6 @@
"""Base wrapper class for accessing ethereum smart contracts.""" """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 eth_utils import is_address, to_checksum_address
from web3 import Web3 from web3 import Web3
@ -12,7 +12,11 @@ from .tx_params import TxParams
class Validator: class Validator:
"""Base class for validating inputs to methods.""" """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.""" """Initialize the instance."""
def assert_valid( def assert_valid(
@ -32,7 +36,7 @@ class ContractMethod:
def __init__( def __init__(
self, self,
provider: BaseProvider, web3_or_provider: Union[Web3, BaseProvider],
contract_address: str, contract_address: str,
validator: Validator = None, validator: Validator = None,
): ):
@ -42,9 +46,20 @@ class ContractMethod:
:param contract_address: Where the contract has been deployed to. :param contract_address: Where the contract has been deployed to.
:param validator: Used to validate method inputs. :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: if validator is None:
validator = Validator(provider, contract_address) validator = Validator(web3_or_provider, contract_address)
self.validator = validator self.validator = validator
@staticmethod @staticmethod
@ -59,8 +74,13 @@ class ContractMethod:
if not tx_params: if not tx_params:
tx_params = TxParams() tx_params = TxParams()
if not tx_params.from_: if not tx_params.from_:
tx_params.from_ = ( tx_params.from_ = self._web3_eth.defaultAccount or (
self._web3_eth.defaultAccount or self._web3_eth.accounts[0] 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 return tx_params

View File

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

View File

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

View File

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

View File

@ -11,17 +11,13 @@ Converting between the JSON wire format and the types accepted by Web3.py (eg
converting Exchange structs between JSON and Python objects. converting Exchange structs between JSON and Python objects.
""" """
from copy import copy from enum import auto, Enum
from typing import cast, Dict
from eth_utils import remove_0x_prefix
from zero_ex.json_schemas import assert_valid
from . import ( from . import (
Tuple0xbb41e5b3, Tuple0x735c43e3,
Tuple0x260219a2, Tuple0x6ca34a6f,
Tuple0x054ca44e, Tuple0x4c5ca29b,
Tuple0xdabc15fe,
Tuple0xb1e4a1ae, Tuple0xb1e4a1ae,
) )
@ -33,27 +29,35 @@ from . import (
# of each of these classes. # of each of these classes.
class FillResults(Tuple0xbb41e5b3): class FillResults(Tuple0x735c43e3):
"""The `FillResults`:code: Solidity struct. """The `FillResults`:code: Solidity struct.
Also known as 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. """The `Order`:code: Solidity struct.
Also known as 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. """The `MatchedFillResults`:code: Solidity struct.
Also known as 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:.
""" """
@ -65,136 +69,11 @@ class OrderInfo(Tuple0xb1e4a1ae):
""" """
def order_to_jsdict( class OrderStatus(Enum): # noqa: D101 # pylint: disable=missing-docstring
order: Order, INVALID = 0
exchange_address="0x0000000000000000000000000000000000000000", INVALID_MAKER_ASSET_AMOUNT = auto()
signature: str = None, INVALID_TAKER_ASSET_AMOUNT = auto()
) -> dict: FILLABLE = auto()
"""Convert a Web3-compatible order struct to a JSON-schema-compatible dict. EXPIRED = auto()
FULLY_FILLED = auto()
More specifically, do explicit decoding for the `bytes`:code: fields, and CANCELLED = auto()
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

View File

@ -1,22 +1,40 @@
"""Validate inputs to the Exchange contract.""" """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 from web3.providers.base import BaseProvider
from zero_ex import json_schemas from zero_ex import json_schemas
from zero_ex.contract_wrappers.order_conversions import order_to_jsdict
from ..bases import Validator from ..bases import Validator
from .types import order_to_jsdict
class ExchangeValidator(Validator): class ExchangeValidator(Validator):
"""Validate inputs to Exchange methods.""" """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.""" """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.contract_address = contract_address
self.chain_id = web3.eth.chainId
def assert_valid( def assert_valid(
self, method_name: str, parameter_name: str, argument_value: Any self, method_name: str, parameter_name: str, argument_value: Any
@ -30,13 +48,17 @@ class ExchangeValidator(Validator):
""" """
if parameter_name == "order": if parameter_name == "order":
json_schemas.assert_valid( json_schemas.assert_valid(
order_to_jsdict(argument_value, self.contract_address), order_to_jsdict(
argument_value, self.chain_id, self.contract_address
),
"/orderSchema", "/orderSchema",
) )
if parameter_name == "orders": if parameter_name == "orders":
for order in argument_value: for order in argument_value:
json_schemas.assert_valid( json_schemas.assert_valid(
order_to_jsdict(order, self.contract_address), order_to_jsdict(
order, self.chain_id, self.contract_address
),
"/orderSchema", "/orderSchema",
) )

View File

@ -0,0 +1,180 @@
"""Utilities to convert between JSON and Python-native objects."""
from copy import copy
from typing import cast, Dict, Union
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,
chain_id: int,
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,
... 'makerFeeAssetData': (0).to_bytes(1, byteorder='big') * 20,
... 'takerFeeAssetData': (0).to_bytes(1, byteorder='big') * 20,
... },
... chain_id=50
... ))
{'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',
'takerFeeAssetData': '0x0000000000000000000000000000000000000000'}
"""
jsdict = cast(Dict, copy(order))
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
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"])
jsdict["chainId"] = chain_id
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",
... 'makerFeeAssetData': "0x0000000000000000000000000000000000000000",
... 'takerFeeAssetData': "0x0000000000000000000000000000000000000000",
... 'exchangeAddress': "0x0000000000000000000000000000000000000000",
... 'chainId': 50
... },
... ))
{'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,
'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")
order = cast(Order, copy(jsdict))
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"])
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

View File

@ -23,7 +23,7 @@ class TxParams:
gas: Optional[int] = attr.ib( gas: Optional[int] = attr.ib(
default=None, converter=attr.converters.optional(int) 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) default=None, converter=attr.converters.optional(int)
) )
nonce: Optional[int] = attr.ib( nonce: Optional[int] = attr.ib(
@ -36,4 +36,7 @@ class TxParams:
if "from_" in res: if "from_" in res:
res["from"] = res["from_"] res["from"] = res["from_"]
del res["from_"] del res["from_"]
if "gas_price" in res:
res["gasPrice"] = res["gas_price"]
del res["gas_price"]
return res return res

View File

@ -26,16 +26,24 @@ class Web3:
class middleware_stack: class middleware_stack:
@staticmethod @staticmethod
def get(key: str) -> Callable: ... def get(key: str) -> Callable: ...
def inject(
self, middleware_func: object, layer: object
) -> None: ...
... ...
middleware_onion: middleware_stack
class net: class net:
version: str version: str
... ...
class eth: class Eth:
defaultAccount: str defaultAccount: str
accounts: List[str] accounts: List[str]
chainId: int
... ...
class account: class account:
@ -53,4 +61,7 @@ class Web3:
@staticmethod @staticmethod
def isAddress(address: str) -> bool: ... def isAddress(address: str) -> bool: ...
... ...
eth: Eth
... ...

View File

@ -5,7 +5,7 @@ from eth_utils import to_checksum_address
from web3 import Web3 from web3 import Web3
from zero_ex.order_utils import asset_data_utils from zero_ex.order_utils import asset_data_utils
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 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") @pytest.fixture(scope="module")
def erc20_proxy_address(): def erc20_proxy_address():
"""Get the 0x 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") @pytest.fixture(scope="module")
def weth_asset_data(): # pylint: disable=redefined-outer-name def weth_asset_data(): # pylint: disable=redefined-outer-name
"""Get 0x asset data for Wrapped Ether (WETH) token.""" """Get 0x asset data for Wrapped Ether (WETH) token."""
return asset_data_utils.encode_erc20( 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.""" """Get an instance of the WrapperEther contract."""
return web3_eth.contract( return web3_eth.contract(
address=to_checksum_address( address=to_checksum_address(
NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token network_to_addresses(NetworkId.GANACHE).ether_token
), ),
abi=abi_by_name("WETH9"), abi=abi_by_name("WETH9"),
) )
@ -61,7 +61,7 @@ def weth_instance(web3_eth): # pylint: disable=redefined-outer-name
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def zrx_address(): def zrx_address():
"""Get address of ZRX token for Ganache network.""" """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") @pytest.fixture(scope="module")

View File

@ -2,7 +2,7 @@
import pytest 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 from zero_ex.contract_wrappers.bases import ContractMethod
@ -10,6 +10,6 @@ from zero_ex.contract_wrappers.bases import ContractMethod
def contract_wrapper(ganache_provider): def contract_wrapper(ganache_provider):
"""Get a ContractMethod instance for testing.""" """Get a ContractMethod instance for testing."""
return ContractMethod( return ContractMethod(
provider=ganache_provider, web3_or_provider=ganache_provider,
contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, contract_address=network_to_addresses(NetworkId.GANACHE).ether_token,
) )

View File

@ -4,7 +4,7 @@ from decimal import Decimal
import pytest 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 import TxParams
from zero_ex.contract_wrappers.erc20_token import ERC20Token from zero_ex.contract_wrappers.erc20_token import ERC20Token
@ -16,7 +16,7 @@ MAX_ALLOWANCE = int("{:.0f}".format(Decimal(2) ** 256 - 1))
def erc20_wrapper(ganache_provider): def erc20_wrapper(ganache_provider):
"""Get an instance of ERC20Token wrapper class for testing.""" """Get an instance of ERC20Token wrapper class for testing."""
return ERC20Token( return ERC20Token(
ganache_provider, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token ganache_provider, network_to_addresses(NetworkId.GANACHE).ether_token
) )

View File

@ -5,20 +5,24 @@ import random
import pytest import pytest
from eth_utils import remove_0x_prefix 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 import TxParams
from zero_ex.contract_wrappers.exchange import Exchange from zero_ex.contract_wrappers.exchange import Exchange
from zero_ex.contract_wrappers.exchange.types import Order from zero_ex.contract_wrappers.exchange.types import Order
from zero_ex.json_schemas import assert_valid 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") @pytest.fixture(scope="module")
def exchange_wrapper(ganache_provider): def exchange_wrapper(ganache_provider):
"""Get an Exchange wrapper instance.""" """Get an Exchange wrapper instance."""
return Exchange( return Exchange(
provider=ganache_provider, web3_or_provider=ganache_provider,
contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, contract_address=network_to_addresses(NetworkId.GANACHE).exchange,
) )
@ -43,6 +47,8 @@ def create_test_order(
salt=random.randint(1, 1000000000), salt=random.randint(1, 1000000000),
makerAssetData=maker_asset_data, makerAssetData=maker_asset_data,
takerAssetData=taker_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 return order
@ -67,16 +73,29 @@ def test_exchange_wrapper__fill_order(
exchange_wrapper, # pylint: disable=redefined-outer-name exchange_wrapper, # pylint: disable=redefined-outer-name
ganache_provider, ganache_provider,
weth_asset_data, weth_asset_data,
zrx_asset_data,
): ):
"""Test filling an order.""" """Test filling an order."""
taker = accounts[0] taker = accounts[0]
maker = accounts[1] maker = accounts[1]
exchange_address = exchange_wrapper.contract_address 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_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( tx_hash = exchange_wrapper.fill_order.send_transaction(
order=order, order=order,
@ -107,11 +126,13 @@ def test_exchange_wrapper__batch_fill_orders(
orders.append(order_1) orders.append(order_1)
orders.append(order_2) orders.append(order_2)
order_hashes = [ 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 for order in orders
] ]
order_signatures = [ order_signatures = [
sign_hash_to_bytes(ganache_provider, maker, order_hash) sign_hash(ganache_provider, maker, order_hash)
for order_hash in order_hashes for order_hash in order_hashes
] ]
taker_amounts = [order["takerAssetAmount"] for order in orders] taker_amounts = [order["takerAssetAmount"] for order in orders]
@ -128,3 +149,20 @@ def test_exchange_wrapper__batch_fill_orders(
assert_fill_log( assert_fill_log(
fill_events[index].args, maker, taker, order, order_hashes[index] 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
)

View File

@ -20,6 +20,7 @@ commands =
pytest test pytest test
[testenv:run_tests_against_deployment] [testenv:run_tests_against_deployment]
setenv = PY_IGNORE_IMPORTMISMATCH = 1
commands = commands =
pip install 0x-contract-wrappers pip install 0x-contract-wrappers[dev]
pytest test pytest --doctest-modules src test

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
## 1.1.1 - TBD
- Removed dev dependency on package `0x-contract-wrappers`
## 1.1.0 - 2019-08-09 ## 1.1.0 - 2019-08-09
- Added `verifyingContractAddress` to `/zeroExTransactionSchema`. - Added `verifyingContractAddress` to `/zeroExTransactionSchema`.

View File

@ -2,12 +2,16 @@
"""setuptools module for json_schemas package.""" """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 import distutils.command.build_py
from distutils.command.clean import clean from distutils.command.clean import clean
import subprocess # nosec import subprocess # nosec
from shutil import copytree, rmtree from shutil import copytree, rmtree
from os import environ, path 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 import find_packages, setup
from setuptools.command.test import test as TestCommand from setuptools.command.test import test as TestCommand
@ -139,7 +143,7 @@ with open("README.md", "r") as file_handle:
setup( setup(
name="0x-json-schemas", name="0x-json-schemas",
version="1.1.0", version="1.1.1",
description="JSON schemas for 0x applications", description="JSON schemas for 0x applications",
long_description=README_MD, long_description=README_MD,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
@ -162,11 +166,11 @@ setup(
extras_require={ extras_require={
"dev": [ "dev": [
"0x-contract-addresses", "0x-contract-addresses",
"0x-contract-wrappers",
"bandit", "bandit",
"black", "black",
"coverage", "coverage",
"coveralls", "coveralls",
"eth_utils",
"mypy", "mypy",
"mypy_extensions", "mypy_extensions",
"pycodestyle", "pycodestyle",

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -60,36 +60,38 @@ def assert_valid(data: Mapping, schema_id: str) -> None:
Raises an exception if validation fails. Raises an exception if validation fails.
>>> from zero_ex.json_schemas import assert_valid >>> 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.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 >>> from eth_utils import remove_0x_prefix
>>> import random >>> import random
>>> from datetime import datetime, timedelta >>> from datetime import datetime, timedelta
>>> example_order = Order( >>> assert_valid(
... makerAddress='0x5409ed021d9299bf6814279a6a1411a7e866a631', ... {'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
... takerAddress='0x0000000000000000000000000000000000000000', ... 'takerAddress': '0x0000000000000000000000000000000000000000',
... senderAddress='0x0000000000000000000000000000000000000000', ... 'senderAddress': '0x0000000000000000000000000000000000000000',
... exchangeAddress='0x4f833a24e1f95d70f028921e27040ca56e09ab0b', ... 'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
... feeRecipientAddress='0x0000000000000000000000000000000000000000', ... 'feeRecipientAddress': (
... makerAssetData=asset_data_utils.encode_erc20( ... '0x0000000000000000000000000000000000000000'
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token ... ),
... ), ... 'makerAssetData': (
... takerAssetData=asset_data_utils.encode_erc20( ... network_to_addresses(NetworkId.MAINNET).zrx_token
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token ... ),
... ), ... 'takerAssetData': (
... salt=random.randint(1, 100000000000000000), ... network_to_addresses(NetworkId.MAINNET).ether_token
... makerFee=0, ... ),
... takerFee=0, ... 'salt': random.randint(1, 100000000000000000),
... makerAssetAmount=1000000000000000000, ... 'makerFee': 0,
... takerAssetAmount=500000000000000000000, ... 'makerFeeAssetData': '0x' + '00'*20,
... expirationTimeSeconds=round( ... 'takerFee': 0,
... (datetime.utcnow() + timedelta(days=1)).timestamp() ... '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) _, schema = _LOCAL_RESOLVER.resolve(schema_id)
jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER) jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER)

View File

@ -15,11 +15,14 @@ EMPTY_ORDER = {
"takerAssetData": NULL_ADDRESS, "takerAssetData": NULL_ADDRESS,
"salt": "0", "salt": "0",
"makerFee": "0", "makerFee": "0",
"makerFeeAssetData": NULL_ADDRESS,
"takerFee": "0", "takerFee": "0",
"takerFeeAssetData": NULL_ADDRESS,
"makerAssetAmount": "0", "makerAssetAmount": "0",
"takerAssetAmount": "0", "takerAssetAmount": "0",
"expirationTimeSeconds": "0", "expirationTimeSeconds": "0",
"exchangeAddress": NULL_ADDRESS, "exchangeAddress": NULL_ADDRESS,
"chainId": 50,
} }

View File

@ -20,6 +20,7 @@ commands =
pytest test pytest test
[testenv:run_tests_against_deployment] [testenv:run_tests_against_deployment]
setenv = PY_IGNORE_IMPORTMISMATCH = 1
commands = commands =
pip install 0x-json-schemas pip install 0x-json-schemas[dev]
pytest test pytest --doctest-modules src test

View File

@ -1,5 +1,5 @@
# Changelog # Changelog
## 1.0.0 - 2019-04-30 ## 1.0.0 - TBD
- Initial release. - Initial release.

View File

@ -2,10 +2,14 @@
"""setuptools module for middlewares package.""" """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 import subprocess # nosec
from shutil import rmtree from shutil import rmtree
from os import environ, path from os import environ, path
from sys import argv from sys import argv, exit # pylint: disable=redefined-builtin
from distutils.command.clean import clean from distutils.command.clean import clean
import distutils.command.build_py import distutils.command.build_py

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -3,25 +3,21 @@
from eth_utils import to_checksum_address from eth_utils import to_checksum_address
from web3 import Web3, HTTPProvider from web3 import Web3, HTTPProvider
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId from zero_ex.contract_addresses import network_to_addresses, NetworkId
from zero_ex.middlewares.local_message_signer import ( from zero_ex.middlewares.local_message_signer import (
construct_local_message_signer, construct_local_message_signer,
) )
from zero_ex.order_utils import ( from zero_ex.order_utils import generate_order_hash_hex, sign_hash
generate_order_hash_hex,
is_valid_signature,
sign_hash,
)
def test_local_message_signer__sign_order(): def test_local_message_signer__sign_order():
"""Test signing order with the local_message_signer middleware""" """Test signing order with the local_message_signer middleware"""
expected_signature = ( expected_signature = (
"0x1cd17d75b891accf16030c572a64cf9e7955de63bcafa5b084439cec630ade2d7" "0x1c8bdfbb3ce3ed0f38c5a358a7f49ad5f21ea9857224c2fe98c458f2fa25551d4"
"c00f47a2f4d5b6a4508267bf4b8527100bd97cf1af9984c0a58e42d25b13f4f0a03" "d6db0157d9dfe9f9fadb8dedabb7786352843357f4ec8d0fbcbeeb619b1091f5803"
) )
address = "0x5409ED021D9299bf6814279A6A1411A7e866A631" address = "0x5409ED021D9299bf6814279A6A1411A7e866A631"
exchange = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange exchange = network_to_addresses(NetworkId.GANACHE).exchange
private_key = ( private_key = (
"f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d" "f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d"
) )
@ -36,7 +32,9 @@ def test_local_message_signer__sign_order():
"senderAddress": "0x0000000000000000000000000000000000000000", "senderAddress": "0x0000000000000000000000000000000000000000",
"feeRecipientAddress": "0x0000000000000000000000000000000000000000", "feeRecipientAddress": "0x0000000000000000000000000000000000000000",
"makerAssetData": (b"\x00") * 20, "makerAssetData": (b"\x00") * 20,
"makerFeeAssetData": (b"\x00") * 20,
"takerAssetData": (b"\x00") * 20, "takerAssetData": (b"\x00") * 20,
"takerFeeAssetData": (b"\x00") * 20,
"salt": 0, "salt": 0,
"makerFee": 0, "makerFee": 0,
"takerFee": 0, "takerFee": 0,
@ -44,8 +42,11 @@ def test_local_message_signer__sign_order():
"takerAssetAmount": 0, "takerAssetAmount": 0,
"expirationTimeSeconds": 0, "expirationTimeSeconds": 0,
} }
order_hash = generate_order_hash_hex(order, exchange) assert (
signature = sign_hash(ganache, to_checksum_address(address), order_hash) sign_hash(
assert signature == expected_signature web3_instance,
is_valid = is_valid_signature(ganache, order_hash, signature, address)[0] to_checksum_address(address),
assert is_valid is True generate_order_hash_hex(order, exchange, chain_id=1337),
)
== expected_signature
)

View File

@ -1,5 +1,11 @@
# Changelog # 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 ## 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. - 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.

View File

@ -2,11 +2,15 @@
"""setuptools module for order_utils package.""" """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 import subprocess # nosec
from shutil import rmtree from shutil import rmtree
from os import environ, path from os import environ, path
from pathlib import Path from pathlib import Path
from sys import argv from sys import argv, exit # pylint: disable=redefined-builtin
from distutils.command.clean import clean from distutils.command.clean import clean
import distutils.command.build_py import distutils.command.build_py
@ -152,7 +156,7 @@ with open("README.md", "r") as file_handle:
setup( setup(
name="0x-order-utils", name="0x-order-utils",
version="3.0.1", version="4.0.0",
description="Order utilities for 0x applications", description="Order utilities for 0x applications",
long_description=README_MD, long_description=README_MD,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -18,9 +18,9 @@ just this purpose. To start it:
from enum import auto, Enum from enum import auto, Enum
import json import json
from typing import Tuple from typing import cast, Tuple, Union
from pkg_resources import resource_string
from pkg_resources import resource_string
from mypy_extensions import TypedDict from mypy_extensions import TypedDict
from eth_utils import keccak, remove_0x_prefix, to_bytes, to_checksum_address from eth_utils import keccak, remove_0x_prefix, to_bytes, to_checksum_address
@ -29,9 +29,11 @@ import web3.exceptions
from web3.providers.base import BaseProvider from web3.providers.base import BaseProvider
from web3.contract import Contract 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 import zero_ex.contract_artifacts
from zero_ex.contract_wrappers.exchange.types import Order, order_to_jsdict 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 ( from zero_ex.dev_utils.type_assertions import (
assert_is_address, assert_is_address,
assert_is_hex_string, assert_is_hex_string,
@ -48,13 +50,18 @@ class _Constants:
eip191_header = b"\x19\x01" eip191_header = b"\x19\x01"
eip712_domain_separator_schema_hash = keccak( 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_struct_header = (
eip712_domain_separator_schema_hash eip712_domain_separator_schema_hash
+ keccak(b"0x Protocol") + keccak(b"0x Protocol")
+ keccak(b"2") + keccak(b"3.0.0")
) )
eip712_order_schema_hash = keccak( eip712_order_schema_hash = keccak(
@ -70,7 +77,9 @@ class _Constants:
+ b"uint256 expirationTimeSeconds," + b"uint256 expirationTimeSeconds,"
+ b"uint256 salt," + b"uint256 salt,"
+ b"bytes makerAssetData," + b"bytes makerAssetData,"
+ b"bytes takerAssetData" + b"bytes takerAssetData,"
+ b"bytes makerFeeAssetData,"
+ b"bytes takerFeeAssetData"
+ b")" + b")"
) )
@ -87,7 +96,9 @@ class _Constants:
N_SIGNATURE_TYPES = auto() 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. """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 <https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schema.json>`_. :param order: The order to be hashed. Must conform to `the 0x order JSON schema <https://github.com/0xProject/0x-monorepo/blob/development/packages/json-schemas/schemas/order_schema.json>`_.
@ -95,27 +106,35 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
contract has been deployed. contract has been deployed.
:returns: A string, of ASCII hex digits, representing the order hash. :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( >>> generate_order_hash_hex(
... Order( ... Order(
... makerAddress="0x0000000000000000000000000000000000000000", ... makerAddress="0x0000000000000000000000000000000000000000",
... takerAddress="0x0000000000000000000000000000000000000000", ... takerAddress="0x0000000000000000000000000000000000000000",
... feeRecipientAddress="0x0000000000000000000000000000000000000000", ... feeRecipientAddress="0x0000000000000000000000000000000000000000",
... senderAddress="0x0000000000000000000000000000000000000000", ... senderAddress="0x0000000000000000000000000000000000000000",
... makerAssetAmount="1000000000000000000", ... makerAssetAmount="0",
... takerAssetAmount="1000000000000000000", ... takerAssetAmount="0",
... makerFee="0", ... makerFee="0",
... takerFee="0", ... takerFee="0",
... expirationTimeSeconds="12345", ... expirationTimeSeconds="0",
... salt="12345", ... salt="0",
... makerAssetData=((0).to_bytes(1, byteorder='big') * 20), ... makerAssetData=((0).to_bytes(1, byteorder='big') * 20),
... takerAssetData=((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) """ # noqa: E501 (line too long)
assert_is_address(exchange_address, "exchange_address") 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): def pad_20_bytes_to_32(twenty_bytes: bytes):
return bytes(12) + twenty_bytes return bytes(12) + twenty_bytes
@ -125,9 +144,17 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
eip712_domain_struct_hash = keccak( eip712_domain_struct_hash = keccak(
_Constants.eip712_domain_struct_header _Constants.eip712_domain_struct_header
+ int_to_32_big_endian_bytes(int(chain_id))
+ pad_20_bytes_to_32(to_bytes(hexstr=exchange_address)) + 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( eip712_order_struct_hash = keccak(
_Constants.eip712_order_schema_hash _Constants.eip712_order_schema_hash
+ pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"])) + pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"]))
@ -140,8 +167,10 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
+ int_to_32_big_endian_bytes(int(order["takerFee"])) + 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["expirationTimeSeconds"]))
+ int_to_32_big_endian_bytes(int(order["salt"])) + int_to_32_big_endian_bytes(int(order["salt"]))
+ keccak(to_bytes(hexstr=order["makerAssetData"].hex())) + keccak(ensure_bytes(order["makerAssetData"]))
+ keccak(to_bytes(hexstr=order["takerAssetData"].hex())) + keccak(ensure_bytes(order["takerAssetData"]))
+ keccak(ensure_bytes(order["makerFeeAssetData"]))
+ keccak(ensure_bytes(order["takerFeeAssetData"]))
) )
return keccak( return keccak(
@ -153,7 +182,7 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
def is_valid_signature( def is_valid_signature(
provider: BaseProvider, data: str, signature: str, signer_address: str provider: BaseProvider, data: str, signature: str, signer_address: str
) -> Tuple[bool, str]: ) -> bool:
"""Check the validity of the supplied signature. """Check the validity of the supplied signature.
Check if the supplied `signature`:code: corresponds to signing `data`:code: Check if the supplied `signature`:code: corresponds to signing `data`:code:
@ -173,42 +202,25 @@ def is_valid_signature(
... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403', ... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403',
... '0x5409ed021d9299bf6814279a6a1411a7e866a631', ... '0x5409ed021d9299bf6814279a6a1411a7e866a631',
... ) ... )
(True, '') True
""" # noqa: E501 (line too long) """ # noqa: E501 (line too long)
assert_is_provider(provider, "provider") assert_is_provider(provider, "provider")
assert_is_hex_string(data, "data") assert_is_hex_string(data, "data")
assert_is_hex_string(signature, "signature") assert_is_hex_string(signature, "signature")
assert_is_address(signer_address, "signer_address") assert_is_address(signer_address, "signer_address")
web3_instance = Web3(provider) return Exchange(
# false positive from pylint: disable=no-member provider,
contract_address = NETWORK_TO_ADDRESSES[ network_to_addresses(
NetworkId(int(web3_instance.net.version)) NetworkId(
].exchange int(Web3(provider).net.version) # pylint: disable=no-member
# false positive from pylint: disable=no-member )
contract: Contract = web3_instance.eth.contract( ).exchange,
address=to_checksum_address(contract_address), ).is_valid_hash_signature.call(
abi=zero_ex.contract_artifacts.abi_by_name("Exchange"), 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): class ECSignature(TypedDict):
@ -272,11 +284,14 @@ def _convert_ec_signature_to_vrs_hex(signature: ECSignature) -> str:
def sign_hash( def sign_hash(
provider: BaseProvider, signer_address: str, hash_hex: str web3_or_provider: Union[Web3, BaseProvider],
signer_address: str,
hash_hex: str,
) -> str: ) -> str:
"""Sign a message with the given hash, and return the signature. """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 signer_address: The address of the signing account.
:param hash_hex: A hex string representing the hash, like that returned :param hash_hex: A hex string representing the hash, like that returned
from `generate_order_hash_hex()`:code:. from `generate_order_hash_hex()`:code:.
@ -290,11 +305,20 @@ def sign_hash(
... ) ... )
'0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03' '0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03'
""" # noqa: E501 (line too long) """ # 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_address(signer_address, "signer_address")
assert_is_hex_string(hash_hex, "hash_hex") assert_is_hex_string(hash_hex, "hash_hex")
web3_instance = Web3(provider)
# false positive from pylint: disable=no-member # false positive from pylint: disable=no-member
signature = web3_instance.eth.sign( # type: ignore signature = web3_instance.eth.sign( # type: ignore
signer_address, hexstr=hash_hex.replace("0x", "") signer_address, hexstr=hash_hex.replace("0x", "")
@ -319,8 +343,11 @@ def sign_hash(
).hex() ).hex()
) )
(valid, _) = is_valid_signature( 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: if valid is True:
@ -334,21 +361,26 @@ def sign_hash(
1, byteorder="big" 1, byteorder="big"
).hex() ).hex()
) )
(valid, _) = is_valid_signature( 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: if valid is True:
return signature_as_vrst_hex return signature_as_vrst_hex
raise RuntimeError( raise RuntimeError(
"Signature returned from web3 provider is in an unknown format." "Signature returned from web3 provider is in an unknown format. "
+ " Attempted to parse as RSV and as VRS." + "Signature was: {signature}"
) )
def sign_hash_to_bytes( 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: ) -> bytes:
"""Sign a message with the given hash, and return the signature. """Sign a message with the given hash, and return the signature.
@ -361,5 +393,5 @@ def sign_hash_to_bytes(
'1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03' '1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03'
""" # noqa: E501 (line too long) """ # noqa: E501 (line too long)
return remove_0x_prefix( return remove_0x_prefix(
sign_hash(provider, signer_address, hash_hex) sign_hash(web3_or_provider, signer_address, hash_hex)
).encode(encoding="utf_8") ).encode(encoding="utf_8")

View File

@ -34,4 +34,6 @@ class Web3:
... ...
... ...
provider: BaseProvider
... ...

View File

@ -6,7 +6,7 @@ from zero_ex.order_utils import generate_order_hash_hex
def test_get_order_hash_hex__empty_order(): def test_get_order_hash_hex__empty_order():
"""Test the hashing of an uninitialized order.""" """Test the hashing of an uninitialized order."""
expected_hash_hex = ( expected_hash_hex = (
"faa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422" "331cb7e07a757bae130702da6646c26531798c92bcfaf671817268fd2c188531"
) )
actual_hash_hex = generate_order_hash_hex( actual_hash_hex = generate_order_hash_hex(
{ {
@ -18,6 +18,8 @@ def test_get_order_hash_hex__empty_order():
), ),
"makerAssetData": (b"\x00") * 20, "makerAssetData": (b"\x00") * 20,
"takerAssetData": (b"\x00") * 20, "takerAssetData": (b"\x00") * 20,
"makerFeeAssetData": (b"\x00") * 20,
"takerFeeAssetData": (b"\x00") * 20,
"salt": 0, "salt": 0,
"makerFee": 0, "makerFee": 0,
"takerFee": 0, "takerFee": 0,
@ -25,6 +27,7 @@ def test_get_order_hash_hex__empty_order():
"takerAssetAmount": 0, "takerAssetAmount": 0,
"expirationTimeSeconds": 0, "expirationTimeSeconds": 0,
}, },
"0x0000000000000000000000000000000000000000", "0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
50,
) )
assert actual_hash_hex == expected_hash_hex assert actual_hash_hex == expected_hash_hex

View File

@ -3,6 +3,10 @@
import pytest import pytest
from web3 import Web3 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 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 To induce this error, the last byte of the signature is tweaked from 03 to
ff.""" ff."""
(is_valid, reason) = is_valid_signature( try:
Web3.HTTPProvider("http://127.0.0.1:8545"), is_valid_signature(
"0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0", Web3.HTTPProvider("http://127.0.0.1:8545"),
"0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc334" "0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222"
+ "0349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254ff", + "b0",
"0x5409ed021d9299bf6814279a6a1411a7e866a631", "0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351b"
) + "c3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace"
assert is_valid is False + "225401",
assert reason == "SIGNATURE_UNSUPPORTED" "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().""" """Test the happy path through sign_hash_to_bytes()."""
provider = Web3.HTTPProvider("http://127.0.0.1:8545") provider = Web3.HTTPProvider("http://127.0.0.1:8545")
signature = sign_hash_to_bytes(
provider, signing_address = Web3( # pylint: disable=no-member
Web3( # pylint: disable=no-member provider
provider ).geth.personal.listAccounts()[0]
).geth.personal.listAccounts()[0],
"0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004", order_hash_hex = (
"0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004"
) )
signature = sign_hash_to_bytes(provider, signing_address, order_hash_hex)
assert ( assert (
signature signature
== b"1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03" # noqa: E501 (line too long) == 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

View File

@ -20,6 +20,7 @@ commands =
pytest test pytest test
[testenv:run_tests_against_deployment] [testenv:run_tests_against_deployment]
setenv = PY_IGNORE_IMPORTMISMATCH = 1
commands = commands =
pip install 0x-order-utils pip install 0x-order-utils[dev]
pytest test pytest --doctest-modules src test

View File

@ -22,7 +22,7 @@ $ ./parallel pip uninstall $(basename $(pwd))
from concurrent.futures import ProcessPoolExecutor, wait from concurrent.futures import ProcessPoolExecutor, wait
from os import chdir from os import chdir
from subprocess import CalledProcessError, check_output from subprocess import CalledProcessError, check_output, STDOUT
from sys import argv from sys import argv
PACKAGES = [ PACKAGES = [
@ -38,11 +38,14 @@ PACKAGES = [
def run_cmd_on_package(package: str): def run_cmd_on_package(package: str):
"""cd to the package dir, ./setup.py lint, cd ..""" """cd to the package dir, ./setup.py lint, cd .."""
chdir(package) chdir(package)
command = f"{' '.join(argv[1:])}"
try: try:
check_output(f"{' '.join(argv[1:])}".split()) check_output(command.split(), stderr=STDOUT)
except CalledProcessError as error: except CalledProcessError as error:
print(f"standard output from command:\n{error.output.decode('utf-8')}") raise RuntimeError(
raise RuntimeError(f"Above exception raised in {package}, ") from error 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: finally:
chdir("..") chdir("..")

View File

@ -1,54 +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
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)
try:
check_output(f"{' '.join(argv[1:])}".split())
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
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

View File

@ -12,6 +12,7 @@ from sys import argv
PACKAGES = [ PACKAGES = [
"contract_wrappers", "contract_wrappers",
"contract_addresses",
"contract_artifacts", "contract_artifacts",
"json_schemas", "json_schemas",
] ]

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
## 4.0.0 - TBD
- Migrated from v2 to v3 of the 0x protocol.
## 3.0.0 - 2019-08-08 ## 3.0.0 - 2019-08-08
- Migrated from v4 to v5 of Web3.py. - Migrated from v4 to v5 of Web3.py.

View File

@ -3,10 +3,15 @@
"""setuptools module for sra_client package.""" """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 subprocess # nosec
import distutils.command.build_py import distutils.command.build_py
from distutils.command.clean import clean from distutils.command.clean import clean
from shutil import rmtree from shutil import rmtree
from sys import exit # pylint: disable=redefined-builtin
from urllib.request import urlopen from urllib.request import urlopen
from urllib.error import URLError from urllib.error import URLError
@ -14,7 +19,7 @@ from setuptools import setup, find_packages # noqa: H301
from setuptools.command.test import test as TestCommand from setuptools.command.test import test as TestCommand
NAME = "0x-sra-client" NAME = "0x-sra-client"
VERSION = "3.0.0" VERSION = "4.0.0"
# To install the library, run the following # To install the library, run the following
# #
# python setup.py install # python setup.py install
@ -41,6 +46,12 @@ class CleanCommandExtension(clean):
rmtree("0x_sra_client.egg-info", ignore_errors=True) rmtree("0x_sra_client.egg-info", ignore_errors=True)
rmtree("build", ignore_errors=True) rmtree("build", ignore_errors=True)
rmtree("dist", 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): class TestCommandExtension(TestCommand):
@ -85,12 +96,15 @@ class StartTestRelayerCommand(distutils.command.build_py.build_py):
("docker-compose -f test/relayer/docker-compose.yml up -d").split() ("docker-compose -f test/relayer/docker-compose.yml up -d").split()
) )
launch_kit_ready = False 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: while not launch_kit_ready:
try: try:
launch_kit_ready = ( launch_kit_ready = (
urlopen( # nosec urlopen( # nosec
"http://localhost:3000/v2/asset_pairs" "http://localhost:3000/v3/asset_pairs"
).getcode() ).getcode()
== 200 == 200
) )

View File

@ -1,2 +1,2 @@
"""0x Python API.""" """0x Python API."""
__import__("pkg_resources").declare_namespace(__name__) __import__("pkg_resources").declare_namespace(__name__) # type: ignore

View File

@ -19,7 +19,7 @@ Install the package with pip::
pip install 0x-sra-client pip install 0x-sra-client
To interact with a 0x Relayer, you need the HTTP endpoint of the Relayer you'd 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 For testing one can use the `0x-launch-kit-backend
<https://github.com/0xProject/0x-launch-kit-backend#table-of-contents/>`_ to host <https://github.com/0xProject/0x-launch-kit-backend#table-of-contents/>`_ to host
@ -83,8 +83,8 @@ for this account, so the example orders below have the maker trading away ZRX.
Before such an order can be valid, though, the maker must give the 0x contracts Before such an order can be valid, though, the maker must give the 0x contracts
permission to trade their ZRX tokens: permission to trade their ZRX tokens:
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES >>> from zero_ex.contract_addresses import network_to_addresses
>>> contract_addresses = NETWORK_TO_ADDRESSES[network_id] >>> contract_addresses = network_to_addresses(network_id)
>>> >>>
>>> from zero_ex.contract_artifacts import abi_by_name >>> from zero_ex.contract_artifacts import abi_by_name
>>> zrx_token_contract = Web3(eth_node).eth.contract( >>> zrx_token_contract = Web3(eth_node).eth.contract(
@ -105,7 +105,8 @@ Post Order
Post an order for our Maker to trade ZRX for WETH: 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 ( >>> from zero_ex.order_utils import (
... asset_data_utils, ... asset_data_utils,
... sign_hash) ... sign_hash)
@ -120,9 +121,11 @@ Post an order for our Maker to trade ZRX for WETH:
... makerAssetData=asset_data_utils.encode_erc20( ... makerAssetData=asset_data_utils.encode_erc20(
... contract_addresses.zrx_token ... contract_addresses.zrx_token
... ), ... ),
... makerFeeAssetData=asset_data_utils.encode_erc20('0x'+'00'*20),
... takerAssetData=asset_data_utils.encode_erc20( ... takerAssetData=asset_data_utils.encode_erc20(
... contract_addresses.ether_token ... contract_addresses.ether_token
... ), ... ),
... takerFeeAssetData=asset_data_utils.encode_erc20('0x'+'00'*20),
... salt=random.randint(1, 100000000000000000), ... salt=random.randint(1, 100000000000000000),
... makerFee=0, ... makerFee=0,
... takerFee=0, ... takerFee=0,
@ -135,7 +138,7 @@ Post an order for our Maker to trade ZRX for WETH:
>>> from zero_ex.order_utils import generate_order_hash_hex >>> from zero_ex.order_utils import generate_order_hash_hex
>>> order_hash_hex = 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( >>> relayer.post_order_with_http_info(
... network_id=network_id.value, ... network_id=network_id.value,
@ -144,7 +147,8 @@ Post an order for our Maker to trade ZRX for WETH:
... exchange_address=contract_addresses.exchange, ... exchange_address=contract_addresses.exchange,
... signature=sign_hash( ... signature=sign_hash(
... eth_node, Web3.toChecksumAddress(maker_address), order_hash_hex ... eth_node, Web3.toChecksumAddress(maker_address), order_hash_hex
... ) ... ),
... chain_id=Web3(eth_node).eth.chainId,
... ) ... )
... )[1] ... )[1]
200 200
@ -152,24 +156,35 @@ Post an order for our Maker to trade ZRX for WETH:
Get Order 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: Retrieve the order we just posted:
>>> relayer.get_order("0x" + order_hash_hex) >>> relayer.get_order("0x" + order_hash_hex)
{'meta_data': {}, {'meta_data': {'orderHash': '0x...',
'order': {'exchangeAddress': '0x...', 'remainingFillableTakerAssetAmount': '2'},
'order': {'chainId': 50,
'exchangeAddress': '0x...',
'expirationTimeSeconds': '...', 'expirationTimeSeconds': '...',
'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
'makerAddress': '0x...', 'makerAddress': '0x...',
'makerAssetAmount': '2', 'makerAssetAmount': '2',
'makerAssetData': '0xf47261b0000000000000000000000000...', 'makerAssetData': '0xf47261b0000000000000000000000000...',
'makerFee': '0', 'makerFee': '0',
'makerFeeAssetData': '0xf47261b0000000000000000000000000...',
'salt': '...', 'salt': '...',
'senderAddress': '0x0000000000000000000000000000000000000000', 'senderAddress': '0x0000000000000000000000000000000000000000',
'signature': '0x...', 'signature': '0x...',
'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000',
'takerAssetAmount': '2', 'takerAssetAmount': '2',
'takerAssetData': '0xf47261b0000000000000000000000000...', 'takerAssetData': '0xf47261b0000000000000000000000000...',
'takerFee': '0'}} 'takerFee': '0',
'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}
Get Orders Get Orders
----------- -----------
@ -178,21 +193,25 @@ Retrieve all of the Relayer's orders, a set which at this point consists solely
of the one we just posted: of the one we just posted:
>>> relayer.get_orders() >>> relayer.get_orders()
{'records': [{'meta_data': {}, {'records': [{'meta_data': {'orderHash': '0x...',
'order': {'exchangeAddress': '0x...', 'remainingFillableTakerAssetAmount': '2'},
'order': {'chainId': 50,
'exchangeAddress': '0x...',
'expirationTimeSeconds': '...', 'expirationTimeSeconds': '...',
'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
'makerAddress': '0x...', 'makerAddress': '0x...',
'makerAssetAmount': '2', 'makerAssetAmount': '2',
'makerAssetData': '0xf47261b000000000000000000000000...', 'makerAssetData': '0xf47261b000000000000000000000000...',
'makerFee': '0', 'makerFee': '0',
'makerFeeAssetData': '0xf47261b000000000000000000000000...',
'salt': '...', 'salt': '...',
'senderAddress': '0x0000000000000000000000000000000000000000', 'senderAddress': '0x0000000000000000000000000000000000000000',
'signature': '0x...', 'signature': '0x...',
'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000',
'takerAssetAmount': '2', 'takerAssetAmount': '2',
'takerAssetData': '0xf47261b0000000000000000000000000...', 'takerAssetData': '0xf47261b0000000000000000000000000...',
'takerFee': '0'}}]} 'takerFee': '0',
'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}...]}
Get Asset Pairs Get Asset Pairs
--------------- ---------------
@ -233,43 +252,50 @@ consists just of our order):
... ).hex(), ... ).hex(),
... ) ... )
>>> orderbook >>> orderbook
{'asks': {'records': []}, {'asks': {'records': [...]},
'bids': {'records': [{'meta_data': {}, 'bids': {'records': [{'meta_data': {'orderHash': '0x...',
'order': {'exchangeAddress': '0x...', 'remainingFillableTakerAssetAmount': '2'},
'order': {'chainId': 50,
'exchangeAddress': '0x...',
'expirationTimeSeconds': '...', 'expirationTimeSeconds': '...',
'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
'makerAddress': '0x...', 'makerAddress': '0x...',
'makerAssetAmount': '2', 'makerAssetAmount': '2',
'makerAssetData': '0xf47261b0000000000000000000000000...', 'makerAssetData': '0xf47261b0000000000000000000000000...',
'makerFee': '0', 'makerFee': '0',
'makerFeeAssetData': '0xf47261b0000000000000000000000000...',
'salt': '...', 'salt': '...',
'senderAddress': '0x0000000000000000000000000000000000000000', 'senderAddress': '0x0000000000000000000000000000000000000000',
'signature': '0x...', 'signature': '0x...',
'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000',
'takerAssetAmount': '2', 'takerAssetAmount': '2',
'takerAssetData': '0xf47261b0000000000000000000000000...', 'takerAssetData': '0xf47261b0000000000000000000000000...',
'takerFee': '0'}}]}} 'takerFee': '0',
'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}...]}}
Select an order from the orderbook 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) >>> order = jsdict_to_order(orderbook.bids.records[0].order)
>>> from pprint import pprint >>> from pprint import pprint
>>> pprint(order) >>> pprint(order)
{'expirationTimeSeconds': ..., {'chainId': 50,
'expirationTimeSeconds': ...,
'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
'makerAddress': '0x...', 'makerAddress': '0x...',
'makerAssetAmount': 2, 'makerAssetAmount': 2,
'makerAssetData': b... 'makerAssetData': b...
'makerFee': 0, 'makerFee': 0,
'makerFeeAssetData': b...
'salt': ..., 'salt': ...,
'senderAddress': '0x0000000000000000000000000000000000000000', 'senderAddress': '0x0000000000000000000000000000000000000000',
'signature': '0x...', 'signature': '0x...',
'takerAddress': '0x0000000000000000000000000000000000000000', 'takerAddress': '0x0000000000000000000000000000000000000000',
'takerAssetAmount': 2, 'takerAssetAmount': 2,
'takerAssetData': b... 'takerAssetData': b...,
'takerFee': 0} 'takerFee': 0,
'takerFeeAssetData': b...}
Filling or Cancelling an Order Filling or Cancelling an Order
------------------------------ ------------------------------
@ -319,8 +345,8 @@ book. Now let's have the taker fill it:
>>> from zero_ex.contract_wrappers.exchange import Exchange >>> from zero_ex.contract_wrappers.exchange import Exchange
>>> from zero_ex.order_utils import Order >>> from zero_ex.order_utils import Order
>>> exchange = Exchange( >>> exchange = Exchange(
... provider=eth_node, ... web3_or_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 (Due to `an Issue with the Launch Kit Backend
@ -331,7 +357,7 @@ checksum the address in the order before filling it.)
>>> exchange.fill_order.send_transaction( >>> exchange.fill_order.send_transaction(
... order=order, ... order=order,
... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill ... 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) ... tx_params=TxParams(from_=taker_address)
... ) ... )
HexBytes('0x...') HexBytes('0x...')

View File

@ -139,7 +139,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/asset_pairs", "/v3/asset_pairs",
"GET", "GET",
path_params, path_params,
query_params, query_params,
@ -250,7 +250,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/fee_recipients", "/v3/fee_recipients",
"GET", "GET",
path_params, path_params,
query_params, query_params,
@ -363,7 +363,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/order/{orderHash}", "/v3/order/{orderHash}",
"GET", "GET",
path_params, path_params,
query_params, query_params,
@ -497,7 +497,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/order_config", "/v3/order_config",
"POST", "POST",
path_params, path_params,
query_params, query_params,
@ -680,7 +680,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/orderbook", "/v3/orderbook",
"GET", "GET",
path_params, path_params,
query_params, query_params,
@ -718,50 +718,48 @@ class DefaultApi(object):
:param bool async_req: Whether request should be asynchronous. :param bool async_req: Whether request should be asynchronous.
:param str maker_asset_proxy_id: The maker :param str maker_asset_proxy_id: The maker
`asset proxy id `asset proxy id
<https://0x.org/docs/tools/0x.js#types-AssetProxyId>`__ <https://0x.org/docs/tools/0x.js#enumeration-assetproxyid>`__
(example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721).
:param str taker_asset_proxy_id: The taker asset :param str taker_asset_proxy_id: The taker asset
`asset proxy id `asset proxy id
<https://0x.org/docs/tools/0x.js#types-AssetProxyId>`__ <https://0x.org/docs/tools/0x.js#enumeration-assetproxyid>`__
(example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721).
:param str maker_asset_address: The contract address for the maker asset. :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 taker_asset_address: The contract address for the taker asset.
:param str exchange_address: Same as exchangeAddress in the :param str exchange_address: Contract address for the exchange
`0x Protocol v2 Specification contract.
<https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__
:param str sender_address: Same as senderAddress in the :param str sender_address: Same as senderAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str maker_asset_data: Same as makerAssetData in the :param str maker_asset_data: Same as makerAssetData in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str taker_asset_data: Same as takerAssetData in the :param str taker_asset_data: Same as takerAssetData in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str trader_asset_data: Same as traderAssetData in the [0x :param str trader_asset_data: Same as traderAssetData in the [0x
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str maker_address: Same as makerAddress in the :param str maker_address: Same as makerAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str taker_address: Same as takerAddress in the :param str taker_address: Same as takerAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str trader_address: Same as traderAddress in the :param str trader_address: Same as traderAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str fee_recipient_address: Same as feeRecipientAddress in the :param str fee_recipient_address: Same as feeRecipientAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param int network_id: The id of the Ethereum network :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 page: The number of the page to request in the collection.
:param int per_page: The number of records to return per page. :param int per_page: The number of records to return per page.
@ -795,50 +793,50 @@ class DefaultApi(object):
:param bool async_req: Whether request should be asynchronous. :param bool async_req: Whether request should be asynchronous.
:param str maker_asset_proxy_id: The maker :param str maker_asset_proxy_id: The maker
`asset proxy id `asset proxy id
<https://0x.org/docs/tools/0x.js#types-AssetProxyId>`__ <https://0x.org/docs/tools/0x.js#enumeration-assetproxyid>`__
(example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721).
:param str taker_asset_proxy_id: The taker asset :param str taker_asset_proxy_id: The taker asset
`asset proxy id `asset proxy id
<https://0x.org/docs/tools/0x.js#types-AssetProxyId>`__ <https://0x.org/docs/tools/0x.js#enumeration-assetproxyid>`__
(example: "0xf47261b0" for ERC20, "0x02571792" for ERC721). (example: "0xf47261b0" for ERC20, "0x02571792" for ERC721).
:param str maker_asset_address: The contract address for the maker asset. :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 taker_asset_address: The contract address for the taker asset.
:param str exchange_address: Same as exchangeAddress in the [0x :param str exchange_address: Same as exchangeAddress in the [0x
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str sender_address: Same as senderAddress in the :param str sender_address: Same as senderAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str maker_asset_data: Same as makerAssetData in the :param str maker_asset_data: Same as makerAssetData in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str taker_asset_data: Same as takerAssetData in the :param str taker_asset_data: Same as takerAssetData in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str trader_asset_data: Same as traderAssetData in the :param str trader_asset_data: Same as traderAssetData in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str maker_address: Same as makerAddress in the :param str maker_address: Same as makerAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str taker_address: Same as takerAddress in the :param str taker_address: Same as takerAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str trader_address: Same as traderAddress in the :param str trader_address: Same as traderAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param str fee_recipient_address: Same as feeRecipientAddress in the :param str fee_recipient_address: Same as feeRecipientAddress in the
`0x Protocol v2 Specification `0x Protocol v3 Specification
<https://github.com/0xProject/0x-protocol-specification/blob/ <https://github.com/0xProject/0x-protocol-specification/blob/
master/v2/v2-specification.md#order-message-format>`__ master/v3/v3-specification.md#order-message-format>`__
:param int network_id: The id of the Ethereum network :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 page: The number of the page to request in the collection.
:param int per_page: The number of records to return per page. :param int per_page: The number of records to return per page.
@ -965,7 +963,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/orders", "/v3/orders",
"GET", "GET",
path_params, path_params,
query_params, query_params,
@ -1077,7 +1075,7 @@ class DefaultApi(object):
auth_settings = [] auth_settings = []
return self.api_client.call_api( return self.api_client.call_api(
"/v2/order", "/v3/order",
"POST", "POST",
path_params, path_params,
query_params, query_params,

View File

@ -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' version: '3'
services: services:
ganache: ganache:
image: "0xorg/ganache-cli:2.2.2" image: "0xorg/ganache-cli:4.4.0-beta.1"
ports: ports:
- "8545:8545" - "8545:8545"
launchkit: environment:
image: "0xorg/launch-kit-backend:74bcc39" - VERSION=4.4.0-beta.1
- SNAPSHOT_NAME=0x_ganache_snapshot-v3-beta
mesh:
image: 0xorg/mesh:6.0.0-beta-0xv3
depends_on: depends_on:
- ganache - 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: ports:
- "3000:3000" - "3000:3000"
network_mode: "host" # to connect to ganache network_mode: "host" # to connect to ganache
@ -16,5 +38,9 @@ services:
- NETWORK_ID=50 - NETWORK_ID=50
- RPC_URL=http://localhost:8545 - RPC_URL=http://localhost:8545
- WHITELIST_ALL_TOKENS=True - WHITELIST_ALL_TOKENS=True
- FEE_RECIPIENT=0x0000000000000000000000000000000000000001
- MAKER_FEE_UNIT_AMOUNT=0
- TAKER_FEE_UNIT_AMOUNT=0
- MESH_ENDPOINT=ws://localhost:60557
command: | 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"

View File

@ -18,7 +18,7 @@ commands =
pytest test pytest test
[testenv:run_tests_against_deployment] [testenv:run_tests_against_deployment]
deps=pytest setenv = PY_IGNORE_IMPORTMISMATCH = 1
commands = commands =
pip install 0x-sra-client pip install 0x-sra-client[dev]
pytest test pytest --doctest-modules src test