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:
parent
cbe4c4fbf9
commit
e61f23d001
@ -192,14 +192,33 @@ jobs:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: nikolaik/python-nodejs:python3.7-nodejs8
|
||||
- image: 0xorg/ganache-cli:2.2.2
|
||||
- image: 0xorg/launch-kit-backend:74bcc39
|
||||
- image: 0xorg/ganache-cli:4.4.0-beta.1
|
||||
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
|
||||
WHITELIST_ALL_TOKENS: True
|
||||
FEE_RECIPIENT: '0x0000000000000000000000000000000000000001'
|
||||
MAKER_FEE_UNIT_AMOUNT: 0
|
||||
TAKER_FEE_UNIT_AMOUNT: 0
|
||||
MESH_ENDPOINT: 'ws://localhost:60557'
|
||||
command: |
|
||||
sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js"
|
||||
sh -c "waitForMesh () { sleep 5; }; waitForMesh && node_modules/.bin/forever ts/lib/index.js"
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@ -221,8 +240,14 @@ jobs:
|
||||
- run:
|
||||
command: |
|
||||
cd python-packages
|
||||
./parallel_without_sra_client coverage run setup.py test
|
||||
./parallel coverage run setup.py test
|
||||
./build_docs
|
||||
- run:
|
||||
command: |
|
||||
# copy generated wrappers into contract_wrappers/build,
|
||||
# JUST so CircleCI will persist them as build artifacts.
|
||||
cd python-packages/contract_wrappers/src/zero_ex
|
||||
for i in contract_wrappers/[^__]*/; do mkdir -p ../../build/$i; cp $i/__init__.py ../../build/$i; done
|
||||
- save_cache:
|
||||
key: coverage-python-contract-addresses-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@ -247,8 +272,6 @@ jobs:
|
||||
key: coverage-python-sra-client-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/python-packages/sra_client/.coverage
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/*/__init__.py
|
||||
- store_artifacts:
|
||||
path: ~/repo/python-packages/contract_addresses/build
|
||||
- store_artifacts:
|
||||
@ -426,12 +449,11 @@ workflows:
|
||||
- test-exchange-ganache-3.0
|
||||
- test-rest
|
||||
- static-tests
|
||||
# - test-python:
|
||||
# requires:
|
||||
# - build
|
||||
# - test-rest
|
||||
# - static-tests-python:
|
||||
# requires:
|
||||
# - test-python
|
||||
- test-python:
|
||||
requires:
|
||||
- build
|
||||
- static-tests-python:
|
||||
requires:
|
||||
- build
|
||||
# skip python tox run for now, as we don't yet have multiple test environments to support.
|
||||
# - test-rest-python
|
||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -130,6 +130,7 @@ contracts/erc1155/generated-wrappers/
|
||||
contracts/extensions/generated-wrappers/
|
||||
contracts/exchange-forwarder/generated-wrappers/
|
||||
contracts/dev-utils/generated-wrappers/
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dev_utils/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/asset_proxy_owner/__init__.py
|
||||
@ -138,6 +139,8 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/coordinator_regi
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc20_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dummy_erc721_token/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/dutch_auction/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_mintable/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc1155_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc20_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/erc721_token/__init__.py
|
||||
@ -148,6 +151,7 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__in
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py
|
||||
python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py
|
||||
|
||||
@ -164,10 +168,14 @@ __pycache__
|
||||
python-packages/*/src/*.egg-info
|
||||
python-packages/*/.coverage
|
||||
|
||||
# python keeps package-local copies of json schemas
|
||||
# python keeps package-local copies of json schemas and contract addresses
|
||||
python-packages/json_schemas/src/zero_ex/json_schemas/schemas
|
||||
python-packages/contract_addresses/src/zero_ex/contract_addresses/addresses.json
|
||||
|
||||
# Doc README copy
|
||||
packages/*/docs/README.md
|
||||
|
||||
.DS_Store
|
||||
|
||||
# the snapshot that gets built for migrations sure does have a ton of files
|
||||
packages/migrations/0x_ganache_snapshot*
|
||||
|
@ -1,4 +1,25 @@
|
||||
[
|
||||
{
|
||||
"version": "4.5.0-beta.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "In Python wrappers, accept string arguments to bytes parameters",
|
||||
"pr": 2284
|
||||
},
|
||||
{
|
||||
"note": "In Python wrappers, support module-local, Web3.py-compatible middleware",
|
||||
"pr": 2284
|
||||
},
|
||||
{
|
||||
"note": "In Python wrappers, allow contracts to be instantiated with EITHER a Web3.py BaseProvider OR a Web3 client object",
|
||||
"pr": 2284
|
||||
},
|
||||
{
|
||||
"note": "In Python wrappers, fix bug with casting some bytes objects using bytes.fromhex()",
|
||||
"pr": 2284
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "4.4.0-beta.0",
|
||||
"changes": [
|
||||
|
@ -115,7 +115,7 @@ export const utils = {
|
||||
{ regex: '^address$', pyType: 'str' },
|
||||
{ regex: '^bool$', pyType: 'bool' },
|
||||
{ regex: '^u?int\\d*$', pyType: 'int' },
|
||||
{ regex: '^bytes\\d*$', pyType: 'bytes' },
|
||||
{ regex: '^bytes\\d*$', pyType: 'Union[bytes, str]' },
|
||||
];
|
||||
for (const regexAndTxType of solTypeRegexToPyType) {
|
||||
const { regex, pyType } = regexAndTxType;
|
||||
|
@ -40,6 +40,12 @@ except ImportError:
|
||||
"""No-op input validator."""
|
||||
|
||||
|
||||
try:
|
||||
from .middleware import MIDDLEWARE # type: ignore
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
{{tupleDefinitions ABIString}}
|
||||
|
||||
{{#each methods}}
|
||||
@ -59,30 +65,57 @@ class {{contractName}}:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
validator: {{contractName}}Validator = None,
|
||||
):
|
||||
"""Get an instance of wrapper for smart contract.
|
||||
|
||||
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
||||
:param web3_or_provider: Either an instance of `web3.Web3`:code: or
|
||||
`web3.providers.base.BaseProvider`:code:
|
||||
:param contract_address: where the contract has been deployed
|
||||
:param validator: for validation of method inputs.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
self.contract_address = contract_address
|
||||
|
||||
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
|
||||
provider
|
||||
).eth
|
||||
web3 = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
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}}
|
||||
functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions
|
||||
|
||||
{{#each methods}}
|
||||
self.{{toPythonIdentifier this.languageSpecificName}} = {{toPythonClassname this.languageSpecificName}}Method(provider, contract_address, functions.{{this.name}}, validator)
|
||||
self.{{toPythonIdentifier this.languageSpecificName}} = {{toPythonClassname this.languageSpecificName}}Method(web3_or_provider, contract_address, functions.{{this.name}}, validator)
|
||||
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
@ -2,9 +2,9 @@
|
||||
class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod):
|
||||
"""Various interfaces to the {{this.name}} method."""
|
||||
|
||||
def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None):
|
||||
def __init__(self, web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator=None):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
{{#if inputs}}
|
||||
@ -21,13 +21,6 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod):
|
||||
{{else if (equal type 'uint256')}}
|
||||
# safeguard against fractional inputs
|
||||
{{toPythonIdentifier this.name}} = int({{toPythonIdentifier this.name}})
|
||||
{{else if (equal type 'bytes')}}
|
||||
{{toPythonIdentifier this.name}} = bytes.fromhex({{toPythonIdentifier this.name}}.decode("utf-8"))
|
||||
{{else if (equal type 'bytes[]')}}
|
||||
{{toPythonIdentifier this.name}} = [
|
||||
bytes.fromhex({{toPythonIdentifier this.name}}_element.decode("utf-8"))
|
||||
for {{toPythonIdentifier this.name}}_element in {{toPythonIdentifier this.name}}
|
||||
]
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
return ({{> params }})
|
||||
|
@ -40,6 +40,12 @@ except ImportError:
|
||||
"""No-op input validator."""
|
||||
|
||||
|
||||
try:
|
||||
from .middleware import MIDDLEWARE # type: ignore
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class Tuple0x246f9407(TypedDict):
|
||||
"""Python representation of a tuple or struct.
|
||||
|
||||
@ -94,11 +100,11 @@ class Tuple0xcf8ad995(TypedDict):
|
||||
accomplished via `str.encode("utf_8")`:code:
|
||||
"""
|
||||
|
||||
someBytes: bytes
|
||||
someBytes: Union[bytes, str]
|
||||
|
||||
anInteger: int
|
||||
|
||||
aDynamicArrayOfBytes: List[bytes]
|
||||
aDynamicArrayOfBytes: List[Union[bytes, str]]
|
||||
|
||||
aString: str
|
||||
|
||||
@ -142,7 +148,7 @@ class Tuple0xf95128ef(TypedDict):
|
||||
|
||||
foo: int
|
||||
|
||||
bar: bytes
|
||||
bar: Union[bytes, str]
|
||||
|
||||
car: str
|
||||
|
||||
@ -165,9 +171,9 @@ class Tuple0xa057bf41(TypedDict):
|
||||
|
||||
input: Tuple0xf95128ef
|
||||
|
||||
lorem: bytes
|
||||
lorem: Union[bytes, str]
|
||||
|
||||
ipsum: bytes
|
||||
ipsum: Union[bytes, str]
|
||||
|
||||
dolor: str
|
||||
|
||||
@ -177,13 +183,13 @@ class SimpleRequireMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> None:
|
||||
@ -217,27 +223,26 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, a: List[bytes]):
|
||||
def validate_and_normalize_inputs(self, a: List[Union[bytes, str]]):
|
||||
"""Validate the inputs to the acceptsAnArrayOfBytes method."""
|
||||
self.validator.assert_valid(
|
||||
method_name="acceptsAnArrayOfBytes",
|
||||
parameter_name="a",
|
||||
argument_value=a,
|
||||
)
|
||||
a = [bytes.fromhex(a_element.decode("utf-8")) for a_element in a]
|
||||
return a
|
||||
|
||||
def call(
|
||||
self, a: List[bytes], tx_params: Optional[TxParams] = None
|
||||
self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None
|
||||
) -> None:
|
||||
"""Execute underlying contract method via eth_call.
|
||||
|
||||
@ -252,7 +257,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod):
|
||||
return self.underlying_method(a).call(tx_params.as_dict())
|
||||
|
||||
def send_transaction(
|
||||
self, a: List[bytes], tx_params: Optional[TxParams] = None
|
||||
self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None
|
||||
) -> Union[HexBytes, bytes]:
|
||||
"""Execute underlying contract method via eth_sendTransaction.
|
||||
|
||||
@ -267,7 +272,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod):
|
||||
return self.underlying_method(a).transact(tx_params.as_dict())
|
||||
|
||||
def estimate_gas(
|
||||
self, a: List[bytes], tx_params: Optional[TxParams] = None
|
||||
self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None
|
||||
) -> int:
|
||||
"""Estimate gas consumption of method call."""
|
||||
(a) = self.validate_and_normalize_inputs(a)
|
||||
@ -280,13 +285,13 @@ class SimpleInputSimpleOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, index_0: int):
|
||||
@ -340,13 +345,13 @@ class WithdrawMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, wad: int):
|
||||
@ -395,17 +400,17 @@ class MultiInputMultiOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(
|
||||
self, index_0: int, index_1: bytes, index_2: str
|
||||
self, index_0: int, index_1: Union[bytes, str], index_2: str
|
||||
):
|
||||
"""Validate the inputs to the multiInputMultiOutput method."""
|
||||
self.validator.assert_valid(
|
||||
@ -420,7 +425,6 @@ class MultiInputMultiOutputMethod(ContractMethod):
|
||||
parameter_name="index_1",
|
||||
argument_value=index_1,
|
||||
)
|
||||
index_1 = bytes.fromhex(index_1.decode("utf-8"))
|
||||
self.validator.assert_valid(
|
||||
method_name="multiInputMultiOutput",
|
||||
parameter_name="index_2",
|
||||
@ -431,10 +435,10 @@ class MultiInputMultiOutputMethod(ContractMethod):
|
||||
def call(
|
||||
self,
|
||||
index_0: int,
|
||||
index_1: bytes,
|
||||
index_1: Union[bytes, str],
|
||||
index_2: str,
|
||||
tx_params: Optional[TxParams] = None,
|
||||
) -> Tuple[bytes, bytes, str]:
|
||||
) -> Tuple[Union[bytes, str], Union[bytes, str], str]:
|
||||
"""Execute underlying contract method via eth_call.
|
||||
|
||||
Tests decoding when the input and output are complex and have more than
|
||||
@ -454,7 +458,7 @@ class MultiInputMultiOutputMethod(ContractMethod):
|
||||
def send_transaction(
|
||||
self,
|
||||
index_0: int,
|
||||
index_1: bytes,
|
||||
index_1: Union[bytes, str],
|
||||
index_2: str,
|
||||
tx_params: Optional[TxParams] = None,
|
||||
) -> Union[HexBytes, bytes]:
|
||||
@ -477,7 +481,7 @@ class MultiInputMultiOutputMethod(ContractMethod):
|
||||
def estimate_gas(
|
||||
self,
|
||||
index_0: int,
|
||||
index_1: bytes,
|
||||
index_1: Union[bytes, str],
|
||||
index_2: str,
|
||||
tx_params: Optional[TxParams] = None,
|
||||
) -> int:
|
||||
@ -496,17 +500,21 @@ class EcrecoverFnMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(
|
||||
self, _hash: bytes, v: int, r: bytes, s: bytes
|
||||
self,
|
||||
_hash: Union[bytes, str],
|
||||
v: int,
|
||||
r: Union[bytes, str],
|
||||
s: Union[bytes, str],
|
||||
):
|
||||
"""Validate the inputs to the ecrecoverFn method."""
|
||||
self.validator.assert_valid(
|
||||
@ -527,10 +535,10 @@ class EcrecoverFnMethod(ContractMethod):
|
||||
|
||||
def call(
|
||||
self,
|
||||
_hash: bytes,
|
||||
_hash: Union[bytes, str],
|
||||
v: int,
|
||||
r: bytes,
|
||||
s: bytes,
|
||||
r: Union[bytes, str],
|
||||
s: Union[bytes, str],
|
||||
tx_params: Optional[TxParams] = None,
|
||||
) -> str:
|
||||
"""Execute underlying contract method via eth_call.
|
||||
@ -555,10 +563,10 @@ class EcrecoverFnMethod(ContractMethod):
|
||||
|
||||
def send_transaction(
|
||||
self,
|
||||
_hash: bytes,
|
||||
_hash: Union[bytes, str],
|
||||
v: int,
|
||||
r: bytes,
|
||||
s: bytes,
|
||||
r: Union[bytes, str],
|
||||
s: Union[bytes, str],
|
||||
tx_params: Optional[TxParams] = None,
|
||||
) -> Union[HexBytes, bytes]:
|
||||
"""Execute underlying contract method via eth_sendTransaction.
|
||||
@ -585,10 +593,10 @@ class EcrecoverFnMethod(ContractMethod):
|
||||
|
||||
def estimate_gas(
|
||||
self,
|
||||
_hash: bytes,
|
||||
_hash: Union[bytes, str],
|
||||
v: int,
|
||||
r: bytes,
|
||||
s: bytes,
|
||||
r: Union[bytes, str],
|
||||
s: Union[bytes, str],
|
||||
tx_params: Optional[TxParams] = None,
|
||||
) -> int:
|
||||
"""Estimate gas consumption of method call."""
|
||||
@ -604,24 +612,25 @@ class AcceptsBytesMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, a: bytes):
|
||||
def validate_and_normalize_inputs(self, a: Union[bytes, str]):
|
||||
"""Validate the inputs to the acceptsBytes method."""
|
||||
self.validator.assert_valid(
|
||||
method_name="acceptsBytes", parameter_name="a", argument_value=a
|
||||
)
|
||||
a = bytes.fromhex(a.decode("utf-8"))
|
||||
return a
|
||||
|
||||
def call(self, a: bytes, tx_params: Optional[TxParams] = None) -> None:
|
||||
def call(
|
||||
self, a: Union[bytes, str], tx_params: Optional[TxParams] = None
|
||||
) -> None:
|
||||
"""Execute underlying contract method via eth_call.
|
||||
|
||||
:param tx_params: transaction parameters
|
||||
@ -632,7 +641,7 @@ class AcceptsBytesMethod(ContractMethod):
|
||||
return self.underlying_method(a).call(tx_params.as_dict())
|
||||
|
||||
def send_transaction(
|
||||
self, a: bytes, tx_params: Optional[TxParams] = None
|
||||
self, a: Union[bytes, str], tx_params: Optional[TxParams] = None
|
||||
) -> Union[HexBytes, bytes]:
|
||||
"""Execute underlying contract method via eth_sendTransaction.
|
||||
|
||||
@ -644,7 +653,7 @@ class AcceptsBytesMethod(ContractMethod):
|
||||
return self.underlying_method(a).transact(tx_params.as_dict())
|
||||
|
||||
def estimate_gas(
|
||||
self, a: bytes, tx_params: Optional[TxParams] = None
|
||||
self, a: Union[bytes, str], tx_params: Optional[TxParams] = None
|
||||
) -> int:
|
||||
"""Estimate gas consumption of method call."""
|
||||
(a) = self.validate_and_normalize_inputs(a)
|
||||
@ -657,13 +666,13 @@ class NoInputSimpleOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> int:
|
||||
@ -701,13 +710,13 @@ class RevertWithConstantMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> None:
|
||||
@ -741,13 +750,13 @@ class SimpleRevertMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> None:
|
||||
@ -783,13 +792,13 @@ class MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod(
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> Tuple0x1b9da225:
|
||||
@ -823,13 +832,13 @@ class NestedStructOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5:
|
||||
@ -863,13 +872,13 @@ class RequireWithConstantMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> None:
|
||||
@ -903,13 +912,13 @@ class WithAddressInputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(
|
||||
@ -1011,13 +1020,13 @@ class StructInputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, s: Tuple0xcf8ad995):
|
||||
@ -1065,13 +1074,13 @@ class NonPureMethodMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(
|
||||
@ -1106,13 +1115,13 @@ class ComplexInputComplexOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, complex_input: Tuple0xf95128ef):
|
||||
@ -1176,13 +1185,13 @@ class NoInputNoOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> None:
|
||||
@ -1220,13 +1229,13 @@ class SimplePureFunctionWithInputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, x: int):
|
||||
@ -1276,13 +1285,13 @@ class NonPureMethodThatReturnsNothingMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(
|
||||
@ -1317,13 +1326,13 @@ class SimplePureFunctionMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> int:
|
||||
@ -1357,13 +1366,13 @@ class NestedStructInputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, n: Tuple0xc9bdd2d5):
|
||||
@ -1413,13 +1422,13 @@ class MethodReturningMultipleValuesMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> Tuple[int, str]:
|
||||
@ -1453,13 +1462,13 @@ class MethodReturningArrayOfStructsMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(
|
||||
@ -1495,13 +1504,13 @@ class EmitSimpleEventMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(
|
||||
@ -1536,13 +1545,13 @@ class StructOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995:
|
||||
@ -1580,13 +1589,13 @@ class PureFunctionWithConstantMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def call(self, tx_params: Optional[TxParams] = None) -> int:
|
||||
@ -1620,13 +1629,13 @@ class SimpleInputNoOutputMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, index_0: int):
|
||||
@ -1680,13 +1689,13 @@ class OverloadedMethod2Method(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, a: str):
|
||||
@ -1734,13 +1743,13 @@ class OverloadedMethod1Method(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, a: int):
|
||||
@ -1943,24 +1952,55 @@ class AbiGenDummy:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
validator: AbiGenDummyValidator = None,
|
||||
):
|
||||
"""Get an instance of wrapper for smart contract.
|
||||
|
||||
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
||||
:param web3_or_provider: Either an instance of `web3.Web3`:code: or
|
||||
`web3.providers.base.BaseProvider`:code:
|
||||
:param contract_address: where the contract has been deployed
|
||||
:param validator: for validation of method inputs.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
self.contract_address = contract_address
|
||||
|
||||
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
|
||||
provider
|
||||
).eth
|
||||
web3 = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
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(
|
||||
address=to_checksum_address(contract_address),
|
||||
@ -1968,162 +2008,210 @@ class AbiGenDummy:
|
||||
).functions
|
||||
|
||||
self.simple_require = SimpleRequireMethod(
|
||||
provider, contract_address, functions.simpleRequire, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.simpleRequire,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.accepts_an_array_of_bytes = AcceptsAnArrayOfBytesMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.acceptsAnArrayOfBytes,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.simple_input_simple_output = SimpleInputSimpleOutputMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.simpleInputSimpleOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.withdraw = WithdrawMethod(
|
||||
provider, contract_address, functions.withdraw, validator
|
||||
web3_or_provider, contract_address, functions.withdraw, validator
|
||||
)
|
||||
|
||||
self.multi_input_multi_output = MultiInputMultiOutputMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.multiInputMultiOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.ecrecover_fn = EcrecoverFnMethod(
|
||||
provider, contract_address, functions.ecrecoverFn, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.ecrecoverFn,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.accepts_bytes = AcceptsBytesMethod(
|
||||
provider, contract_address, functions.acceptsBytes, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.acceptsBytes,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.no_input_simple_output = NoInputSimpleOutputMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.noInputSimpleOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.revert_with_constant = RevertWithConstantMethod(
|
||||
provider, contract_address, functions.revertWithConstant, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.revertWithConstant,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.simple_revert = SimpleRevertMethod(
|
||||
provider, contract_address, functions.simpleRevert, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.simpleRevert,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.method_using_nested_struct_with_inner_struct_not_used_elsewhere = MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.methodUsingNestedStructWithInnerStructNotUsedElsewhere,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.nested_struct_output = NestedStructOutputMethod(
|
||||
provider, contract_address, functions.nestedStructOutput, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.nestedStructOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.require_with_constant = RequireWithConstantMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.requireWithConstant,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.with_address_input = WithAddressInputMethod(
|
||||
provider, contract_address, functions.withAddressInput, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.withAddressInput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.struct_input = StructInputMethod(
|
||||
provider, contract_address, functions.structInput, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.structInput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.non_pure_method = NonPureMethodMethod(
|
||||
provider, contract_address, functions.nonPureMethod, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.nonPureMethod,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.complex_input_complex_output = ComplexInputComplexOutputMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.complexInputComplexOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.no_input_no_output = NoInputNoOutputMethod(
|
||||
provider, contract_address, functions.noInputNoOutput, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.noInputNoOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.simple_pure_function_with_input = SimplePureFunctionWithInputMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.simplePureFunctionWithInput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.nonPureMethodThatReturnsNothing,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.simple_pure_function = SimplePureFunctionMethod(
|
||||
provider, contract_address, functions.simplePureFunction, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.simplePureFunction,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.nested_struct_input = NestedStructInputMethod(
|
||||
provider, contract_address, functions.nestedStructInput, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.nestedStructInput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.method_returning_multiple_values = MethodReturningMultipleValuesMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.methodReturningMultipleValues,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.method_returning_array_of_structs = MethodReturningArrayOfStructsMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.methodReturningArrayOfStructs,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.emit_simple_event = EmitSimpleEventMethod(
|
||||
provider, contract_address, functions.emitSimpleEvent, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.emitSimpleEvent,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.struct_output = StructOutputMethod(
|
||||
provider, contract_address, functions.structOutput, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.structOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.pure_function_with_constant = PureFunctionWithConstantMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.pureFunctionWithConstant,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.simple_input_no_output = SimpleInputNoOutputMethod(
|
||||
provider,
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.simpleInputNoOutput,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.overloaded_method2 = OverloadedMethod2Method(
|
||||
provider, contract_address, functions.overloadedMethod, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.overloadedMethod,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.overloaded_method1 = OverloadedMethod1Method(
|
||||
provider, contract_address, functions.overloadedMethod, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.overloadedMethod,
|
||||
validator,
|
||||
)
|
||||
|
||||
def get_withdrawal_event(
|
||||
|
@ -40,30 +40,65 @@ except ImportError:
|
||||
"""No-op input validator."""
|
||||
|
||||
|
||||
try:
|
||||
from .middleware import MIDDLEWARE # type: ignore
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
# pylint: disable=too-many-public-methods,too-many-instance-attributes
|
||||
class LibDummy:
|
||||
"""Wrapper class for LibDummy Solidity contract."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
validator: LibDummyValidator = None,
|
||||
):
|
||||
"""Get an instance of wrapper for smart contract.
|
||||
|
||||
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
||||
:param web3_or_provider: Either an instance of `web3.Web3`:code: or
|
||||
`web3.providers.base.BaseProvider`:code:
|
||||
:param contract_address: where the contract has been deployed
|
||||
:param validator: for validation of method inputs.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
self.contract_address = contract_address
|
||||
|
||||
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
|
||||
provider
|
||||
).eth
|
||||
web3 = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
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
|
||||
def abi():
|
||||
|
@ -40,18 +40,24 @@ except ImportError:
|
||||
"""No-op input validator."""
|
||||
|
||||
|
||||
try:
|
||||
from .middleware import MIDDLEWARE # type: ignore
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class PublicAddConstantMethod(ContractMethod):
|
||||
"""Various interfaces to the publicAddConstant method."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, x: int):
|
||||
@ -101,13 +107,13 @@ class PublicAddOneMethod(ContractMethod):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
contract_function: ContractFunction,
|
||||
validator: Validator = None,
|
||||
):
|
||||
"""Persist instance data."""
|
||||
super().__init__(provider, contract_address, validator)
|
||||
super().__init__(web3_or_provider, contract_address, validator)
|
||||
self.underlying_method = contract_function
|
||||
|
||||
def validate_and_normalize_inputs(self, x: int):
|
||||
@ -166,24 +172,55 @@ class TestLibDummy:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
validator: TestLibDummyValidator = None,
|
||||
):
|
||||
"""Get an instance of wrapper for smart contract.
|
||||
|
||||
:param provider: instance of :class:`web3.providers.base.BaseProvider`
|
||||
:param web3_or_provider: Either an instance of `web3.Web3`:code: or
|
||||
`web3.providers.base.BaseProvider`:code:
|
||||
:param contract_address: where the contract has been deployed
|
||||
:param validator: for validation of method inputs.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
self.contract_address = contract_address
|
||||
|
||||
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
|
||||
provider
|
||||
).eth
|
||||
web3 = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
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(
|
||||
address=to_checksum_address(contract_address),
|
||||
@ -191,11 +228,17 @@ class TestLibDummy:
|
||||
).functions
|
||||
|
||||
self.public_add_constant = PublicAddConstantMethod(
|
||||
provider, contract_address, functions.publicAddConstant, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.publicAddConstant,
|
||||
validator,
|
||||
)
|
||||
|
||||
self.public_add_one = PublicAddOneMethod(
|
||||
provider, contract_address, functions.publicAddOne, validator
|
||||
web3_or_provider,
|
||||
contract_address,
|
||||
functions.publicAddOne,
|
||||
validator,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
117
packages/contract-addresses/addresses.json
Normal file
117
packages/contract-addresses/addresses.json
Normal 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"
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import addresses from '../addresses.json';
|
||||
|
||||
export interface ContractAddresses {
|
||||
erc20Proxy: string;
|
||||
erc721Proxy: string;
|
||||
@ -32,127 +34,6 @@ export enum NetworkId {
|
||||
Ganache = 50,
|
||||
}
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
1: {
|
||||
exchangeV2: '0x080bf510fcbf18b91105470639e9561022937712',
|
||||
exchange: NULL_ADDRESS,
|
||||
erc20Proxy: '0x95e6f48254609a6ee006f7d493c8e5fb97094cef',
|
||||
erc721Proxy: '0xefc70a1b18c432bdc64b596838b4d138f6bc6cad',
|
||||
forwarder: NULL_ADDRESS,
|
||||
orderValidator: NULL_ADDRESS,
|
||||
zrxToken: '0xe41d2489571d322189246dafa5ebde1f4699f498',
|
||||
etherToken: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||
assetProxyOwner: '0xdffe798c7172dd6deb32baee68af322e8f495ce0',
|
||||
zeroExGovernor: NULL_ADDRESS,
|
||||
dutchAuction: NULL_ADDRESS,
|
||||
coordinatorRegistry: '0x45797531b873fd5e519477a070a955764c1a5b07',
|
||||
coordinator: NULL_ADDRESS,
|
||||
multiAssetProxy: '0xef701d5389ae74503d633396c4d654eabedc9d78',
|
||||
staticCallProxy: '0x3517b88c19508c08650616019062b898ab65ed29',
|
||||
erc1155Proxy: '0x7eefbd48fd63d441ec7435d024ec7c5131019add',
|
||||
zrxVault: NULL_ADDRESS,
|
||||
staking: NULL_ADDRESS,
|
||||
stakingProxy: NULL_ADDRESS,
|
||||
devUtils: NULL_ADDRESS,
|
||||
erc20BridgeProxy: NULL_ADDRESS,
|
||||
},
|
||||
3: {
|
||||
erc20Proxy: '0xb1408f4c245a23c31b98d2c626777d4c0d766caa',
|
||||
erc721Proxy: '0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4',
|
||||
zrxToken: '0xff67881f8d12f372d91baae9752eb3631ff0ed00',
|
||||
etherToken: '0xc778417e063141139fce010982780140aa0cd5ab',
|
||||
exchangeV2: '0xbff9493f92a3df4b0429b6d00743b3cfb4c85831',
|
||||
exchange: '0xc56388332ddfc98701fefed94535100c6166956c',
|
||||
assetProxyOwner: NULL_ADDRESS,
|
||||
zeroExGovernor: '0xdcf20f7b447d51f2b3e5499b7f6cbbf7295a5d26',
|
||||
forwarder: '0xe66ae6162b3e9067d6ce9e5b9799cca1ba0d09f8',
|
||||
orderValidator: NULL_ADDRESS,
|
||||
dutchAuction: NULL_ADDRESS,
|
||||
coordinatorRegistry: '0x403cc23e88c17c4652fb904784d1af640a6722d9',
|
||||
coordinator: '0xad8464022213a618c96a1178a927a5ed15ad6949',
|
||||
multiAssetProxy: '0xab8fbd189c569ccdee3a4d929bb7f557be4028f6',
|
||||
staticCallProxy: '0xe1b97e47aa3796276033a5341e884d2ba46b6ac1',
|
||||
erc1155Proxy: '0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d',
|
||||
devUtils: '0x9a8590eebcfc53f0cc7ab5ebb8c079e9e7d4e0f5',
|
||||
zrxVault: '0xffd161026865ad8b4ab28a76840474935eec4dfa',
|
||||
staking: '0x3f46b98061a3e1e1f41dff296ec19402c298f8a9',
|
||||
stakingProxy: '0xfaabcee42ab6b9c649794ac6c133711071897ee9',
|
||||
erc20BridgeProxy: '0x599b340b5045436a99b1f0c718d30f5a0c8519dd',
|
||||
},
|
||||
4: {
|
||||
exchangeV2: '0xbff9493f92a3df4b0429b6d00743b3cfb4c85831',
|
||||
exchange: '0x3afe8aa355e086d898447732cfa5d931cfb2a792',
|
||||
erc20Proxy: '0x2f5ae4f6106e89b4147651688a92256885c5f410',
|
||||
erc721Proxy: '0x7656d773e11ff7383a14dcf09a9c50990481cd10',
|
||||
zrxToken: '0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa',
|
||||
etherToken: '0xc778417e063141139fce010982780140aa0cd5ab',
|
||||
assetProxyOwner: NULL_ADDRESS,
|
||||
zeroExGovernor: '0x5d751aa855a1aee5fe44cf5350ed25b5727b66ae',
|
||||
forwarder: '0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291',
|
||||
orderValidator: NULL_ADDRESS,
|
||||
dutchAuction: NULL_ADDRESS,
|
||||
coordinatorRegistry: '0x1084b6a398e47907bae43fec3ff4b677db6e4fee',
|
||||
coordinator: '0x9ae7a6e4e4d58c36b7aa573fc06ce46dd3cb0d44',
|
||||
multiAssetProxy: '0xb34cde0ad3a83d04abebc0b66e75196f22216621',
|
||||
staticCallProxy: '0xe1b97e47aa3796276033a5341e884d2ba46b6ac1',
|
||||
erc1155Proxy: '0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d',
|
||||
devUtils: '0xfcbb258112485f18dd68f4b1016e48c23542fdc5',
|
||||
zrxVault: '0xa5bf6ac73bc40790fc6ffc9dbbbce76c9176e224',
|
||||
staking: '0x344d4f661a82afdd84d31456c291822d90d5dc3a',
|
||||
stakingProxy: '0xc6ad5277ea225ac05e271eb14a7ebb480cd9dd9f',
|
||||
erc20BridgeProxy: '0x31b8653642110f17bdb1f719901d7e7d49b08141',
|
||||
},
|
||||
42: {
|
||||
erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e',
|
||||
erc721Proxy: '0x2a9127c745688a165106c11cd4d647d2220af821',
|
||||
zrxToken: '0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa',
|
||||
etherToken: '0xd0a1e359811322d97991e03f863a0c30c2cf029c',
|
||||
exchangeV2: '0x30589010550762d2f0d06f650d8e8b6ade6dbf4b',
|
||||
exchange: '0xca8b1626b3b7a0da722ca9f264c4630c7d34d3b8',
|
||||
assetProxyOwner: NULL_ADDRESS,
|
||||
zeroExGovernor: '0x3654e5363cd75c8974c76208137df9691e820e97',
|
||||
forwarder: '0xd6330f9d2073e1889a295dd1dd2e28d42dec4bff',
|
||||
orderValidator: NULL_ADDRESS,
|
||||
dutchAuction: NULL_ADDRESS,
|
||||
coordinatorRegistry: '0x09fb99968c016a3ff537bf58fb3d9fe55a7975d5',
|
||||
coordinator: '0x10e0b1c2e6065ec7f290c7e3731264f9a2bf2b2d',
|
||||
multiAssetProxy: '0xf6313a772c222f51c28f2304c0703b8cf5428fd8',
|
||||
staticCallProxy: '0x48e94bdb9033640d45ea7c721e25f380f8bffa43',
|
||||
erc1155Proxy: '0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f',
|
||||
devUtils: '0x58c4fbdf9222f10ad2bef8f4d374f209135e71a5',
|
||||
zrxVault: '0xf36eabdfe986b35b62c8fd5a98a7f2aebb79b291',
|
||||
staking: '0x89150f5eed50b3528f79bfb539f29d727f92821c',
|
||||
stakingProxy: '0xbab9145f1d57cd4bb0c9aa2d1ece0a5b6e734d34',
|
||||
erc20BridgeProxy: '0xfb2dd2a1366de37f7241c83d47da58fd503e2c64',
|
||||
},
|
||||
// NetworkId 50 represents our Ganache snapshot generated from migrations.
|
||||
50: {
|
||||
erc20Proxy: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
|
||||
erc721Proxy: '0x1d7022f5b17d2f8b695918fb48fa1089c9f85401',
|
||||
erc1155Proxy: '0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8',
|
||||
zrxToken: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
|
||||
etherToken: '0x0b1ba0af832d7c05fd64161e0db78e85978e8082',
|
||||
exchangeV2: '0x48bacb9266a570d521063ef5dd96e61686dbe788',
|
||||
exchange: '0x48bacb9266a570d521063ef5dd96e61686dbe788',
|
||||
zeroExGovernor: NULL_ADDRESS,
|
||||
assetProxyOwner: NULL_ADDRESS,
|
||||
forwarder: NULL_ADDRESS,
|
||||
orderValidator: NULL_ADDRESS,
|
||||
dutchAuction: NULL_ADDRESS,
|
||||
coordinatorRegistry: '0x1941ff73d1154774d87521d2d0aaad5d19c8df60',
|
||||
coordinator: NULL_ADDRESS,
|
||||
multiAssetProxy: '0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db',
|
||||
staticCallProxy: '0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f',
|
||||
devUtils: '0x38ef19fdf8e8415f18c307ed71967e19aac28ba1',
|
||||
zrxVault: NULL_ADDRESS,
|
||||
staking: NULL_ADDRESS,
|
||||
stakingProxy: NULL_ADDRESS,
|
||||
erc20BridgeProxy: NULL_ADDRESS,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to get addresses of contracts that have been deployed to either the
|
||||
* Ethereum mainnet or a supported testnet. Throws if there are no known
|
||||
@ -162,6 +43,8 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = {
|
||||
* given networkId.
|
||||
*/
|
||||
export function getContractAddressesForNetworkOrThrow(networkId: NetworkId): ContractAddresses {
|
||||
const networkToAddresses: { [networkId: number]: ContractAddresses } = addresses;
|
||||
|
||||
if (networkToAddresses[networkId] === undefined) {
|
||||
throw new Error(`Unknown network id (${networkId}). No known 0x contracts have been deployed on this network.`);
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "."
|
||||
"rootDir": ".",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
"include": ["./src/**/*"],
|
||||
"files": ["./addresses.json"]
|
||||
}
|
||||
|
@ -2,10 +2,14 @@
|
||||
|
||||
"""setuptools module for contract_addresses package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import subprocess # nosec
|
||||
from shutil import rmtree
|
||||
from shutil import copyfile, rmtree
|
||||
from os import environ, path
|
||||
from sys import argv
|
||||
from sys import argv, exit # pylint: disable=redefined-builtin
|
||||
|
||||
from distutils.command.clean import clean
|
||||
import distutils.command.build_py
|
||||
@ -13,6 +17,34 @@ from setuptools import find_packages, setup
|
||||
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):
|
||||
"""Custom setuptools command class for running linters."""
|
||||
|
||||
@ -131,6 +163,7 @@ setup(
|
||||
author_email="feuGeneA@users.noreply.github.com",
|
||||
cmdclass={
|
||||
"clean": CleanCommandExtension,
|
||||
"pre_install": PreInstallCommand,
|
||||
"lint": LintCommand,
|
||||
"test": TestCommandExtension,
|
||||
"test_publish": TestPublishCommand,
|
||||
@ -156,7 +189,9 @@ setup(
|
||||
]
|
||||
},
|
||||
python_requires=">=3.6, <4",
|
||||
package_data={"zero_ex.contract_addresses": ["py.typed"]},
|
||||
package_data={
|
||||
"zero_ex.contract_addresses": ["py.typed", "addresses.json"]
|
||||
},
|
||||
package_dir={"": "src"},
|
||||
license="Apache 2.0",
|
||||
keywords=(
|
||||
|
@ -19,7 +19,7 @@ Python zero_ex.contract_addresses
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autodata:: zero_ex.contract_addresses.NETWORK_TO_ADDRESSES
|
||||
.. autodata:: zero_ex.contract_addresses.network_to_addresses
|
||||
:annotation: : Dict[NetworkId, ContractAddresses]
|
||||
|
||||
Indices and tables
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -10,8 +10,11 @@ Install the package with pip::
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
import json
|
||||
from typing import Dict, NamedTuple
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
|
||||
class ContractAddresses(NamedTuple):
|
||||
"""An abstract record listing all the contracts that have addresses."""
|
||||
@ -20,10 +23,7 @@ class ContractAddresses(NamedTuple):
|
||||
"""Address of the ERC20Proxy contract."""
|
||||
|
||||
erc721_proxy: str
|
||||
"""Address of the ERC20Proxy contract."""
|
||||
|
||||
erc1155_proxy: str
|
||||
"""Address of the ERC1155Proxy contract."""
|
||||
"""Address of the ERC721Proxy contract."""
|
||||
|
||||
zrx_token: str
|
||||
"""Address of the ZRX token contract."""
|
||||
@ -31,27 +31,57 @@ class ContractAddresses(NamedTuple):
|
||||
ether_token: str
|
||||
"""Address of the WETH token contract."""
|
||||
|
||||
exchange_v2: str
|
||||
"""Address of the v2 Exchange contract."""
|
||||
|
||||
exchange: str
|
||||
"""Address of the Exchange contract."""
|
||||
"""Address of the v3 Exchange contract."""
|
||||
|
||||
asset_proxy_owner: str
|
||||
"""Address of the AssetProxyOwner contract."""
|
||||
|
||||
zero_ex_governor: str
|
||||
"""Address of the ZeroExGovernor contract."""
|
||||
|
||||
forwarder: str
|
||||
"""Address of the Forwarder contract."""
|
||||
|
||||
order_validator: str
|
||||
"""Address of the OrderValidator contract."""
|
||||
|
||||
dutch_auction: str
|
||||
"""Address of the DutchAuction contract."""
|
||||
|
||||
coordinator_registry: str
|
||||
"""Address of the CoordinatorRegistry contract."""
|
||||
|
||||
coordinator: str
|
||||
"""Address of the Coordinator contract."""
|
||||
|
||||
multi_asset_proxy: str
|
||||
"""Address of the MultiAssetProxy contract."""
|
||||
|
||||
static_call_proxy: str
|
||||
"""Address of the StaticCallProxy contract."""
|
||||
|
||||
erc1155_proxy: str
|
||||
"""Address of the ERC1155Proxy contract."""
|
||||
|
||||
dev_utils: str
|
||||
"""Address of the DevUtils contract."""
|
||||
|
||||
zrx_vault: str
|
||||
"""Address of the ZRXVault contract."""
|
||||
|
||||
staking: str
|
||||
"""Address of the Staking contract."""
|
||||
|
||||
staking_proxy: str
|
||||
"""Address of the StakingProxy contract."""
|
||||
|
||||
erc20_bridge_proxy: str
|
||||
"""Address of the ERC20BridgeProxy contract."""
|
||||
|
||||
|
||||
class NetworkId(Enum):
|
||||
"""Network names correlated to their network identification numbers.
|
||||
@ -70,83 +100,62 @@ class NetworkId(Enum):
|
||||
GANACHE = 50
|
||||
|
||||
|
||||
NETWORK_TO_ADDRESSES: Dict[NetworkId, ContractAddresses] = {
|
||||
NetworkId.MAINNET: ContractAddresses( # nosec
|
||||
erc20_proxy="0x95e6f48254609a6ee006f7d493c8e5fb97094cef",
|
||||
erc721_proxy="0xefc70a1b18c432bdc64b596838b4d138f6bc6cad",
|
||||
zrx_token="0xe41d2489571d322189246dafa5ebde1f4699f498",
|
||||
ether_token="0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
|
||||
exchange="0x080bf510fcbf18b91105470639e9561022937712",
|
||||
asset_proxy_owner="0xdffe798c7172dd6deb32baee68af322e8f495ce0",
|
||||
forwarder="0x76481caa104b5f6bccb540dae4cefaf1c398ebea",
|
||||
order_validator="0xa09329c6003c9a5402102e226417738ee22cf1f2",
|
||||
coordinator_registry="0x45797531b873fd5e519477a070a955764c1a5b07",
|
||||
coordinator="0xa14857e8930acd9a882d33ec20559beb5479c8a6",
|
||||
erc1155_proxy="0x7eefbd48fd63d441ec7435d024ec7c5131019add",
|
||||
dev_utils="0x92d9a4d50190ae04e03914db2ee650124af844e6",
|
||||
),
|
||||
NetworkId.ROPSTEN: ContractAddresses( # nosec
|
||||
erc20_proxy="0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
|
||||
erc721_proxy="0xe654aac058bfbf9f83fcaee7793311dd82f6ddb4",
|
||||
zrx_token="0xff67881f8d12f372d91baae9752eb3631ff0ed00",
|
||||
ether_token="0xc778417e063141139fce010982780140aa0cd5ab",
|
||||
exchange="0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
||||
asset_proxy_owner="0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b",
|
||||
forwarder="0x1ebdc9758e85c1c6a85af06cc96cf89000a31913",
|
||||
order_validator="0x90431a90516ab49af23a0530e04e8c7836e7122f",
|
||||
coordinator_registry="0x403cc23e88c17c4652fb904784d1af640a6722d9",
|
||||
coordinator="0x2ba02e03ee0029311e0f43715307870a3e701b53",
|
||||
erc1155_proxy="0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d",
|
||||
dev_utils="0x3e0b46bad8e374e4a110c12b832cb120dbe4a479",
|
||||
),
|
||||
NetworkId.RINKEBY: ContractAddresses( # nosec
|
||||
exchange="0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
||||
erc20_proxy="0x2f5ae4f6106e89b4147651688a92256885c5f410",
|
||||
erc721_proxy="0x7656d773e11ff7383a14dcf09a9c50990481cd10",
|
||||
zrx_token="0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa",
|
||||
ether_token="0xc778417e063141139fce010982780140aa0cd5ab",
|
||||
asset_proxy_owner="0xe1703da878afcebff5b7624a826902af475b9c03",
|
||||
forwarder="0x1ebdc9758e85c1c6a85af06cc96cf89000a31913",
|
||||
order_validator="0x0c5173a51e26b29d6126c686756fb9fbef71f762",
|
||||
coordinator_registry="0x1084b6a398e47907bae43fec3ff4b677db6e4fee",
|
||||
coordinator="0x2ba02e03ee0029311e0f43715307870a3e701b53",
|
||||
erc1155_proxy="0x19bb6caa3bc34d39e5a23cedfa3e6c7e7f3c931d",
|
||||
dev_utils="0x2d4a9abda7b8b3605c8dbd34e3550a7467c78287'",
|
||||
),
|
||||
NetworkId.KOVAN: ContractAddresses( # nosec
|
||||
erc20_proxy="0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e",
|
||||
erc721_proxy="0x2a9127c745688a165106c11cd4d647d2220af821",
|
||||
zrx_token="0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa",
|
||||
ether_token="0xd0a1e359811322d97991e03f863a0c30c2cf029c",
|
||||
exchange="0x30589010550762d2f0d06f650d8e8b6ade6dbf4b",
|
||||
asset_proxy_owner="0x2c824d2882baa668e0d5202b1e7f2922278703f8",
|
||||
forwarder="0x1ebdc9758e85c1c6a85af06cc96cf89000a31913",
|
||||
order_validator="0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d",
|
||||
coordinator_registry="0x09fb99968c016a3ff537bf58fb3d9fe55a7975d5",
|
||||
coordinator="0x2ba02e03ee0029311e0f43715307870a3e701b53",
|
||||
erc1155_proxy="0x64517fa2b480ba3678a2a3c0cf08ef7fd4fad36f",
|
||||
dev_utils="0x1e3616bc5144362f95d72de41874395567697e93",
|
||||
),
|
||||
NetworkId.GANACHE: ContractAddresses( # nosec
|
||||
exchange="0x48bacb9266a570d521063ef5dd96e61686dbe788",
|
||||
erc20_proxy="0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
|
||||
erc721_proxy="0x1d7022f5b17d2f8b695918fb48fa1089c9f85401",
|
||||
erc1155_proxy="0x6a4a62e5a7ed13c361b176a5f62c2ee620ac0df8",
|
||||
zrx_token="0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c",
|
||||
ether_token="0x0b1ba0af832d7c05fd64161e0db78e85978e8082",
|
||||
asset_proxy_owner="0x8d42e38980ce74736c21c059b2240df09958d3c8",
|
||||
forwarder="0xaa86dda78e9434aca114b6676fc742a18d15a1cc",
|
||||
order_validator="0x4d3d5c850dd5bd9d6f4adda3dd039a3c8054ca29",
|
||||
coordinator_registry="0x1941ff73d1154774d87521d2d0aaad5d19c8df60",
|
||||
coordinator="0x0d8b0dd11f5d34ed41d556def5f841900d5b1c6b",
|
||||
dev_utils="0x38ef19fdf8e8415f18c307ed71967e19aac28ba1",
|
||||
),
|
||||
}
|
||||
"""A mapping from instances of NetworkId to instances of ContractAddresses.
|
||||
class _AddressCache:
|
||||
"""A cache to facilitate lazy & singular loading of contract addresses."""
|
||||
|
||||
Addresses under NetworkId.Ganache are from our Ganache snapshot generated from
|
||||
npm package @0x/migrations.
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
>>> NETWORK_TO_ADDRESSES[NetworkId.MAINNET].exchange
|
||||
0x4f833a24e1f95d70f028921e27040ca56e09ab0b
|
||||
"""
|
||||
# class data, not instance:
|
||||
_network_to_addresses: Dict[str, ContractAddresses] = {}
|
||||
|
||||
@classmethod
|
||||
def network_to_addresses(cls, network_id: NetworkId):
|
||||
"""Return the addresses for the given network ID.
|
||||
|
||||
First tries to get data from the class level storage
|
||||
`_network_to_addresses`. If it's not there, loads it from disk, stores
|
||||
it in the class data (for the next caller), and then returns it.
|
||||
"""
|
||||
try:
|
||||
return cls._network_to_addresses[str(network_id.value)]
|
||||
except KeyError:
|
||||
cls._network_to_addresses = json.loads(
|
||||
resource_string("zero_ex.contract_addresses", "addresses.json")
|
||||
)
|
||||
return cls._network_to_addresses[str(network_id.value)]
|
||||
|
||||
|
||||
def network_to_addresses(network_id: NetworkId) -> ContractAddresses:
|
||||
"""Map a NetworkId to an instance of ContractAddresses.
|
||||
|
||||
Addresses under NetworkId.Ganache are from our Ganache snapshot generated
|
||||
from npm package @0x/migrations.
|
||||
|
||||
>>> network_to_addresses(NetworkId.MAINNET).exchange
|
||||
'0x...'
|
||||
"""
|
||||
addresses = _AddressCache.network_to_addresses(network_id)
|
||||
|
||||
return ContractAddresses(
|
||||
erc20_proxy=addresses["erc20Proxy"],
|
||||
erc721_proxy=addresses["erc721Proxy"],
|
||||
zrx_token=addresses["zrxToken"],
|
||||
ether_token=addresses["etherToken"],
|
||||
exchange_v2=addresses["exchangeV2"],
|
||||
exchange=addresses["exchange"],
|
||||
asset_proxy_owner=addresses["assetProxyOwner"],
|
||||
zero_ex_governor=addresses["zeroExGovernor"],
|
||||
forwarder=addresses["forwarder"],
|
||||
order_validator=addresses["orderValidator"],
|
||||
dutch_auction=addresses["dutchAuction"],
|
||||
coordinator_registry=addresses["coordinatorRegistry"],
|
||||
coordinator=addresses["coordinator"],
|
||||
multi_asset_proxy=addresses["multiAssetProxy"],
|
||||
static_call_proxy=addresses["staticCallProxy"],
|
||||
erc1155_proxy=addresses["erc1155Proxy"],
|
||||
dev_utils=addresses["devUtils"],
|
||||
zrx_vault=addresses["zrxVault"],
|
||||
staking=addresses["staking"],
|
||||
staking_proxy=addresses["stakingProxy"],
|
||||
erc20_bridge_proxy=addresses["erc20BridgeProxy"],
|
||||
)
|
||||
|
@ -10,3 +10,9 @@ envlist = py37
|
||||
commands =
|
||||
pip install -e .[dev]
|
||||
python setup.py test
|
||||
|
||||
[testenv:run_tests_against_deployment]
|
||||
setenv = PY_IGNORE_IMPORTMISMATCH = 1
|
||||
commands=
|
||||
pip install 0x-contract-addresses[dev]
|
||||
pytest --doctest-modules src
|
||||
|
@ -1,9 +1,13 @@
|
||||
# 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
|
||||
|
||||
- Expanded documentation.
|
||||
|
||||
## 2.0.0 - 2019-01-09
|
||||
|
||||
- Initial release.
|
||||
|
@ -2,10 +2,14 @@
|
||||
|
||||
"""setuptools module for contract_artifacts package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import subprocess # nosec
|
||||
from shutil import copytree, rmtree
|
||||
from os import environ, path
|
||||
from sys import argv
|
||||
from sys import argv, exit # pylint: disable=redefined-builtin
|
||||
|
||||
from distutils.command.clean import clean
|
||||
import distutils.command.build_py
|
||||
@ -148,7 +152,7 @@ with open("README.md", "r") as file_handle:
|
||||
|
||||
setup(
|
||||
name="0x-contract-artifacts",
|
||||
version="2.0.1",
|
||||
version="3.0.0",
|
||||
description="0x smart contract compilation artifacts",
|
||||
long_description=README_MD,
|
||||
long_description_content_type="text/markdown",
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -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": {}
|
||||
}
|
@ -10,3 +10,9 @@ envlist = py37
|
||||
commands =
|
||||
pip install -e .[dev]
|
||||
python setup.py test
|
||||
|
||||
[testenv:run_tests_against_deployment]
|
||||
setenv = PY_IGNORE_IMPORTMISMATCH = 1
|
||||
commands=
|
||||
pip install 0x-contract-artifacts[dev]
|
||||
pytest --doctest-modules src
|
||||
|
@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.0 - TBD
|
||||
|
||||
- Updated for version 3 of the protocol.
|
||||
- Allow wrappers to be instantiated with EITHER a Web3.py `BaseProvider` OR an already-instantiated `Web3` client object.
|
||||
- Accept `str`ing arguments to `bytes` contract method parameters.
|
||||
- Expanded documentation examples.
|
||||
- Moved methods `jsdict_to_order()` and `order_to_jsdict()` from `zero_ex.contract_wrappers.exchange.types` to `zero_ex.contract_wrappers.order_conversions`.
|
||||
- Changed field name `zero_ex.contract_wrappers.tx_params.TxParams.gasPrice` to `.gas_price`.
|
||||
|
||||
## 1.1.0 - 2019-08-14
|
||||
|
||||
- Added wrapper for DevUtils contract.
|
||||
|
@ -2,11 +2,15 @@
|
||||
|
||||
"""setuptools module for contract_wrappers package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import subprocess # nosec
|
||||
from shutil import rmtree
|
||||
from os import environ, path, remove
|
||||
from pathlib import Path
|
||||
from sys import argv
|
||||
from sys import argv, exit # pylint: disable=redefined-builtin
|
||||
|
||||
from distutils.command.clean import clean
|
||||
import distutils.command.build_py
|
||||
@ -192,7 +196,7 @@ with open("README.md", "r") as file_handle:
|
||||
|
||||
setup(
|
||||
name="0x-contract-wrappers",
|
||||
version="1.1.0",
|
||||
version="2.0.0",
|
||||
description="Python wrappers for 0x smart contracts",
|
||||
long_description=README_MD,
|
||||
long_description_content_type="text/markdown",
|
||||
|
@ -34,7 +34,7 @@ zero_ex.contract_wrappers.coordinator_registry
|
||||
|
||||
|
||||
zero_ex.contract_wrappers.dev_utils
|
||||
=======================================
|
||||
===================================
|
||||
|
||||
.. automodule:: zero_ex.contract_wrappers.dev_utils
|
||||
:members:
|
||||
@ -49,6 +49,22 @@ zero_ex.contract_wrappers.dutch_auction
|
||||
:special-members:
|
||||
|
||||
|
||||
zero_ex.contract_wrappers.erc1155_mintable
|
||||
==========================================
|
||||
|
||||
.. automodule:: zero_ex.contract_wrappers.erc1155_mintable
|
||||
:members:
|
||||
:special-members:
|
||||
|
||||
|
||||
zero_ex.contract_wrappers.erc1155_proxy
|
||||
=======================================
|
||||
|
||||
.. automodule:: zero_ex.contract_wrappers.erc1155_proxy
|
||||
:members:
|
||||
:special-members:
|
||||
|
||||
|
||||
zero_ex.contract_wrappers.erc20_proxy
|
||||
=====================================
|
||||
|
||||
@ -145,6 +161,14 @@ zero_ex.contract_wrappers.order_validator
|
||||
:special-members:
|
||||
|
||||
|
||||
zero_ex.contract_wrappers.static_call_proxy
|
||||
===========================================
|
||||
|
||||
.. automodule:: zero_ex.contract_wrappers.static_call_proxy
|
||||
:members:
|
||||
:special-members:
|
||||
|
||||
|
||||
zero_ex.contract_wrappers.weth9
|
||||
===============================
|
||||
|
||||
@ -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.ZeroExTransaction
|
||||
|
||||
zero_ex.contract_wrappers.exchange: Generated Tuples
|
||||
====================================================
|
||||
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x260219a2
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x6ca34a6f
|
||||
|
||||
This is the generated class representing `the Order struct <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>`_.
|
||||
|
||||
.. 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>`_.
|
||||
|
||||
@ -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>`_.
|
||||
|
||||
.. 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
|
||||
==================
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -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
|
||||
capture the addresses we'll use throughout the examples below:
|
||||
|
||||
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
>>> weth_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
||||
>>> zrx_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token
|
||||
>>> exchange_address = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange
|
||||
>>> from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
>>> weth_address = network_to_addresses(NetworkId.GANACHE).ether_token
|
||||
>>> zrx_address = network_to_addresses(NetworkId.GANACHE).zrx_token
|
||||
>>> exchange_address = network_to_addresses(NetworkId.GANACHE).exchange
|
||||
|
||||
Wrapping ETH
|
||||
------------
|
||||
@ -92,15 +92,15 @@ balance:
|
||||
|
||||
>>> from zero_ex.contract_wrappers.erc20_token import ERC20Token
|
||||
>>> zrx_token = ERC20Token(
|
||||
... provider=ganache,
|
||||
... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token,
|
||||
... web3_or_provider=ganache,
|
||||
... contract_address=network_to_addresses(NetworkId.GANACHE).zrx_token,
|
||||
... )
|
||||
>>> weth_token = ERC20Token(
|
||||
... provider=ganache,
|
||||
... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token,
|
||||
... web3_or_provider=ganache,
|
||||
... contract_address=network_to_addresses(NetworkId.GANACHE).ether_token,
|
||||
... )
|
||||
|
||||
>>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy
|
||||
>>> erc20_proxy_addr = network_to_addresses(NetworkId.GANACHE).erc20_proxy
|
||||
|
||||
>>> tx = zrx_token.approve.send_transaction(
|
||||
... erc20_proxy_addr,
|
||||
@ -135,16 +135,20 @@ Constructing an order
|
||||
... takerAssetAmount=to_wei(0.1, 'ether'),
|
||||
... expirationTimeSeconds=round(
|
||||
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||
... )
|
||||
... ),
|
||||
... makerFeeAssetData='0x',
|
||||
... takerFeeAssetData='0x',
|
||||
... )
|
||||
|
||||
For this order to be valid, our Maker must sign a hash of it:
|
||||
|
||||
>>> from zero_ex.order_utils import generate_order_hash_hex
|
||||
>>> order_hash_hex = generate_order_hash_hex(order, exchange_address)
|
||||
>>> order_hash_hex = generate_order_hash_hex(
|
||||
... order, exchange_address, Web3(ganache).eth.chainId
|
||||
... )
|
||||
|
||||
>>> from zero_ex.order_utils import sign_hash_to_bytes
|
||||
>>> maker_signature = sign_hash_to_bytes(
|
||||
>>> from zero_ex.order_utils import sign_hash
|
||||
>>> maker_signature = sign_hash(
|
||||
... ganache, Web3.toChecksumAddress(maker_address), order_hash_hex
|
||||
... )
|
||||
|
||||
@ -156,16 +160,37 @@ more information on working with Relayers, see `the documentation for
|
||||
Filling an order
|
||||
----------------
|
||||
|
||||
Now our Taker will fill the order. The `takerAssetAmount`:code: parameter
|
||||
specifies the amount of tokens (in this case WETH) that the taker wants to
|
||||
fill. This example fills the order completely, but partial fills are possible
|
||||
too.
|
||||
Now we'll have our Taker fill the order.
|
||||
|
||||
>>> from zero_ex.contract_wrappers.exchange import Exchange
|
||||
>>> exchange = Exchange(
|
||||
... provider=ganache,
|
||||
... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange,
|
||||
... web3_or_provider=ganache,
|
||||
... contract_address=network_to_addresses(NetworkId.GANACHE).exchange,
|
||||
... )
|
||||
|
||||
But before filling an order, one may wish to check that it's actually fillable:
|
||||
|
||||
>>> from zero_ex.contract_wrappers.exchange.types import OrderStatus
|
||||
>>> OrderStatus(exchange.get_order_info.call(order)[0])
|
||||
<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(
|
||||
... order=order,
|
||||
... taker_asset_fill_amount=order["takerAssetAmount"],
|
||||
@ -184,12 +209,15 @@ the exchange wrapper:
|
||||
'makerAddress': '0x...',
|
||||
'makerAssetData': b...,
|
||||
'makerAssetFilledAmount': 100000000000000000,
|
||||
'makerFeeAssetData': b...,
|
||||
'makerFeePaid': 0,
|
||||
'orderHash': b...,
|
||||
'protocolFeePaid': ...,
|
||||
'senderAddress': '0x...',
|
||||
'takerAddress': '0x...',
|
||||
'takerAssetData': b...,
|
||||
'takerAssetFilledAmount': 100000000000000000,
|
||||
'takerFeeAssetData': b...,
|
||||
'takerFeePaid': 0}
|
||||
>>> exchange.get_fill_event(tx_hash)[0].args.takerAssetFilledAmount
|
||||
100000000000000000
|
||||
@ -206,7 +234,9 @@ A Maker can cancel an order that has yet to be filled.
|
||||
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||
... makerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||
... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... salt=random.randint(1, 100000000000000000),
|
||||
... makerFee=0,
|
||||
... takerFee=0,
|
||||
@ -248,7 +278,9 @@ is an example where the taker fills two orders in one transaction:
|
||||
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||
... makerAssetData=asset_data_utils.encode_erc20(zrx_address),
|
||||
... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... salt=random.randint(1, 100000000000000000),
|
||||
... makerFee=0,
|
||||
... takerFee=0,
|
||||
@ -258,10 +290,12 @@ is an example where the taker fills two orders in one transaction:
|
||||
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||
... )
|
||||
... )
|
||||
>>> signature_1 = sign_hash_to_bytes(
|
||||
>>> signature_1 = sign_hash(
|
||||
... ganache,
|
||||
... Web3.toChecksumAddress(maker_address),
|
||||
... generate_order_hash_hex(order_1, exchange.contract_address)
|
||||
... generate_order_hash_hex(
|
||||
... order_1, exchange.contract_address, Web3(ganache).eth.chainId
|
||||
... ),
|
||||
... )
|
||||
>>> order_2 = Order(
|
||||
... makerAddress=maker_address,
|
||||
@ -269,7 +303,9 @@ is an example where the taker fills two orders in one transaction:
|
||||
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||
... makerAssetData=asset_data_utils.encode_erc20(zrx_address),
|
||||
... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... salt=random.randint(1, 100000000000000000),
|
||||
... makerFee=0,
|
||||
... takerFee=0,
|
||||
@ -279,10 +315,12 @@ is an example where the taker fills two orders in one transaction:
|
||||
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||
... )
|
||||
... )
|
||||
>>> signature_2 = sign_hash_to_bytes(
|
||||
>>> signature_2 = sign_hash(
|
||||
... ganache,
|
||||
... Web3.toChecksumAddress(maker_address),
|
||||
... generate_order_hash_hex(order_2, exchange.contract_address)
|
||||
... generate_order_hash_hex(
|
||||
... order_2, exchange.contract_address, Web3(ganache).eth.chainId
|
||||
... ),
|
||||
... )
|
||||
|
||||
Fill order_1 and order_2 together:
|
||||
@ -308,7 +346,9 @@ will be consumed.
|
||||
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||
... makerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||
... makerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
|
||||
... takerFeeAssetData=asset_data_utils.encode_erc20('0x' + '00'*20),
|
||||
... salt=random.randint(1, 100000000000000000),
|
||||
... makerFee=0,
|
||||
... takerFee=0,
|
||||
@ -320,7 +360,7 @@ will be consumed.
|
||||
... ),
|
||||
... tx_params=TxParams(from_=maker_address),
|
||||
... )
|
||||
73...
|
||||
74...
|
||||
"""
|
||||
|
||||
from .tx_params import TxParams
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Base wrapper class for accessing ethereum smart contracts."""
|
||||
|
||||
from typing import Any
|
||||
from typing import Any, Union
|
||||
|
||||
from eth_utils import is_address, to_checksum_address
|
||||
from web3 import Web3
|
||||
@ -12,7 +12,11 @@ from .tx_params import TxParams
|
||||
class Validator:
|
||||
"""Base class for validating inputs to methods."""
|
||||
|
||||
def __init__(self, provider: BaseProvider, contract_address: str):
|
||||
def __init__(
|
||||
self,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
):
|
||||
"""Initialize the instance."""
|
||||
|
||||
def assert_valid(
|
||||
@ -32,7 +36,7 @@ class ContractMethod:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
provider: BaseProvider,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
validator: Validator = None,
|
||||
):
|
||||
@ -42,9 +46,20 @@ class ContractMethod:
|
||||
:param contract_address: Where the contract has been deployed to.
|
||||
:param validator: Used to validate method inputs.
|
||||
"""
|
||||
self._web3_eth = Web3(provider).eth # pylint: disable=no-member
|
||||
web3 = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
web3 = Web3(web3_or_provider)
|
||||
elif isinstance(web3_or_provider, Web3):
|
||||
web3 = web3_or_provider
|
||||
if web3 is None:
|
||||
raise TypeError(
|
||||
"Expected parameter 'web3_or_provider' to be an instance of either"
|
||||
+ " Web3 or BaseProvider"
|
||||
)
|
||||
|
||||
self._web3_eth = web3.eth # pylint: disable=no-member
|
||||
if validator is None:
|
||||
validator = Validator(provider, contract_address)
|
||||
validator = Validator(web3_or_provider, contract_address)
|
||||
self.validator = validator
|
||||
|
||||
@staticmethod
|
||||
@ -59,8 +74,13 @@ class ContractMethod:
|
||||
if not tx_params:
|
||||
tx_params = TxParams()
|
||||
if not tx_params.from_:
|
||||
tx_params.from_ = (
|
||||
self._web3_eth.defaultAccount or self._web3_eth.accounts[0]
|
||||
tx_params.from_ = self._web3_eth.defaultAccount or (
|
||||
self._web3_eth.accounts[0]
|
||||
if len(self._web3_eth.accounts) > 0
|
||||
else None
|
||||
)
|
||||
if tx_params.from_:
|
||||
tx_params.from_ = self.validate_and_checksum_address(
|
||||
tx_params.from_
|
||||
)
|
||||
tx_params.from_ = self.validate_and_checksum_address(tx_params.from_)
|
||||
return tx_params
|
||||
|
@ -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)
|
@ -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"
|
@ -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}]
|
@ -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.
|
||||
"""
|
||||
|
||||
from copy import copy
|
||||
from typing import cast, Dict
|
||||
|
||||
from eth_utils import remove_0x_prefix
|
||||
|
||||
from zero_ex.json_schemas import assert_valid
|
||||
from enum import auto, Enum
|
||||
|
||||
from . import (
|
||||
Tuple0xbb41e5b3,
|
||||
Tuple0x260219a2,
|
||||
Tuple0x054ca44e,
|
||||
Tuple0x735c43e3,
|
||||
Tuple0x6ca34a6f,
|
||||
Tuple0x4c5ca29b,
|
||||
Tuple0xdabc15fe,
|
||||
Tuple0xb1e4a1ae,
|
||||
)
|
||||
|
||||
@ -33,27 +29,35 @@ from . import (
|
||||
# of each of these classes.
|
||||
|
||||
|
||||
class FillResults(Tuple0xbb41e5b3):
|
||||
class FillResults(Tuple0x735c43e3):
|
||||
"""The `FillResults`:code: Solidity struct.
|
||||
|
||||
Also known as
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0xbb41e5b3`:py:class:.
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0x735c43e3`:py:class:.
|
||||
"""
|
||||
|
||||
|
||||
class Order(Tuple0x260219a2):
|
||||
class Order(Tuple0x6ca34a6f):
|
||||
"""The `Order`:code: Solidity struct.
|
||||
|
||||
Also known as
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0x260219a2`:py:class:.
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0x6ca34a6f`:py:class:.
|
||||
"""
|
||||
|
||||
|
||||
class MatchedFillResults(Tuple0x054ca44e):
|
||||
class MatchedFillResults(Tuple0x4c5ca29b):
|
||||
"""The `MatchedFillResults`:code: Solidity struct.
|
||||
|
||||
Also known as
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0x054ca44e`:py:class:.
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b`:py:class:.
|
||||
"""
|
||||
|
||||
|
||||
class ZeroExTransaction(Tuple0xdabc15fe):
|
||||
"""The `ZeroExTransaction`:code: Solidity struct.
|
||||
|
||||
Also known as
|
||||
`zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe`:py:class:.
|
||||
"""
|
||||
|
||||
|
||||
@ -65,136 +69,11 @@ class OrderInfo(Tuple0xb1e4a1ae):
|
||||
"""
|
||||
|
||||
|
||||
def order_to_jsdict(
|
||||
order: Order,
|
||||
exchange_address="0x0000000000000000000000000000000000000000",
|
||||
signature: str = None,
|
||||
) -> dict:
|
||||
"""Convert a Web3-compatible order struct to a JSON-schema-compatible dict.
|
||||
|
||||
More specifically, do explicit decoding for the `bytes`:code: fields, and
|
||||
convert numerics to strings.
|
||||
|
||||
>>> import pprint
|
||||
>>> pprint.pprint(order_to_jsdict(
|
||||
... {
|
||||
... 'makerAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'takerAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'feeRecipientAddress':
|
||||
... "0x0000000000000000000000000000000000000000",
|
||||
... 'senderAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'makerAssetAmount': 1,
|
||||
... 'takerAssetAmount': 1,
|
||||
... 'makerFee': 0,
|
||||
... 'takerFee': 0,
|
||||
... 'expirationTimeSeconds': 1,
|
||||
... 'salt': 1,
|
||||
... 'makerAssetData': (0).to_bytes(1, byteorder='big') * 20,
|
||||
... 'takerAssetData': (0).to_bytes(1, byteorder='big') * 20,
|
||||
... },
|
||||
... ))
|
||||
{'exchangeAddress': '0x0000000000000000000000000000000000000000',
|
||||
'expirationTimeSeconds': '1',
|
||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAssetAmount': '1',
|
||||
'makerAssetData': '0x0000000000000000000000000000000000000000',
|
||||
'makerFee': '0',
|
||||
'salt': '1',
|
||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAssetAmount': '1',
|
||||
'takerAssetData': '0x0000000000000000000000000000000000000000',
|
||||
'takerFee': '0'}
|
||||
"""
|
||||
jsdict = cast(Dict, copy(order))
|
||||
|
||||
# encode bytes fields
|
||||
jsdict["makerAssetData"] = "0x" + order["makerAssetData"].hex()
|
||||
jsdict["takerAssetData"] = "0x" + order["takerAssetData"].hex()
|
||||
|
||||
jsdict["exchangeAddress"] = exchange_address
|
||||
|
||||
jsdict["expirationTimeSeconds"] = str(order["expirationTimeSeconds"])
|
||||
|
||||
jsdict["makerAssetAmount"] = str(order["makerAssetAmount"])
|
||||
jsdict["takerAssetAmount"] = str(order["takerAssetAmount"])
|
||||
|
||||
jsdict["makerFee"] = str(order["makerFee"])
|
||||
jsdict["takerFee"] = str(order["takerFee"])
|
||||
|
||||
jsdict["salt"] = str(order["salt"])
|
||||
|
||||
if signature is not None:
|
||||
jsdict["signature"] = signature
|
||||
|
||||
assert_valid(jsdict, "/orderSchema")
|
||||
|
||||
return jsdict
|
||||
|
||||
|
||||
def jsdict_to_order(jsdict: dict) -> Order:
|
||||
r"""Convert a JSON-schema-compatible dict order to a Web3-compatible struct.
|
||||
|
||||
More specifically, do explicit encoding of the `bytes`:code: fields, and
|
||||
parse integers from strings.
|
||||
|
||||
>>> import pprint
|
||||
>>> pprint.pprint(jsdict_to_order(
|
||||
... {
|
||||
... 'makerAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'takerAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'feeRecipientAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'senderAddress': "0x0000000000000000000000000000000000000000",
|
||||
... 'makerAssetAmount': "1000000000000000000",
|
||||
... 'takerAssetAmount': "1000000000000000000",
|
||||
... 'makerFee': "0",
|
||||
... 'takerFee': "0",
|
||||
... 'expirationTimeSeconds': "12345",
|
||||
... 'salt': "12345",
|
||||
... 'makerAssetData': "0x0000000000000000000000000000000000000000",
|
||||
... 'takerAssetData': "0x0000000000000000000000000000000000000000",
|
||||
... 'exchangeAddress': "0x0000000000000000000000000000000000000000",
|
||||
... },
|
||||
... ))
|
||||
{'expirationTimeSeconds': 12345,
|
||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAssetAmount': 1000000000000000000,
|
||||
'makerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
'makerFee': 0,
|
||||
'salt': 12345,
|
||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAssetAmount': 1000000000000000000,
|
||||
'takerAssetData': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
'takerFee': 0}
|
||||
""" # noqa: E501 (line too long)
|
||||
assert_valid(jsdict, "/orderSchema")
|
||||
|
||||
order = cast(Order, copy(jsdict))
|
||||
|
||||
order["makerAssetData"] = bytes.fromhex(
|
||||
remove_0x_prefix(jsdict["makerAssetData"])
|
||||
)
|
||||
order["takerAssetData"] = bytes.fromhex(
|
||||
remove_0x_prefix(jsdict["takerAssetData"])
|
||||
)
|
||||
|
||||
order["makerAssetAmount"] = int(jsdict["makerAssetAmount"])
|
||||
order["takerAssetAmount"] = int(jsdict["takerAssetAmount"])
|
||||
|
||||
order["makerFee"] = int(jsdict["makerFee"])
|
||||
order["takerFee"] = int(jsdict["takerFee"])
|
||||
|
||||
order["expirationTimeSeconds"] = int(jsdict["expirationTimeSeconds"])
|
||||
|
||||
order["salt"] = int(jsdict["salt"])
|
||||
|
||||
del order["exchangeAddress"] # type: ignore
|
||||
# silence mypy pending release of
|
||||
# https://github.com/python/mypy/issues/3550
|
||||
|
||||
return order
|
||||
class OrderStatus(Enum): # noqa: D101 # pylint: disable=missing-docstring
|
||||
INVALID = 0
|
||||
INVALID_MAKER_ASSET_AMOUNT = auto()
|
||||
INVALID_TAKER_ASSET_AMOUNT = auto()
|
||||
FILLABLE = auto()
|
||||
EXPIRED = auto()
|
||||
FULLY_FILLED = auto()
|
||||
CANCELLED = auto()
|
||||
|
@ -1,22 +1,40 @@
|
||||
"""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 zero_ex import json_schemas
|
||||
from zero_ex.contract_wrappers.order_conversions import order_to_jsdict
|
||||
|
||||
from ..bases import Validator
|
||||
from .types import order_to_jsdict
|
||||
|
||||
|
||||
class ExchangeValidator(Validator):
|
||||
"""Validate inputs to Exchange methods."""
|
||||
|
||||
def __init__(self, provider: BaseProvider, contract_address: str):
|
||||
def __init__(
|
||||
self,
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
contract_address: str,
|
||||
):
|
||||
"""Initialize the class."""
|
||||
super().__init__(provider, contract_address)
|
||||
super().__init__(web3_or_provider, contract_address)
|
||||
|
||||
web3 = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
web3 = Web3(web3_or_provider)
|
||||
elif isinstance(web3_or_provider, Web3):
|
||||
web3 = web3_or_provider
|
||||
if web3 is None:
|
||||
raise TypeError(
|
||||
"Expected parameter 'web3_or_provider' to be an instance of either"
|
||||
+ " Web3 or BaseProvider"
|
||||
)
|
||||
|
||||
self.contract_address = contract_address
|
||||
self.chain_id = web3.eth.chainId
|
||||
|
||||
def assert_valid(
|
||||
self, method_name: str, parameter_name: str, argument_value: Any
|
||||
@ -30,13 +48,17 @@ class ExchangeValidator(Validator):
|
||||
"""
|
||||
if parameter_name == "order":
|
||||
json_schemas.assert_valid(
|
||||
order_to_jsdict(argument_value, self.contract_address),
|
||||
order_to_jsdict(
|
||||
argument_value, self.chain_id, self.contract_address
|
||||
),
|
||||
"/orderSchema",
|
||||
)
|
||||
|
||||
if parameter_name == "orders":
|
||||
for order in argument_value:
|
||||
json_schemas.assert_valid(
|
||||
order_to_jsdict(order, self.contract_address),
|
||||
order_to_jsdict(
|
||||
order, self.chain_id, self.contract_address
|
||||
),
|
||||
"/orderSchema",
|
||||
)
|
||||
|
@ -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
|
@ -23,7 +23,7 @@ class TxParams:
|
||||
gas: Optional[int] = attr.ib(
|
||||
default=None, converter=attr.converters.optional(int)
|
||||
)
|
||||
gasPrice: Optional[int] = attr.ib(
|
||||
gas_price: Optional[int] = attr.ib(
|
||||
default=None, converter=attr.converters.optional(int)
|
||||
)
|
||||
nonce: Optional[int] = attr.ib(
|
||||
@ -36,4 +36,7 @@ class TxParams:
|
||||
if "from_" in res:
|
||||
res["from"] = res["from_"]
|
||||
del res["from_"]
|
||||
if "gas_price" in res:
|
||||
res["gasPrice"] = res["gas_price"]
|
||||
del res["gas_price"]
|
||||
return res
|
||||
|
@ -26,16 +26,24 @@ class Web3:
|
||||
class middleware_stack:
|
||||
@staticmethod
|
||||
def get(key: str) -> Callable: ...
|
||||
|
||||
def inject(
|
||||
self, middleware_func: object, layer: object
|
||||
) -> None: ...
|
||||
|
||||
...
|
||||
|
||||
middleware_onion: middleware_stack
|
||||
|
||||
class net:
|
||||
version: str
|
||||
...
|
||||
|
||||
|
||||
class eth:
|
||||
class Eth:
|
||||
defaultAccount: str
|
||||
accounts: List[str]
|
||||
chainId: int
|
||||
...
|
||||
|
||||
class account:
|
||||
@ -53,4 +61,7 @@ class Web3:
|
||||
@staticmethod
|
||||
def isAddress(address: str) -> bool: ...
|
||||
...
|
||||
|
||||
eth: Eth
|
||||
|
||||
...
|
||||
|
@ -5,7 +5,7 @@ from eth_utils import to_checksum_address
|
||||
from web3 import Web3
|
||||
|
||||
from zero_ex.order_utils import asset_data_utils
|
||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
from zero_ex.contract_artifacts import abi_by_name
|
||||
|
||||
|
||||
@ -36,14 +36,14 @@ def accounts(web3_eth): # pylint: disable=redefined-outer-name
|
||||
@pytest.fixture(scope="module")
|
||||
def erc20_proxy_address():
|
||||
"""Get the 0x ERC20 Proxy address."""
|
||||
return NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy
|
||||
return network_to_addresses(NetworkId.GANACHE).erc20_proxy
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def weth_asset_data(): # pylint: disable=redefined-outer-name
|
||||
"""Get 0x asset data for Wrapped Ether (WETH) token."""
|
||||
return asset_data_utils.encode_erc20(
|
||||
NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
||||
network_to_addresses(NetworkId.GANACHE).ether_token
|
||||
)
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ def weth_instance(web3_eth): # pylint: disable=redefined-outer-name
|
||||
"""Get an instance of the WrapperEther contract."""
|
||||
return web3_eth.contract(
|
||||
address=to_checksum_address(
|
||||
NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
||||
network_to_addresses(NetworkId.GANACHE).ether_token
|
||||
),
|
||||
abi=abi_by_name("WETH9"),
|
||||
)
|
||||
@ -61,7 +61,7 @@ def weth_instance(web3_eth): # pylint: disable=redefined-outer-name
|
||||
@pytest.fixture(scope="module")
|
||||
def zrx_address():
|
||||
"""Get address of ZRX token for Ganache network."""
|
||||
return NETWORK_TO_ADDRESSES[NetworkId.GANACHE].zrx_token
|
||||
return network_to_addresses(NetworkId.GANACHE).zrx_token
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
from zero_ex.contract_wrappers.bases import ContractMethod
|
||||
|
||||
|
||||
@ -10,6 +10,6 @@ from zero_ex.contract_wrappers.bases import ContractMethod
|
||||
def contract_wrapper(ganache_provider):
|
||||
"""Get a ContractMethod instance for testing."""
|
||||
return ContractMethod(
|
||||
provider=ganache_provider,
|
||||
contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token,
|
||||
web3_or_provider=ganache_provider,
|
||||
contract_address=network_to_addresses(NetworkId.GANACHE).ether_token,
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
from zero_ex.contract_wrappers import TxParams
|
||||
from zero_ex.contract_wrappers.erc20_token import ERC20Token
|
||||
|
||||
@ -16,7 +16,7 @@ MAX_ALLOWANCE = int("{:.0f}".format(Decimal(2) ** 256 - 1))
|
||||
def erc20_wrapper(ganache_provider):
|
||||
"""Get an instance of ERC20Token wrapper class for testing."""
|
||||
return ERC20Token(
|
||||
ganache_provider, NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token
|
||||
ganache_provider, network_to_addresses(NetworkId.GANACHE).ether_token
|
||||
)
|
||||
|
||||
|
||||
|
@ -5,20 +5,24 @@ import random
|
||||
import pytest
|
||||
from eth_utils import remove_0x_prefix
|
||||
|
||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
from zero_ex.contract_wrappers import TxParams
|
||||
from zero_ex.contract_wrappers.exchange import Exchange
|
||||
from zero_ex.contract_wrappers.exchange.types import Order
|
||||
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")
|
||||
def exchange_wrapper(ganache_provider):
|
||||
"""Get an Exchange wrapper instance."""
|
||||
return Exchange(
|
||||
provider=ganache_provider,
|
||||
contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange,
|
||||
web3_or_provider=ganache_provider,
|
||||
contract_address=network_to_addresses(NetworkId.GANACHE).exchange,
|
||||
)
|
||||
|
||||
|
||||
@ -43,6 +47,8 @@ def create_test_order(
|
||||
salt=random.randint(1, 1000000000),
|
||||
makerAssetData=maker_asset_data,
|
||||
takerAssetData=taker_asset_data,
|
||||
makerFeeAssetData=asset_data_utils.encode_erc20("0x" + "00" * 20),
|
||||
takerFeeAssetData=asset_data_utils.encode_erc20("0x" + "00" * 20),
|
||||
)
|
||||
return order
|
||||
|
||||
@ -67,16 +73,29 @@ def test_exchange_wrapper__fill_order(
|
||||
exchange_wrapper, # pylint: disable=redefined-outer-name
|
||||
ganache_provider,
|
||||
weth_asset_data,
|
||||
zrx_asset_data,
|
||||
):
|
||||
"""Test filling an order."""
|
||||
taker = accounts[0]
|
||||
maker = accounts[1]
|
||||
exchange_address = exchange_wrapper.contract_address
|
||||
order = create_test_order(maker, 1, weth_asset_data, 1, weth_asset_data)
|
||||
order = create_test_order(maker, 1, weth_asset_data, 1, zrx_asset_data)
|
||||
order_hash = generate_order_hash_hex(
|
||||
order=order, exchange_address=exchange_address
|
||||
order=order, exchange_address=exchange_address, chain_id=1337
|
||||
)
|
||||
order_signature = sign_hash_to_bytes(ganache_provider, maker, order_hash)
|
||||
order_signature = sign_hash(ganache_provider, maker, order_hash)
|
||||
|
||||
fill_results = exchange_wrapper.fill_order.call(
|
||||
order=order,
|
||||
taker_asset_fill_amount=order["takerAssetAmount"],
|
||||
signature=order_signature,
|
||||
tx_params=TxParams(from_=taker),
|
||||
)
|
||||
assert fill_results[0] == 1
|
||||
assert fill_results[1] == 1
|
||||
assert fill_results[2] == 0
|
||||
assert fill_results[3] == 0
|
||||
assert fill_results[4] == 0
|
||||
|
||||
tx_hash = exchange_wrapper.fill_order.send_transaction(
|
||||
order=order,
|
||||
@ -107,11 +126,13 @@ def test_exchange_wrapper__batch_fill_orders(
|
||||
orders.append(order_1)
|
||||
orders.append(order_2)
|
||||
order_hashes = [
|
||||
generate_order_hash_hex(order=order, exchange_address=exchange_address)
|
||||
generate_order_hash_hex(
|
||||
order=order, exchange_address=exchange_address, chain_id=1337
|
||||
)
|
||||
for order in orders
|
||||
]
|
||||
order_signatures = [
|
||||
sign_hash_to_bytes(ganache_provider, maker, order_hash)
|
||||
sign_hash(ganache_provider, maker, order_hash)
|
||||
for order_hash in order_hashes
|
||||
]
|
||||
taker_amounts = [order["takerAssetAmount"] for order in orders]
|
||||
@ -128,3 +149,20 @@ def test_exchange_wrapper__batch_fill_orders(
|
||||
assert_fill_log(
|
||||
fill_events[index].args, maker, taker, order, order_hashes[index]
|
||||
)
|
||||
|
||||
|
||||
def test_two_instantiations_with_web3_objects(web3_instance):
|
||||
"""Test that instantiating two Exchange objects doesn't raise.
|
||||
|
||||
When instantiating an Exchange object with a web3 client (rather than a
|
||||
provider) there was a bug encountered where web3.py was giving an error
|
||||
when trying to install the rich-revert-handling middleware on the web3
|
||||
client, an error saying "can't install this same middleware instance
|
||||
again." Test that that bug isn't occurring.
|
||||
"""
|
||||
exchange = Exchange( # pylint: disable=unused-variable
|
||||
web3_instance, network_to_addresses(NetworkId.GANACHE).exchange
|
||||
)
|
||||
exchange2 = Exchange( # pylint: disable=unused-variable
|
||||
web3_instance, network_to_addresses(NetworkId.GANACHE).exchange
|
||||
)
|
||||
|
@ -20,6 +20,7 @@ commands =
|
||||
pytest test
|
||||
|
||||
[testenv:run_tests_against_deployment]
|
||||
setenv = PY_IGNORE_IMPORTMISMATCH = 1
|
||||
commands =
|
||||
pip install 0x-contract-wrappers
|
||||
pytest test
|
||||
pip install 0x-contract-wrappers[dev]
|
||||
pytest --doctest-modules src test
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 1.1.1 - TBD
|
||||
|
||||
- Removed dev dependency on package `0x-contract-wrappers`
|
||||
|
||||
## 1.1.0 - 2019-08-09
|
||||
|
||||
- Added `verifyingContractAddress` to `/zeroExTransactionSchema`.
|
||||
|
@ -2,12 +2,16 @@
|
||||
|
||||
"""setuptools module for json_schemas package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import distutils.command.build_py
|
||||
from distutils.command.clean import clean
|
||||
import subprocess # nosec
|
||||
from shutil import copytree, rmtree
|
||||
from os import environ, path
|
||||
from sys import argv
|
||||
from sys import argv, exit # pylint: disable=redefined-builtin
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
from setuptools.command.test import test as TestCommand
|
||||
@ -139,7 +143,7 @@ with open("README.md", "r") as file_handle:
|
||||
|
||||
setup(
|
||||
name="0x-json-schemas",
|
||||
version="1.1.0",
|
||||
version="1.1.1",
|
||||
description="JSON schemas for 0x applications",
|
||||
long_description=README_MD,
|
||||
long_description_content_type="text/markdown",
|
||||
@ -162,11 +166,11 @@ setup(
|
||||
extras_require={
|
||||
"dev": [
|
||||
"0x-contract-addresses",
|
||||
"0x-contract-wrappers",
|
||||
"bandit",
|
||||
"black",
|
||||
"coverage",
|
||||
"coveralls",
|
||||
"eth_utils",
|
||||
"mypy",
|
||||
"mypy_extensions",
|
||||
"pycodestyle",
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -60,36 +60,38 @@ def assert_valid(data: Mapping, schema_id: str) -> None:
|
||||
Raises an exception if validation fails.
|
||||
|
||||
>>> from zero_ex.json_schemas import assert_valid
|
||||
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
>>> from zero_ex.contract_wrappers.exchange.types import (
|
||||
... Order, order_to_jsdict
|
||||
... )
|
||||
>>> from zero_ex.order_utils import asset_data_utils
|
||||
>>> from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
>>> from eth_utils import remove_0x_prefix
|
||||
>>> import random
|
||||
>>> from datetime import datetime, timedelta
|
||||
>>> example_order = Order(
|
||||
... makerAddress='0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
||||
... takerAddress='0x0000000000000000000000000000000000000000',
|
||||
... senderAddress='0x0000000000000000000000000000000000000000',
|
||||
... exchangeAddress='0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
|
||||
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
|
||||
... makerAssetData=asset_data_utils.encode_erc20(
|
||||
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].zrx_token
|
||||
... ),
|
||||
... takerAssetData=asset_data_utils.encode_erc20(
|
||||
... NETWORK_TO_ADDRESSES[NetworkId.MAINNET].ether_token
|
||||
... ),
|
||||
... salt=random.randint(1, 100000000000000000),
|
||||
... makerFee=0,
|
||||
... takerFee=0,
|
||||
... makerAssetAmount=1000000000000000000,
|
||||
... takerAssetAmount=500000000000000000000,
|
||||
... expirationTimeSeconds=round(
|
||||
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||
... )
|
||||
>>> assert_valid(
|
||||
... {'makerAddress': '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
||||
... 'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
... 'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
... 'exchangeAddress': '0x4f833a24e1f95d70f028921e27040ca56e09ab0b',
|
||||
... 'feeRecipientAddress': (
|
||||
... '0x0000000000000000000000000000000000000000'
|
||||
... ),
|
||||
... 'makerAssetData': (
|
||||
... network_to_addresses(NetworkId.MAINNET).zrx_token
|
||||
... ),
|
||||
... 'takerAssetData': (
|
||||
... network_to_addresses(NetworkId.MAINNET).ether_token
|
||||
... ),
|
||||
... 'salt': random.randint(1, 100000000000000000),
|
||||
... 'makerFee': 0,
|
||||
... 'makerFeeAssetData': '0x' + '00'*20,
|
||||
... 'takerFee': 0,
|
||||
... 'takerFeeAssetData': '0x' + '00'*20,
|
||||
... 'makerAssetAmount': 1000000000000000000,
|
||||
... 'takerAssetAmount': 500000000000000000000,
|
||||
... 'expirationTimeSeconds': round(
|
||||
... (datetime.utcnow() + timedelta(days=1)).timestamp()
|
||||
... ),
|
||||
... 'chainId': 50
|
||||
... },
|
||||
... "/orderSchema"
|
||||
... )
|
||||
>>> assert_valid(order_to_jsdict(example_order), "/orderSchema")
|
||||
"""
|
||||
_, schema = _LOCAL_RESOLVER.resolve(schema_id)
|
||||
jsonschema.validate(data, schema, resolver=_LOCAL_RESOLVER)
|
||||
|
@ -15,11 +15,14 @@ EMPTY_ORDER = {
|
||||
"takerAssetData": NULL_ADDRESS,
|
||||
"salt": "0",
|
||||
"makerFee": "0",
|
||||
"makerFeeAssetData": NULL_ADDRESS,
|
||||
"takerFee": "0",
|
||||
"takerFeeAssetData": NULL_ADDRESS,
|
||||
"makerAssetAmount": "0",
|
||||
"takerAssetAmount": "0",
|
||||
"expirationTimeSeconds": "0",
|
||||
"exchangeAddress": NULL_ADDRESS,
|
||||
"chainId": 50,
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,6 +20,7 @@ commands =
|
||||
pytest test
|
||||
|
||||
[testenv:run_tests_against_deployment]
|
||||
setenv = PY_IGNORE_IMPORTMISMATCH = 1
|
||||
commands =
|
||||
pip install 0x-json-schemas
|
||||
pytest test
|
||||
pip install 0x-json-schemas[dev]
|
||||
pytest --doctest-modules src test
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0 - 2019-04-30
|
||||
## 1.0.0 - TBD
|
||||
|
||||
- Initial release.
|
||||
|
@ -2,10 +2,14 @@
|
||||
|
||||
"""setuptools module for middlewares package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import subprocess # nosec
|
||||
from shutil import rmtree
|
||||
from os import environ, path
|
||||
from sys import argv
|
||||
from sys import argv, exit # pylint: disable=redefined-builtin
|
||||
|
||||
from distutils.command.clean import clean
|
||||
import distutils.command.build_py
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -3,25 +3,21 @@
|
||||
from eth_utils import to_checksum_address
|
||||
from web3 import Web3, HTTPProvider
|
||||
|
||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
from zero_ex.middlewares.local_message_signer import (
|
||||
construct_local_message_signer,
|
||||
)
|
||||
from zero_ex.order_utils import (
|
||||
generate_order_hash_hex,
|
||||
is_valid_signature,
|
||||
sign_hash,
|
||||
)
|
||||
from zero_ex.order_utils import generate_order_hash_hex, sign_hash
|
||||
|
||||
|
||||
def test_local_message_signer__sign_order():
|
||||
"""Test signing order with the local_message_signer middleware"""
|
||||
expected_signature = (
|
||||
"0x1cd17d75b891accf16030c572a64cf9e7955de63bcafa5b084439cec630ade2d7"
|
||||
"c00f47a2f4d5b6a4508267bf4b8527100bd97cf1af9984c0a58e42d25b13f4f0a03"
|
||||
"0x1c8bdfbb3ce3ed0f38c5a358a7f49ad5f21ea9857224c2fe98c458f2fa25551d4"
|
||||
"d6db0157d9dfe9f9fadb8dedabb7786352843357f4ec8d0fbcbeeb619b1091f5803"
|
||||
)
|
||||
address = "0x5409ED021D9299bf6814279A6A1411A7e866A631"
|
||||
exchange = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange
|
||||
exchange = network_to_addresses(NetworkId.GANACHE).exchange
|
||||
private_key = (
|
||||
"f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d"
|
||||
)
|
||||
@ -36,7 +32,9 @@ def test_local_message_signer__sign_order():
|
||||
"senderAddress": "0x0000000000000000000000000000000000000000",
|
||||
"feeRecipientAddress": "0x0000000000000000000000000000000000000000",
|
||||
"makerAssetData": (b"\x00") * 20,
|
||||
"makerFeeAssetData": (b"\x00") * 20,
|
||||
"takerAssetData": (b"\x00") * 20,
|
||||
"takerFeeAssetData": (b"\x00") * 20,
|
||||
"salt": 0,
|
||||
"makerFee": 0,
|
||||
"takerFee": 0,
|
||||
@ -44,8 +42,11 @@ def test_local_message_signer__sign_order():
|
||||
"takerAssetAmount": 0,
|
||||
"expirationTimeSeconds": 0,
|
||||
}
|
||||
order_hash = generate_order_hash_hex(order, exchange)
|
||||
signature = sign_hash(ganache, to_checksum_address(address), order_hash)
|
||||
assert signature == expected_signature
|
||||
is_valid = is_valid_signature(ganache, order_hash, signature, address)[0]
|
||||
assert is_valid is True
|
||||
assert (
|
||||
sign_hash(
|
||||
web3_instance,
|
||||
to_checksum_address(address),
|
||||
generate_order_hash_hex(order, exchange, chain_id=1337),
|
||||
)
|
||||
== expected_signature
|
||||
)
|
||||
|
@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 4.0.0 - TBD
|
||||
|
||||
- Upgraded to protocol version 3.
|
||||
- `is_valid_signature()` now returns just a boolean. (Formerly, it returned a tuple consisting of the boolean and a reason string.)
|
||||
- Allow `sign_hash()` to be called with EITHER a Web3.py `BaseProvider` OR an already-instantiated `Web3` client object.
|
||||
|
||||
## 3.0.1 - 2019-08-09
|
||||
|
||||
- Fixed dependencies: changed `deprecated` from being an extras_require["dev"] dependency to being an install_requires dependency, since it's required not just for doc generation but also just to import the package.
|
||||
|
@ -2,11 +2,15 @@
|
||||
|
||||
"""setuptools module for order_utils package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import subprocess # nosec
|
||||
from shutil import rmtree
|
||||
from os import environ, path
|
||||
from pathlib import Path
|
||||
from sys import argv
|
||||
from sys import argv, exit # pylint: disable=redefined-builtin
|
||||
|
||||
from distutils.command.clean import clean
|
||||
import distutils.command.build_py
|
||||
@ -152,7 +156,7 @@ with open("README.md", "r") as file_handle:
|
||||
|
||||
setup(
|
||||
name="0x-order-utils",
|
||||
version="3.0.1",
|
||||
version="4.0.0",
|
||||
description="Order utilities for 0x applications",
|
||||
long_description=README_MD,
|
||||
long_description_content_type="text/markdown",
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -18,9 +18,9 @@ just this purpose. To start it:
|
||||
|
||||
from enum import auto, Enum
|
||||
import json
|
||||
from typing import Tuple
|
||||
from pkg_resources import resource_string
|
||||
from typing import cast, Tuple, Union
|
||||
|
||||
from pkg_resources import resource_string
|
||||
from mypy_extensions import TypedDict
|
||||
|
||||
from eth_utils import keccak, remove_0x_prefix, to_bytes, to_checksum_address
|
||||
@ -29,9 +29,11 @@ import web3.exceptions
|
||||
from web3.providers.base import BaseProvider
|
||||
from web3.contract import Contract
|
||||
|
||||
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
|
||||
from zero_ex.contract_addresses import network_to_addresses, NetworkId
|
||||
import zero_ex.contract_artifacts
|
||||
from zero_ex.contract_wrappers.exchange.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 (
|
||||
assert_is_address,
|
||||
assert_is_hex_string,
|
||||
@ -48,13 +50,18 @@ class _Constants:
|
||||
eip191_header = b"\x19\x01"
|
||||
|
||||
eip712_domain_separator_schema_hash = keccak(
|
||||
b"EIP712Domain(string name,string version,address verifyingContract)"
|
||||
b"EIP712Domain("
|
||||
+ b"string name,"
|
||||
+ b"string version,"
|
||||
+ b"uint256 chainId,"
|
||||
+ b"address verifyingContract"
|
||||
+ b")"
|
||||
)
|
||||
|
||||
eip712_domain_struct_header = (
|
||||
eip712_domain_separator_schema_hash
|
||||
+ keccak(b"0x Protocol")
|
||||
+ keccak(b"2")
|
||||
+ keccak(b"3.0.0")
|
||||
)
|
||||
|
||||
eip712_order_schema_hash = keccak(
|
||||
@ -70,7 +77,9 @@ class _Constants:
|
||||
+ b"uint256 expirationTimeSeconds,"
|
||||
+ b"uint256 salt,"
|
||||
+ b"bytes makerAssetData,"
|
||||
+ b"bytes takerAssetData"
|
||||
+ b"bytes takerAssetData,"
|
||||
+ b"bytes makerFeeAssetData,"
|
||||
+ b"bytes takerFeeAssetData"
|
||||
+ b")"
|
||||
)
|
||||
|
||||
@ -87,7 +96,9 @@ class _Constants:
|
||||
N_SIGNATURE_TYPES = auto()
|
||||
|
||||
|
||||
def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
|
||||
def generate_order_hash_hex(
|
||||
order: Order, exchange_address: str, chain_id: int
|
||||
) -> str:
|
||||
"""Calculate the hash of the given order as a hexadecimal string.
|
||||
|
||||
:param order: The order to be hashed. Must conform to `the 0x order JSON schema <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.
|
||||
:returns: A string, of ASCII hex digits, representing the order hash.
|
||||
|
||||
Inputs and expected result below were copied from
|
||||
@0x/order-utils/test/order_hash_test.ts
|
||||
|
||||
>>> generate_order_hash_hex(
|
||||
... Order(
|
||||
... makerAddress="0x0000000000000000000000000000000000000000",
|
||||
... takerAddress="0x0000000000000000000000000000000000000000",
|
||||
... feeRecipientAddress="0x0000000000000000000000000000000000000000",
|
||||
... senderAddress="0x0000000000000000000000000000000000000000",
|
||||
... makerAssetAmount="1000000000000000000",
|
||||
... takerAssetAmount="1000000000000000000",
|
||||
... makerAssetAmount="0",
|
||||
... takerAssetAmount="0",
|
||||
... makerFee="0",
|
||||
... takerFee="0",
|
||||
... expirationTimeSeconds="12345",
|
||||
... salt="12345",
|
||||
... expirationTimeSeconds="0",
|
||||
... salt="0",
|
||||
... makerAssetData=((0).to_bytes(1, byteorder='big') * 20),
|
||||
... takerAssetData=((0).to_bytes(1, byteorder='big') * 20),
|
||||
... makerFeeAssetData=((0).to_bytes(1, byteorder='big') * 20),
|
||||
... takerFeeAssetData=((0).to_bytes(1, byteorder='big') * 20),
|
||||
... ),
|
||||
... exchange_address="0x0000000000000000000000000000000000000000",
|
||||
... exchange_address="0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
|
||||
... chain_id=50
|
||||
... )
|
||||
'55eaa6ec02f3224d30873577e9ddd069a288c16d6fb407210eecbc501fa76692'
|
||||
'331cb7e07a757bae130702da6646c26531798c92bcfaf671817268fd2c188531'
|
||||
""" # noqa: E501 (line too long)
|
||||
assert_is_address(exchange_address, "exchange_address")
|
||||
assert_valid(order_to_jsdict(order, exchange_address), "/orderSchema")
|
||||
assert_valid(
|
||||
order_to_jsdict(order, chain_id, exchange_address), "/orderSchema"
|
||||
)
|
||||
|
||||
def pad_20_bytes_to_32(twenty_bytes: bytes):
|
||||
return bytes(12) + twenty_bytes
|
||||
@ -125,9 +144,17 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
|
||||
|
||||
eip712_domain_struct_hash = keccak(
|
||||
_Constants.eip712_domain_struct_header
|
||||
+ int_to_32_big_endian_bytes(int(chain_id))
|
||||
+ pad_20_bytes_to_32(to_bytes(hexstr=exchange_address))
|
||||
)
|
||||
|
||||
def ensure_bytes(str_or_bytes: Union[str, bytes]) -> bytes:
|
||||
return (
|
||||
to_bytes(hexstr=cast(bytes, str_or_bytes))
|
||||
if isinstance(str_or_bytes, str)
|
||||
else str_or_bytes
|
||||
)
|
||||
|
||||
eip712_order_struct_hash = keccak(
|
||||
_Constants.eip712_order_schema_hash
|
||||
+ pad_20_bytes_to_32(to_bytes(hexstr=order["makerAddress"]))
|
||||
@ -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["expirationTimeSeconds"]))
|
||||
+ int_to_32_big_endian_bytes(int(order["salt"]))
|
||||
+ keccak(to_bytes(hexstr=order["makerAssetData"].hex()))
|
||||
+ keccak(to_bytes(hexstr=order["takerAssetData"].hex()))
|
||||
+ keccak(ensure_bytes(order["makerAssetData"]))
|
||||
+ keccak(ensure_bytes(order["takerAssetData"]))
|
||||
+ keccak(ensure_bytes(order["makerFeeAssetData"]))
|
||||
+ keccak(ensure_bytes(order["takerFeeAssetData"]))
|
||||
)
|
||||
|
||||
return keccak(
|
||||
@ -153,7 +182,7 @@ def generate_order_hash_hex(order: Order, exchange_address: str) -> str:
|
||||
|
||||
def is_valid_signature(
|
||||
provider: BaseProvider, data: str, signature: str, signer_address: str
|
||||
) -> Tuple[bool, str]:
|
||||
) -> bool:
|
||||
"""Check the validity of the supplied signature.
|
||||
|
||||
Check if the supplied `signature`:code: corresponds to signing `data`:code:
|
||||
@ -173,42 +202,25 @@ def is_valid_signature(
|
||||
... '0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403',
|
||||
... '0x5409ed021d9299bf6814279a6a1411a7e866a631',
|
||||
... )
|
||||
(True, '')
|
||||
True
|
||||
""" # noqa: E501 (line too long)
|
||||
assert_is_provider(provider, "provider")
|
||||
assert_is_hex_string(data, "data")
|
||||
assert_is_hex_string(signature, "signature")
|
||||
assert_is_address(signer_address, "signer_address")
|
||||
|
||||
web3_instance = Web3(provider)
|
||||
# false positive from pylint: disable=no-member
|
||||
contract_address = NETWORK_TO_ADDRESSES[
|
||||
NetworkId(int(web3_instance.net.version))
|
||||
].exchange
|
||||
# false positive from pylint: disable=no-member
|
||||
contract: Contract = web3_instance.eth.contract(
|
||||
address=to_checksum_address(contract_address),
|
||||
abi=zero_ex.contract_artifacts.abi_by_name("Exchange"),
|
||||
return Exchange(
|
||||
provider,
|
||||
network_to_addresses(
|
||||
NetworkId(
|
||||
int(Web3(provider).net.version) # pylint: disable=no-member
|
||||
)
|
||||
).exchange,
|
||||
).is_valid_hash_signature.call(
|
||||
bytes.fromhex(remove_0x_prefix(data)),
|
||||
to_checksum_address(signer_address),
|
||||
bytes.fromhex(remove_0x_prefix(signature)),
|
||||
)
|
||||
try:
|
||||
return (
|
||||
contract.functions.isValidSignature(
|
||||
data, to_checksum_address(signer_address), signature
|
||||
).call(),
|
||||
"",
|
||||
)
|
||||
except web3.exceptions.BadFunctionCallOutput as exception:
|
||||
known_revert_reasons = [
|
||||
"LENGTH_GREATER_THAN_0_REQUIRED",
|
||||
"SIGNATURE_ILLEGAL",
|
||||
"SIGNATURE_UNSUPPORTED",
|
||||
"LENGTH_0_REQUIRED",
|
||||
"LENGTH_65_REQUIRED",
|
||||
]
|
||||
for known_revert_reason in known_revert_reasons:
|
||||
if known_revert_reason in str(exception):
|
||||
return (False, known_revert_reason)
|
||||
return (False, f"Unknown: {exception}")
|
||||
|
||||
|
||||
class ECSignature(TypedDict):
|
||||
@ -272,11 +284,14 @@ def _convert_ec_signature_to_vrs_hex(signature: ECSignature) -> str:
|
||||
|
||||
|
||||
def sign_hash(
|
||||
provider: BaseProvider, signer_address: str, hash_hex: str
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
signer_address: str,
|
||||
hash_hex: str,
|
||||
) -> str:
|
||||
"""Sign a message with the given hash, and return the signature.
|
||||
|
||||
:param provider: A Web3 provider.
|
||||
:param web3_or_provider: Either an instance of `web3.Web3`:code: or
|
||||
`web3.providers.base.BaseProvider`:code:
|
||||
:param signer_address: The address of the signing account.
|
||||
:param hash_hex: A hex string representing the hash, like that returned
|
||||
from `generate_order_hash_hex()`:code:.
|
||||
@ -290,11 +305,20 @@ def sign_hash(
|
||||
... )
|
||||
'0x1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03'
|
||||
""" # noqa: E501 (line too long)
|
||||
assert_is_provider(provider, "provider")
|
||||
web3_instance = None
|
||||
if isinstance(web3_or_provider, BaseProvider):
|
||||
web3_instance = Web3(web3_or_provider)
|
||||
elif isinstance(web3_or_provider, Web3):
|
||||
web3_instance = web3_or_provider
|
||||
else:
|
||||
raise TypeError(
|
||||
"Expected parameter 'web3_or_provider' to be an instance of either"
|
||||
+ " Web3 or BaseProvider"
|
||||
)
|
||||
|
||||
assert_is_address(signer_address, "signer_address")
|
||||
assert_is_hex_string(hash_hex, "hash_hex")
|
||||
|
||||
web3_instance = Web3(provider)
|
||||
# false positive from pylint: disable=no-member
|
||||
signature = web3_instance.eth.sign( # type: ignore
|
||||
signer_address, hexstr=hash_hex.replace("0x", "")
|
||||
@ -319,8 +343,11 @@ def sign_hash(
|
||||
).hex()
|
||||
)
|
||||
|
||||
(valid, _) = is_valid_signature(
|
||||
provider, hash_hex, signature_as_vrst_hex, signer_address
|
||||
valid = is_valid_signature(
|
||||
web3_instance.provider,
|
||||
hash_hex,
|
||||
signature_as_vrst_hex,
|
||||
signer_address,
|
||||
)
|
||||
|
||||
if valid is True:
|
||||
@ -334,21 +361,26 @@ def sign_hash(
|
||||
1, byteorder="big"
|
||||
).hex()
|
||||
)
|
||||
(valid, _) = is_valid_signature(
|
||||
provider, hash_hex, signature_as_vrst_hex, signer_address
|
||||
valid = is_valid_signature(
|
||||
web3_instance.provider,
|
||||
hash_hex,
|
||||
signature_as_vrst_hex,
|
||||
signer_address,
|
||||
)
|
||||
|
||||
if valid is True:
|
||||
return signature_as_vrst_hex
|
||||
|
||||
raise RuntimeError(
|
||||
"Signature returned from web3 provider is in an unknown format."
|
||||
+ " Attempted to parse as RSV and as VRS."
|
||||
"Signature returned from web3 provider is in an unknown format. "
|
||||
+ "Signature was: {signature}"
|
||||
)
|
||||
|
||||
|
||||
def sign_hash_to_bytes(
|
||||
provider: BaseProvider, signer_address: str, hash_hex: str
|
||||
web3_or_provider: Union[Web3, BaseProvider],
|
||||
signer_address: str,
|
||||
hash_hex: str,
|
||||
) -> bytes:
|
||||
"""Sign a message with the given hash, and return the signature.
|
||||
|
||||
@ -361,5 +393,5 @@ def sign_hash_to_bytes(
|
||||
'1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03'
|
||||
""" # noqa: E501 (line too long)
|
||||
return remove_0x_prefix(
|
||||
sign_hash(provider, signer_address, hash_hex)
|
||||
sign_hash(web3_or_provider, signer_address, hash_hex)
|
||||
).encode(encoding="utf_8")
|
||||
|
@ -34,4 +34,6 @@ class Web3:
|
||||
...
|
||||
...
|
||||
|
||||
provider: BaseProvider
|
||||
|
||||
...
|
||||
|
@ -6,7 +6,7 @@ from zero_ex.order_utils import generate_order_hash_hex
|
||||
def test_get_order_hash_hex__empty_order():
|
||||
"""Test the hashing of an uninitialized order."""
|
||||
expected_hash_hex = (
|
||||
"faa49b35faeb9197e9c3ba7a52075e6dad19739549f153b77dfcf59408a4b422"
|
||||
"331cb7e07a757bae130702da6646c26531798c92bcfaf671817268fd2c188531"
|
||||
)
|
||||
actual_hash_hex = generate_order_hash_hex(
|
||||
{
|
||||
@ -18,6 +18,8 @@ def test_get_order_hash_hex__empty_order():
|
||||
),
|
||||
"makerAssetData": (b"\x00") * 20,
|
||||
"takerAssetData": (b"\x00") * 20,
|
||||
"makerFeeAssetData": (b"\x00") * 20,
|
||||
"takerFeeAssetData": (b"\x00") * 20,
|
||||
"salt": 0,
|
||||
"makerFee": 0,
|
||||
"takerFee": 0,
|
||||
@ -25,6 +27,7 @@ def test_get_order_hash_hex__empty_order():
|
||||
"takerAssetAmount": 0,
|
||||
"expirationTimeSeconds": 0,
|
||||
},
|
||||
"0x0000000000000000000000000000000000000000",
|
||||
"0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
|
||||
50,
|
||||
)
|
||||
assert actual_hash_hex == expected_hash_hex
|
||||
|
@ -3,6 +3,10 @@
|
||||
import pytest
|
||||
from web3 import Web3
|
||||
|
||||
from zero_ex.contract_wrappers.exchange.exceptions import (
|
||||
SignatureError,
|
||||
SignatureErrorCodes,
|
||||
)
|
||||
from zero_ex.order_utils import is_valid_signature, sign_hash_to_bytes
|
||||
|
||||
|
||||
@ -117,28 +121,49 @@ def test_is_valid_signature__unsupported_sig_types():
|
||||
|
||||
To induce this error, the last byte of the signature is tweaked from 03 to
|
||||
ff."""
|
||||
(is_valid, reason) = is_valid_signature(
|
||||
Web3.HTTPProvider("http://127.0.0.1:8545"),
|
||||
"0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0",
|
||||
"0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc334"
|
||||
+ "0349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254ff",
|
||||
"0x5409ed021d9299bf6814279a6a1411a7e866a631",
|
||||
)
|
||||
assert is_valid is False
|
||||
assert reason == "SIGNATURE_UNSUPPORTED"
|
||||
try:
|
||||
is_valid_signature(
|
||||
Web3.HTTPProvider("http://127.0.0.1:8545"),
|
||||
"0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222"
|
||||
+ "b0",
|
||||
"0x1B61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351b"
|
||||
+ "c3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace"
|
||||
+ "225401",
|
||||
"0x5409ed021d9299bf6814279a6a1411a7e866a631",
|
||||
)
|
||||
except SignatureError as signature_error:
|
||||
assert (
|
||||
signature_error.errorCode
|
||||
== SignatureErrorCodes.INVALID_LENGTH.value
|
||||
)
|
||||
else:
|
||||
pytest.fail("Expected exception")
|
||||
|
||||
|
||||
def test_sign_hash_to_bytes__golden_path():
|
||||
def test_sign_hash_to_bytes_and_validate__golden_path():
|
||||
"""Test the happy path through sign_hash_to_bytes()."""
|
||||
provider = Web3.HTTPProvider("http://127.0.0.1:8545")
|
||||
signature = sign_hash_to_bytes(
|
||||
provider,
|
||||
Web3( # pylint: disable=no-member
|
||||
provider
|
||||
).geth.personal.listAccounts()[0],
|
||||
"0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004",
|
||||
|
||||
signing_address = Web3( # pylint: disable=no-member
|
||||
provider
|
||||
).geth.personal.listAccounts()[0]
|
||||
|
||||
order_hash_hex = (
|
||||
"0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004"
|
||||
)
|
||||
|
||||
signature = sign_hash_to_bytes(provider, signing_address, order_hash_hex)
|
||||
|
||||
assert (
|
||||
signature
|
||||
== b"1b117902c86dfb95fe0d1badd983ee166ad259b27acb220174cbb4460d872871137feabdfe76e05924b484789f79af4ee7fa29ec006cedce1bbf369320d034e10b03" # noqa: E501 (line too long)
|
||||
)
|
||||
|
||||
is_valid = is_valid_signature(
|
||||
Web3.HTTPProvider("http://127.0.0.1:8545"),
|
||||
order_hash_hex,
|
||||
signature.decode("utf-8"),
|
||||
signing_address,
|
||||
)
|
||||
|
||||
assert is_valid is True
|
||||
|
@ -20,6 +20,7 @@ commands =
|
||||
pytest test
|
||||
|
||||
[testenv:run_tests_against_deployment]
|
||||
setenv = PY_IGNORE_IMPORTMISMATCH = 1
|
||||
commands =
|
||||
pip install 0x-order-utils
|
||||
pytest test
|
||||
pip install 0x-order-utils[dev]
|
||||
pytest --doctest-modules src test
|
||||
|
@ -22,7 +22,7 @@ $ ./parallel pip uninstall $(basename $(pwd))
|
||||
|
||||
from concurrent.futures import ProcessPoolExecutor, wait
|
||||
from os import chdir
|
||||
from subprocess import CalledProcessError, check_output
|
||||
from subprocess import CalledProcessError, check_output, STDOUT
|
||||
from sys import argv
|
||||
|
||||
PACKAGES = [
|
||||
@ -38,11 +38,14 @@ PACKAGES = [
|
||||
def run_cmd_on_package(package: str):
|
||||
"""cd to the package dir, ./setup.py lint, cd .."""
|
||||
chdir(package)
|
||||
command = f"{' '.join(argv[1:])}"
|
||||
try:
|
||||
check_output(f"{' '.join(argv[1:])}".split())
|
||||
check_output(command.split(), stderr=STDOUT)
|
||||
except CalledProcessError as error:
|
||||
print(f"standard output from command:\n{error.output.decode('utf-8')}")
|
||||
raise RuntimeError(f"Above exception raised in {package}, ") from error
|
||||
raise RuntimeError(
|
||||
f"Failure return code received from command `{command}` in package"
|
||||
+ f" {package}, which produced the following output:\n"
|
||||
+ f"{error.output.decode('utf-8')}") from error
|
||||
finally:
|
||||
chdir("..")
|
||||
|
||||
|
@ -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
|
@ -12,6 +12,7 @@ from sys import argv
|
||||
|
||||
PACKAGES = [
|
||||
"contract_wrappers",
|
||||
"contract_addresses",
|
||||
"contract_artifacts",
|
||||
"json_schemas",
|
||||
]
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 4.0.0 - TBD
|
||||
|
||||
- Migrated from v2 to v3 of the 0x protocol.
|
||||
|
||||
## 3.0.0 - 2019-08-08
|
||||
|
||||
- Migrated from v4 to v5 of Web3.py.
|
||||
|
@ -3,10 +3,15 @@
|
||||
|
||||
"""setuptools module for sra_client package."""
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
# we import things outside of top-level because 3rd party libs may not yet be
|
||||
# installed when you invoke this script
|
||||
|
||||
import subprocess # nosec
|
||||
import distutils.command.build_py
|
||||
from distutils.command.clean import clean
|
||||
from shutil import rmtree
|
||||
from sys import exit # pylint: disable=redefined-builtin
|
||||
from urllib.request import urlopen
|
||||
from urllib.error import URLError
|
||||
|
||||
@ -14,7 +19,7 @@ from setuptools import setup, find_packages # noqa: H301
|
||||
from setuptools.command.test import test as TestCommand
|
||||
|
||||
NAME = "0x-sra-client"
|
||||
VERSION = "3.0.0"
|
||||
VERSION = "4.0.0"
|
||||
# To install the library, run the following
|
||||
#
|
||||
# python setup.py install
|
||||
@ -41,6 +46,12 @@ class CleanCommandExtension(clean):
|
||||
rmtree("0x_sra_client.egg-info", ignore_errors=True)
|
||||
rmtree("build", ignore_errors=True)
|
||||
rmtree("dist", ignore_errors=True)
|
||||
subprocess.check_call( # nosec
|
||||
("docker-compose -f test/relayer/docker-compose.yml down").split()
|
||||
)
|
||||
subprocess.check_call( # nosec
|
||||
("docker-compose -f test/relayer/docker-compose.yml rm").split()
|
||||
)
|
||||
|
||||
|
||||
class TestCommandExtension(TestCommand):
|
||||
@ -85,12 +96,15 @@ class StartTestRelayerCommand(distutils.command.build_py.build_py):
|
||||
("docker-compose -f test/relayer/docker-compose.yml up -d").split()
|
||||
)
|
||||
launch_kit_ready = False
|
||||
print("Waiting for relayer to start accepting connections...", end="")
|
||||
print(
|
||||
"Waiting for Launch Kit Backend to start accepting connections...",
|
||||
flush=True,
|
||||
)
|
||||
while not launch_kit_ready:
|
||||
try:
|
||||
launch_kit_ready = (
|
||||
urlopen( # nosec
|
||||
"http://localhost:3000/v2/asset_pairs"
|
||||
"http://localhost:3000/v3/asset_pairs"
|
||||
).getcode()
|
||||
== 200
|
||||
)
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""0x Python API."""
|
||||
__import__("pkg_resources").declare_namespace(__name__)
|
||||
__import__("pkg_resources").declare_namespace(__name__) # type: ignore
|
||||
|
@ -19,7 +19,7 @@ Install the package with pip::
|
||||
pip install 0x-sra-client
|
||||
|
||||
To interact with a 0x Relayer, you need the HTTP endpoint of the Relayer you'd
|
||||
like to connect to (eg https://api.radarrelay.com/0x/v2).
|
||||
like to connect to (eg https://api.radarrelay.com/0x/v3).
|
||||
|
||||
For testing one can use the `0x-launch-kit-backend
|
||||
<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
|
||||
permission to trade their ZRX tokens:
|
||||
|
||||
>>> from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES
|
||||
>>> contract_addresses = NETWORK_TO_ADDRESSES[network_id]
|
||||
>>> from zero_ex.contract_addresses import network_to_addresses
|
||||
>>> contract_addresses = network_to_addresses(network_id)
|
||||
>>>
|
||||
>>> from zero_ex.contract_artifacts import abi_by_name
|
||||
>>> zrx_token_contract = Web3(eth_node).eth.contract(
|
||||
@ -105,7 +105,8 @@ Post Order
|
||||
|
||||
Post an order for our Maker to trade ZRX for WETH:
|
||||
|
||||
>>> from zero_ex.contract_wrappers.exchange.types import Order, order_to_jsdict
|
||||
>>> from zero_ex.contract_wrappers.exchange.types import Order
|
||||
>>> from zero_ex.contract_wrappers.order_conversions import order_to_jsdict
|
||||
>>> from zero_ex.order_utils import (
|
||||
... asset_data_utils,
|
||||
... sign_hash)
|
||||
@ -120,9 +121,11 @@ Post an order for our Maker to trade ZRX for WETH:
|
||||
... makerAssetData=asset_data_utils.encode_erc20(
|
||||
... contract_addresses.zrx_token
|
||||
... ),
|
||||
... makerFeeAssetData=asset_data_utils.encode_erc20('0x'+'00'*20),
|
||||
... takerAssetData=asset_data_utils.encode_erc20(
|
||||
... contract_addresses.ether_token
|
||||
... ),
|
||||
... takerFeeAssetData=asset_data_utils.encode_erc20('0x'+'00'*20),
|
||||
... salt=random.randint(1, 100000000000000000),
|
||||
... makerFee=0,
|
||||
... takerFee=0,
|
||||
@ -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
|
||||
>>> order_hash_hex = generate_order_hash_hex(
|
||||
... order, contract_addresses.exchange
|
||||
... order, contract_addresses.exchange, Web3(eth_node).eth.chainId
|
||||
... )
|
||||
>>> relayer.post_order_with_http_info(
|
||||
... network_id=network_id.value,
|
||||
@ -144,7 +147,8 @@ Post an order for our Maker to trade ZRX for WETH:
|
||||
... exchange_address=contract_addresses.exchange,
|
||||
... signature=sign_hash(
|
||||
... eth_node, Web3.toChecksumAddress(maker_address), order_hash_hex
|
||||
... )
|
||||
... ),
|
||||
... chain_id=Web3(eth_node).eth.chainId,
|
||||
... )
|
||||
... )[1]
|
||||
200
|
||||
@ -152,24 +156,35 @@ Post an order for our Maker to trade ZRX for WETH:
|
||||
Get Order
|
||||
---------
|
||||
|
||||
(But first sleep for a moment, to give the test relayer a chance to start up.
|
||||
|
||||
>>> from time import sleep
|
||||
>>> sleep(0.2)
|
||||
|
||||
This is necessary for automated verification of these examples.)
|
||||
|
||||
Retrieve the order we just posted:
|
||||
|
||||
>>> relayer.get_order("0x" + order_hash_hex)
|
||||
{'meta_data': {},
|
||||
'order': {'exchangeAddress': '0x...',
|
||||
{'meta_data': {'orderHash': '0x...',
|
||||
'remainingFillableTakerAssetAmount': '2'},
|
||||
'order': {'chainId': 50,
|
||||
'exchangeAddress': '0x...',
|
||||
'expirationTimeSeconds': '...',
|
||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAddress': '0x...',
|
||||
'makerAssetAmount': '2',
|
||||
'makerAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'makerFee': '0',
|
||||
'makerFeeAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'salt': '...',
|
||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
'signature': '0x...',
|
||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAssetAmount': '2',
|
||||
'takerAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'takerFee': '0'}}
|
||||
'takerFee': '0',
|
||||
'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}
|
||||
|
||||
Get Orders
|
||||
-----------
|
||||
@ -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:
|
||||
|
||||
>>> relayer.get_orders()
|
||||
{'records': [{'meta_data': {},
|
||||
'order': {'exchangeAddress': '0x...',
|
||||
{'records': [{'meta_data': {'orderHash': '0x...',
|
||||
'remainingFillableTakerAssetAmount': '2'},
|
||||
'order': {'chainId': 50,
|
||||
'exchangeAddress': '0x...',
|
||||
'expirationTimeSeconds': '...',
|
||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAddress': '0x...',
|
||||
'makerAssetAmount': '2',
|
||||
'makerAssetData': '0xf47261b000000000000000000000000...',
|
||||
'makerFee': '0',
|
||||
'makerFeeAssetData': '0xf47261b000000000000000000000000...',
|
||||
'salt': '...',
|
||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
'signature': '0x...',
|
||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAssetAmount': '2',
|
||||
'takerAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'takerFee': '0'}}]}
|
||||
'takerFee': '0',
|
||||
'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}...]}
|
||||
|
||||
Get Asset Pairs
|
||||
---------------
|
||||
@ -233,43 +252,50 @@ consists just of our order):
|
||||
... ).hex(),
|
||||
... )
|
||||
>>> orderbook
|
||||
{'asks': {'records': []},
|
||||
'bids': {'records': [{'meta_data': {},
|
||||
'order': {'exchangeAddress': '0x...',
|
||||
{'asks': {'records': [...]},
|
||||
'bids': {'records': [{'meta_data': {'orderHash': '0x...',
|
||||
'remainingFillableTakerAssetAmount': '2'},
|
||||
'order': {'chainId': 50,
|
||||
'exchangeAddress': '0x...',
|
||||
'expirationTimeSeconds': '...',
|
||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAddress': '0x...',
|
||||
'makerAssetAmount': '2',
|
||||
'makerAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'makerFee': '0',
|
||||
'makerFeeAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'salt': '...',
|
||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
'signature': '0x...',
|
||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAssetAmount': '2',
|
||||
'takerAssetData': '0xf47261b0000000000000000000000000...',
|
||||
'takerFee': '0'}}]}}
|
||||
'takerFee': '0',
|
||||
'takerFeeAssetData': '0xf47261b0000000000000000000000000...'}}...]}}
|
||||
|
||||
Select an order from the orderbook
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
>>> from zero_ex.contract_wrappers.exchange.types import jsdict_to_order
|
||||
>>> from zero_ex.contract_wrappers.order_conversions import jsdict_to_order
|
||||
>>> order = jsdict_to_order(orderbook.bids.records[0].order)
|
||||
>>> from pprint import pprint
|
||||
>>> pprint(order)
|
||||
{'expirationTimeSeconds': ...,
|
||||
{'chainId': 50,
|
||||
'expirationTimeSeconds': ...,
|
||||
'feeRecipientAddress': '0x0000000000000000000000000000000000000000',
|
||||
'makerAddress': '0x...',
|
||||
'makerAssetAmount': 2,
|
||||
'makerAssetData': b...
|
||||
'makerFee': 0,
|
||||
'makerFeeAssetData': b...
|
||||
'salt': ...,
|
||||
'senderAddress': '0x0000000000000000000000000000000000000000',
|
||||
'signature': '0x...',
|
||||
'takerAddress': '0x0000000000000000000000000000000000000000',
|
||||
'takerAssetAmount': 2,
|
||||
'takerAssetData': b...
|
||||
'takerFee': 0}
|
||||
'takerAssetData': b...,
|
||||
'takerFee': 0,
|
||||
'takerFeeAssetData': b...}
|
||||
|
||||
Filling or Cancelling an Order
|
||||
------------------------------
|
||||
@ -319,8 +345,8 @@ book. Now let's have the taker fill it:
|
||||
>>> from zero_ex.contract_wrappers.exchange import Exchange
|
||||
>>> from zero_ex.order_utils import Order
|
||||
>>> exchange = Exchange(
|
||||
... provider=eth_node,
|
||||
... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange
|
||||
... web3_or_provider=eth_node,
|
||||
... contract_address=network_to_addresses(NetworkId.GANACHE).exchange
|
||||
... )
|
||||
|
||||
(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(
|
||||
... order=order,
|
||||
... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill
|
||||
... signature=order['signature'].replace('0x', '').encode('utf-8'),
|
||||
... signature=bytes.fromhex(order['signature'].replace('0x', '')),
|
||||
... tx_params=TxParams(from_=taker_address)
|
||||
... )
|
||||
HexBytes('0x...')
|
||||
|
@ -139,7 +139,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/asset_pairs",
|
||||
"/v3/asset_pairs",
|
||||
"GET",
|
||||
path_params,
|
||||
query_params,
|
||||
@ -250,7 +250,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/fee_recipients",
|
||||
"/v3/fee_recipients",
|
||||
"GET",
|
||||
path_params,
|
||||
query_params,
|
||||
@ -363,7 +363,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/order/{orderHash}",
|
||||
"/v3/order/{orderHash}",
|
||||
"GET",
|
||||
path_params,
|
||||
query_params,
|
||||
@ -497,7 +497,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/order_config",
|
||||
"/v3/order_config",
|
||||
"POST",
|
||||
path_params,
|
||||
query_params,
|
||||
@ -680,7 +680,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/orderbook",
|
||||
"/v3/orderbook",
|
||||
"GET",
|
||||
path_params,
|
||||
query_params,
|
||||
@ -718,50 +718,48 @@ class DefaultApi(object):
|
||||
:param bool async_req: Whether request should be asynchronous.
|
||||
:param str maker_asset_proxy_id: The maker
|
||||
`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).
|
||||
:param str taker_asset_proxy_id: The taker asset
|
||||
`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).
|
||||
:param str maker_asset_address: The contract address for the maker asset.
|
||||
:param str taker_asset_address: The contract address for the taker asset.
|
||||
:param str exchange_address: Same as exchangeAddress in the
|
||||
`0x Protocol v2 Specification
|
||||
<https://github.com/0xProject/0x-protocol-specification/blob/
|
||||
master/v2/v2-specification.md#order-message-format>`__
|
||||
:param str exchange_address: Contract address for the exchange
|
||||
contract.
|
||||
:param str sender_address: Same as senderAddress in the
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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 page: The number of the page to request in the collection.
|
||||
:param int per_page: The number of records to return per page.
|
||||
@ -795,50 +793,50 @@ class DefaultApi(object):
|
||||
:param bool async_req: Whether request should be asynchronous.
|
||||
:param str maker_asset_proxy_id: The maker
|
||||
`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).
|
||||
:param str taker_asset_proxy_id: The taker asset
|
||||
`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).
|
||||
:param str maker_asset_address: The contract address for the maker asset.
|
||||
:param str taker_asset_address: The contract address for the taker asset.
|
||||
:param str exchange_address: Same as exchangeAddress in the [0x
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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 Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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
|
||||
`0x Protocol v2 Specification
|
||||
`0x Protocol v3 Specification
|
||||
<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 page: The number of the page to request in the collection.
|
||||
:param int per_page: The number of records to return per page.
|
||||
@ -965,7 +963,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/orders",
|
||||
"/v3/orders",
|
||||
"GET",
|
||||
path_params,
|
||||
query_params,
|
||||
@ -1077,7 +1075,7 @@ class DefaultApi(object):
|
||||
auth_settings = []
|
||||
|
||||
return self.api_client.call_api(
|
||||
"/v2/order",
|
||||
"/v3/order",
|
||||
"POST",
|
||||
path_params,
|
||||
query_params,
|
||||
|
@ -1,14 +1,36 @@
|
||||
# Run Launch Kit with Ganache as the backing node
|
||||
# Run Launch Kit Backend with Ganache and Mesh instances backing it.
|
||||
version: '3'
|
||||
services:
|
||||
ganache:
|
||||
image: "0xorg/ganache-cli:2.2.2"
|
||||
image: "0xorg/ganache-cli:4.4.0-beta.1"
|
||||
ports:
|
||||
- "8545:8545"
|
||||
launchkit:
|
||||
image: "0xorg/launch-kit-backend:74bcc39"
|
||||
environment:
|
||||
- VERSION=4.4.0-beta.1
|
||||
- SNAPSHOT_NAME=0x_ganache_snapshot-v3-beta
|
||||
mesh:
|
||||
image: 0xorg/mesh:6.0.0-beta-0xv3
|
||||
depends_on:
|
||||
- ganache
|
||||
environment:
|
||||
ETHEREUM_RPC_URL: 'http://localhost:8545'
|
||||
ETHEREUM_NETWORK_ID: '50'
|
||||
ETHEREUM_CHAIN_ID: '1337'
|
||||
USE_BOOTSTRAP_LIST: 'true'
|
||||
VERBOSITY: 3
|
||||
PRIVATE_KEY_PATH: ''
|
||||
BLOCK_POLLING_INTERVAL: '5s'
|
||||
P2P_LISTEN_PORT: '60557'
|
||||
ports:
|
||||
- '60557:60557'
|
||||
network_mode: "host" # to connect to ganache
|
||||
command: |
|
||||
sh -c "waitForGanache () { until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done }; waitForGanache && ./mesh"
|
||||
launch-kit-backend:
|
||||
image: "0xorg/launch-kit-backend:v3"
|
||||
depends_on:
|
||||
- ganache
|
||||
- mesh
|
||||
ports:
|
||||
- "3000:3000"
|
||||
network_mode: "host" # to connect to ganache
|
||||
@ -16,5 +38,9 @@ services:
|
||||
- NETWORK_ID=50
|
||||
- RPC_URL=http://localhost:8545
|
||||
- WHITELIST_ALL_TOKENS=True
|
||||
- FEE_RECIPIENT=0x0000000000000000000000000000000000000001
|
||||
- MAKER_FEE_UNIT_AMOUNT=0
|
||||
- TAKER_FEE_UNIT_AMOUNT=0
|
||||
- MESH_ENDPOINT=ws://localhost:60557
|
||||
command: |
|
||||
sh -c "until printf 'POST /\r\nContent-Length: 26\r\n\r\n{\"method\":\"net_listening\"}' | nc localhost 8545 | grep true; do continue; done; node_modules/.bin/forever ts/lib/index.js"
|
||||
sh -c "waitForMesh () { sleep 3; }; waitForMesh && sleep 5 && node_modules/.bin/forever ts/lib/index.js"
|
||||
|
@ -18,7 +18,7 @@ commands =
|
||||
pytest test
|
||||
|
||||
[testenv:run_tests_against_deployment]
|
||||
deps=pytest
|
||||
setenv = PY_IGNORE_IMPORTMISMATCH = 1
|
||||
commands =
|
||||
pip install 0x-sra-client
|
||||
pytest test
|
||||
pip install 0x-sra-client[dev]
|
||||
pytest --doctest-modules src test
|
||||
|
Loading…
x
Reference in New Issue
Block a user