Merge branch 'development' of https://github.com/0xProject/0x-monorepo into feature/what_is_0x_video_embed

This commit is contained in:
fragosti 2019-08-01 13:43:33 -07:00
commit 25ef3b8445
40 changed files with 1152 additions and 904 deletions

View File

@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import
Union, Union,
) )
from eth_utils import to_checksum_address
from mypy_extensions import TypedDict # pylint: disable=unused-import from mypy_extensions import TypedDict # pylint: disable=unused-import
from hexbytes import HexBytes from hexbytes import HexBytes
from web3 import Web3
from web3.contract import ContractFunction
from web3.datastructures import AttributeDict from web3.datastructures import AttributeDict
from web3.providers.base import BaseProvider from web3.providers.base import BaseProvider
from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper from zero_ex.contract_wrappers.bases import ContractMethod, Validator
from zero_ex.contract_wrappers.tx_params import TxParams from zero_ex.contract_wrappers.tx_params import TxParams
class {{contractName}}ValidatorBase:
"""Base class for validating inputs to {{contractName}} methods."""
def __init__(
self,
provider: BaseProvider,
contract_address: str,
private_key: str = None,
):
"""Initialize the instance."""
def assert_valid(
self, method_name: str, parameter_name: str, argument_value: Any
):
"""Raise an exception if method input is not valid.
:param method_name: Name of the method whose input is to be validated.
:param parameter_name: Name of the parameter whose input is to be
validated.
:param argument_value: Value of argument to parameter to be validated.
"""
# Try to import a custom validator class definition; if there isn't one, # Try to import a custom validator class definition; if there isn't one,
# declare one that we can instantiate for the default argument to the # declare one that we can instantiate for the default argument to the
# constructor for {{contractName}} below. # constructor for {{contractName}} below.
@ -53,56 +34,52 @@ try:
) )
except ImportError: except ImportError:
class {{contractName}}Validator({{contractName}}ValidatorBase): # type: ignore class {{contractName}}Validator(Validator): # type: ignore
"""No-op input validator.""" """No-op input validator."""
{{tupleDefinitions ABIString}} {{tupleDefinitions ABIString}}
{{#each methods}}
{{> method_class contractName=../contractName}}
{{/each}}
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods,too-many-instance-attributes
class {{contractName}}(BaseContractWrapper): class {{contractName}}:
"""Wrapper class for {{contractName}} Solidity contract.{{docBytesIfNecessary ABIString}}""" """Wrapper class for {{contractName}} Solidity contract.{{docBytesIfNecessary ABIString}}"""
{{#each methods}}
{{toPythonIdentifier this.name}}: {{toPythonClassname this.name}}Method
{{/each}}
def __init__( def __init__(
self, self,
provider: BaseProvider, provider: BaseProvider,
contract_address: str, contract_address: str,
validator: {{contractName}}Validator = None, validator: {{contractName}}Validator = None,
private_key: str = None,
): ):
"""Get an instance of wrapper for smart contract. """Get an instance of wrapper for smart contract.
:param provider: instance of :class:`web3.providers.base.BaseProvider` :param provider: instance of :class:`web3.providers.base.BaseProvider`
:param contract_address: where the contract has been deployed :param contract_address: where the contract has been deployed
:param private_key: If specified, transactions will be signed locally, :param validator: for validation of method inputs.
via Web3.py's `eth.account.signTransaction()`:code:, before being
sent via `eth.sendRawTransaction()`:code:.
""" """
super().__init__( self.contract_address = contract_address
provider=provider,
contract_address=contract_address,
private_key=private_key,
)
if not validator: if not validator:
validator = {{contractName}}Validator(provider, contract_address, private_key) validator = {{contractName}}Validator(provider, contract_address)
self.validator = validator self._web3_eth = Web3( # type: ignore # pylint: disable=no-member
provider
).eth
def _get_contract_instance(self, token_address): functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions
"""Get an instance of the smart contract at a specific address.
:returns: contract object {{#each methods}}
""" self.{{toPythonIdentifier this.name}} = {{toPythonClassname this.name}}Method(provider, contract_address, functions.{{this.name}}, validator)
return self._contract_instance(
address=token_address, abi={{contractName}}.abi() {{/each}}
)
{{#each methods}}
{{> call contractName=../contractName}}
{{/each}}
{{#each events}} {{#each events}}
{{> event}} {{> event contractName=../contractName}}
{{/each}} {{/each}}
@staticmethod @staticmethod

View File

@ -1,54 +0,0 @@
def {{this.languageSpecificName}}(
self,
{{> typed_params inputs=inputs}}
tx_params: Optional[TxParams] = None,
{{^this.constant}}
view_only: bool = False,
{{/this.constant}}
) -> {{> return_type outputs=outputs~}}:
"""Execute underlying, same-named contract method.
{{sanitizeDevdocDetails this.name this.devdoc.details 8}}{{~#if this.devdoc.params~}}{{#each this.devdoc.params}}
{{makeParameterDocstringRole @key this 8}}{{/each}}{{/if}}
:param tx_params: transaction parameters
{{#if this.constant~}}
{{#if this.devdoc.return}}
{{makeReturnDocstringRole this.devdoc.return 8}}{{/if}}
{{else}}
:param view_only: whether to use transact() or call()
:returns: if param `view_only`:code: is `True`:code:, then returns the
value returned from the underlying function; else returns the
transaction hash.
{{/if}}
"""
{{#each this.inputs}}
self.validator.assert_valid(
method_name='{{../name}}',
parameter_name='{{name}}',
argument_value={{toPythonIdentifier name}},
)
{{#if (equal type 'address')}}
{{toPythonIdentifier this.name}} = self._validate_and_checksum_address({{toPythonIdentifier this.name}})
{{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}}
func = self._get_contract_instance(
self.contract_address
).functions.{{this.name}}(
{{> params}}
)
return self._invoke_function_call(
func=func,
tx_params=tx_params,
view_only={{#if this.constant}}True{{else}}view_only{{/if}}
)

View File

@ -6,8 +6,4 @@
{{makeEventParameterDocstringRole name 8}} {{makeEventParameterDocstringRole name 8}}
""" """
tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash)
return ( return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi={{contractName}}.abi()).events.{{name}}().processReceipt(tx_receipt)
self._get_contract_instance(self.contract_address)
.events.{{name}}()
.processReceipt(tx_receipt)
)

View File

@ -0,0 +1,76 @@
class {{toPythonClassname this.name}}Method(ContractMethod):
"""Various interfaces to the {{this.name}} method."""
def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None):
"""Persist instance data."""
super().__init__(provider, contract_address, validator)
self.underlying_method = contract_function
{{#if inputs}}
def validate_and_normalize_inputs(self, {{> typed_params inputs=inputs}}):
"""Validate the inputs to the {{this.name}} method."""
{{#each this.inputs}}
self.validator.assert_valid(
method_name='{{../name}}',
parameter_name='{{name}}',
argument_value={{toPythonIdentifier name}},
)
{{#if (equal type 'address')}}
{{toPythonIdentifier this.name}} = self.validate_and_checksum_address({{toPythonIdentifier this.name}})
{{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 }})
{{/if}}
def call(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> {{> return_type outputs=outputs type='call'~}}:
"""Execute underlying contract method via eth_call.
{{sanitizeDevdocDetails this.name this.devdoc.details 8}}{{~#if this.devdoc.params~}}{{#each this.devdoc.params}}
{{makeParameterDocstringRole @key this 8}}{{/each}}{{/if}}
:param tx_params: transaction parameters
{{#if this.constant~}}
{{#if this.devdoc.return}}
{{makeReturnDocstringRole this.devdoc.return 8}}{{/if}}
{{else}}
:returns: the return value of the underlying method.
{{/if}}
"""
{{#if inputs}}
({{> params }}) = self.validate_and_normalize_inputs({{> params}})
{{/if}}
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method({{> params}}).call(tx_params.as_dict())
def send_transaction(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]:
"""Execute underlying contract method via eth_sendTransaction.
{{sanitizeDevdocDetails this.name this.devdoc.details 8}}{{~#if this.devdoc.params~}}{{#each this.devdoc.params}}
{{makeParameterDocstringRole @key this 8}}{{/each}}{{/if}}
:param tx_params: transaction parameters
{{#if this.constant~}}
{{#if this.devdoc.return}}
{{makeReturnDocstringRole this.devdoc.return 8}}{{/if}}
{{/if}}
"""
{{#if inputs}}
({{> params }}) = self.validate_and_normalize_inputs({{> params}})
{{/if}}
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method({{> params}}).transact(tx_params.as_dict())
def estimate_gas(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> int:
"""Estimate gas consumption of method call."""
{{#if inputs}}
({{> params }}) = self.validate_and_normalize_inputs({{> params}})
{{/if}}
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method({{> params}}).estimateGas(tx_params.as_dict())

View File

@ -1,3 +1,3 @@
{{#each inputs}} {{#each inputs}}
{{toPythonIdentifier name}}{{#if @last}}{{else}},{{/if}} {{toPythonIdentifier name}}{{#if @last}}{{else}}, {{/if~}}
{{/each}} {{/each~}}

View File

@ -1,3 +1,3 @@
{{#each inputs}} {{#each inputs}}
{{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}, {{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}{{^if @last}}, {{/if~}}
{{/each}} {{/each~}}

View File

@ -1,12 +1,33 @@
[ [
{ {
"timestamp": 1564604963, "version": "4.0.0",
"changes": [
{
"note": "whitespace changes to generated Python code",
"pr": 1996
},
{
"note": "move Python Validator base class from generated code to common package",
"pr": 1996
},
{
"note": "Changed fundamental thing-to-be-wrapped from the contract to the contract method. That is, now there is a base contract method wrapper class rather than a base contract wrapper class, and individual contract methods are represented by named classes inheriting from that base, and the different operations on a method are now represented by a nested-object dot notation, ie, WrappedContract.ContractMethod.call() and WrappedContract.ContractMethod.send_transaction().",
"pr": 1996
},
{
"note": "added gas estimation functionality to contract methods",
"pr": 1996
}
]
},
{
"version": "3.1.2", "version": "3.1.2",
"changes": [ "changes": [
{ {
"note": "Dependencies updated" "note": "Dependencies updated"
} }
] ],
"timestamp": 1564604963
}, },
{ {
"version": "3.1.1", "version": "3.1.1",

View File

@ -23,6 +23,7 @@
"test_cli:clean": "rm -rf test-cli/output && rm -rf test-cli/test_typescript/lib", "test_cli:clean": "rm -rf test-cli/output && rm -rf test-cli/test_typescript/lib",
"test_cli:build": "tsc --project test-cli/tsconfig.json", "test_cli:build": "tsc --project test-cli/tsconfig.json",
"test_cli:run_mocha": "mocha --require source-map-support/register --require make-promises-safe test-cli/test_typescript/lib/**/*_test.js --timeout 100000 --bail --exit", "test_cli:run_mocha": "mocha --require source-map-support/register --require make-promises-safe test-cli/test_typescript/lib/**/*_test.js --timeout 100000 --bail --exit",
"test_cli:test_python": "black --check test-cli/output/python/**/__init__.py; test $? -le 1 # just make sure black can parse the output",
"rebuild_and_test": "run-s build test", "rebuild_and_test": "run-s build test",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",

View File

@ -225,6 +225,10 @@ function registerPythonHelpers(): void {
} }
return ''; return '';
}); });
Handlebars.registerHelper(
'toPythonClassname',
(sourceName: string) => new Handlebars.SafeString(changeCase.pascal(sourceName)),
);
} }
if (args.language === 'TypeScript') { if (args.language === 'TypeScript') {
registerTypeScriptHelpers(); registerTypeScriptHelpers();

View File

@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import
Union, Union,
) )
from eth_utils import to_checksum_address
from mypy_extensions import TypedDict # pylint: disable=unused-import from mypy_extensions import TypedDict # pylint: disable=unused-import
from hexbytes import HexBytes from hexbytes import HexBytes
from web3 import Web3
from web3.contract import ContractFunction
from web3.datastructures import AttributeDict from web3.datastructures import AttributeDict
from web3.providers.base import BaseProvider from web3.providers.base import BaseProvider
from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper from zero_ex.contract_wrappers.bases import ContractMethod, Validator
from zero_ex.contract_wrappers.tx_params import TxParams from zero_ex.contract_wrappers.tx_params import TxParams
class LibDummyValidatorBase:
"""Base class for validating inputs to LibDummy methods."""
def __init__(
self,
provider: BaseProvider,
contract_address: str,
private_key: str = None,
):
"""Initialize the instance."""
def assert_valid(
self, method_name: str, parameter_name: str, argument_value: Any
):
"""Raise an exception if method input is not valid.
:param method_name: Name of the method whose input is to be validated.
:param parameter_name: Name of the parameter whose input is to be
validated.
:param argument_value: Value of argument to parameter to be validated.
"""
# Try to import a custom validator class definition; if there isn't one, # Try to import a custom validator class definition; if there isn't one,
# declare one that we can instantiate for the default argument to the # declare one that we can instantiate for the default argument to the
# constructor for LibDummy below. # constructor for LibDummy below.
@ -53,15 +34,15 @@ try:
) )
except ImportError: except ImportError:
class LibDummyValidator(LibDummyValidatorBase): # type: ignore class LibDummyValidator(Validator): # type: ignore
"""No-op input validator.""" """No-op input validator."""
# pylint: disable=too-many-public-methods # pylint: disable=too-many-public-methods,too-many-instance-attributes
class LibDummy(BaseContractWrapper): class LibDummy:
"""Wrapper class for LibDummy Solidity contract.""" """Wrapper class for LibDummy Solidity contract."""
def __init__( def __init__(
@ -69,35 +50,24 @@ class LibDummy(BaseContractWrapper):
provider: BaseProvider, provider: BaseProvider,
contract_address: str, contract_address: str,
validator: LibDummyValidator = None, validator: LibDummyValidator = None,
private_key: str = None,
): ):
"""Get an instance of wrapper for smart contract. """Get an instance of wrapper for smart contract.
:param provider: instance of :class:`web3.providers.base.BaseProvider` :param provider: instance of :class:`web3.providers.base.BaseProvider`
:param contract_address: where the contract has been deployed :param contract_address: where the contract has been deployed
:param private_key: If specified, transactions will be signed locally, :param validator: for validation of method inputs.
via Web3.py's `eth.account.signTransaction()`:code:, before being
sent via `eth.sendRawTransaction()`:code:.
""" """
super().__init__( self.contract_address = contract_address
provider=provider,
contract_address=contract_address,
private_key=private_key,
)
if not validator: if not validator:
validator = LibDummyValidator(provider, contract_address, private_key) validator = LibDummyValidator(provider, contract_address)
self.validator = validator self._web3_eth = Web3( # type: ignore # pylint: disable=no-member
provider
).eth
def _get_contract_instance(self, token_address): functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=LibDummy.abi()).functions
"""Get an instance of the smart contract at a specific address.
:returns: contract object
"""
return self._contract_instance(
address=token_address, abi=LibDummy.abi()
)
@staticmethod @staticmethod
def abi(): def abi():

View File

@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import
Union, Union,
) )
from eth_utils import to_checksum_address
from mypy_extensions import TypedDict # pylint: disable=unused-import from mypy_extensions import TypedDict # pylint: disable=unused-import
from hexbytes import HexBytes from hexbytes import HexBytes
from web3 import Web3
from web3.contract import ContractFunction
from web3.datastructures import AttributeDict from web3.datastructures import AttributeDict
from web3.providers.base import BaseProvider from web3.providers.base import BaseProvider
from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper from zero_ex.contract_wrappers.bases import ContractMethod, Validator
from zero_ex.contract_wrappers.tx_params import TxParams from zero_ex.contract_wrappers.tx_params import TxParams
class TestLibDummyValidatorBase:
"""Base class for validating inputs to TestLibDummy methods."""
def __init__(
self,
provider: BaseProvider,
contract_address: str,
private_key: str = None,
):
"""Initialize the instance."""
def assert_valid(
self, method_name: str, parameter_name: str, argument_value: Any
):
"""Raise an exception if method input is not valid.
:param method_name: Name of the method whose input is to be validated.
:param parameter_name: Name of the parameter whose input is to be
validated.
:param argument_value: Value of argument to parameter to be validated.
"""
# Try to import a custom validator class definition; if there isn't one, # Try to import a custom validator class definition; if there isn't one,
# declare one that we can instantiate for the default argument to the # declare one that we can instantiate for the default argument to the
# constructor for TestLibDummy below. # constructor for TestLibDummy below.
@ -53,62 +34,23 @@ try:
) )
except ImportError: except ImportError:
class TestLibDummyValidator(TestLibDummyValidatorBase): # type: ignore class TestLibDummyValidator(Validator): # type: ignore
"""No-op input validator.""" """No-op input validator."""
# pylint: disable=too-many-public-methods class PublicAddConstantMethod(ContractMethod):
class TestLibDummy(BaseContractWrapper): """Various interfaces to the publicAddConstant method."""
"""Wrapper class for TestLibDummy Solidity contract."""
def __init__( def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None):
self, """Persist instance data."""
provider: BaseProvider, super().__init__(provider, contract_address, validator)
contract_address: str, self.underlying_method = contract_function
validator: TestLibDummyValidator = None,
private_key: str = None,
):
"""Get an instance of wrapper for smart contract.
:param provider: instance of :class:`web3.providers.base.BaseProvider` def validate_and_normalize_inputs(self, x: int):
:param contract_address: where the contract has been deployed """Validate the inputs to the publicAddConstant method."""
:param private_key: If specified, transactions will be signed locally,
via Web3.py's `eth.account.signTransaction()`:code:, before being
sent via `eth.sendRawTransaction()`:code:.
"""
super().__init__(
provider=provider,
contract_address=contract_address,
private_key=private_key,
)
if not validator:
validator = TestLibDummyValidator(provider, contract_address, private_key)
self.validator = validator
def _get_contract_instance(self, token_address):
"""Get an instance of the smart contract at a specific address.
:returns: contract object
"""
return self._contract_instance(
address=token_address, abi=TestLibDummy.abi()
)
def public_add_constant(
self,
x: int,
tx_params: Optional[TxParams] = None,
) -> int:
"""Execute underlying, same-named contract method.
:param tx_params: transaction parameters
"""
self.validator.assert_valid( self.validator.assert_valid(
method_name='publicAddConstant', method_name='publicAddConstant',
parameter_name='x', parameter_name='x',
@ -116,27 +58,44 @@ class TestLibDummy(BaseContractWrapper):
) )
# safeguard against fractional inputs # safeguard against fractional inputs
x = int(x) x = int(x)
func = self._get_contract_instance( return (x)
self.contract_address
).functions.publicAddConstant(
x
)
return self._invoke_function_call(
func=func,
tx_params=tx_params,
view_only=True
)
def public_add_one( def call(self, x: int, tx_params: Optional[TxParams] = None) -> int:
self, """Execute underlying contract method via eth_call.
x: int,
tx_params: Optional[TxParams] = None,
) -> int:
"""Execute underlying, same-named contract method.
:param tx_params: transaction parameters :param tx_params: transaction parameters
""" """
(x) = self.validate_and_normalize_inputs(x)
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method(x).call(tx_params.as_dict())
def send_transaction(self, x: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]:
"""Execute underlying contract method via eth_sendTransaction.
:param tx_params: transaction parameters
"""
(x) = self.validate_and_normalize_inputs(x)
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method(x).transact(tx_params.as_dict())
def estimate_gas(self, x: int, tx_params: Optional[TxParams] = None) -> int:
"""Estimate gas consumption of method call."""
(x) = self.validate_and_normalize_inputs(x)
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method(x).estimateGas(tx_params.as_dict())
class PublicAddOneMethod(ContractMethod):
"""Various interfaces to the publicAddOne method."""
def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None):
"""Persist instance data."""
super().__init__(provider, contract_address, validator)
self.underlying_method = contract_function
def validate_and_normalize_inputs(self, x: int):
"""Validate the inputs to the publicAddOne method."""
self.validator.assert_valid( self.validator.assert_valid(
method_name='publicAddOne', method_name='publicAddOne',
parameter_name='x', parameter_name='x',
@ -144,16 +103,67 @@ class TestLibDummy(BaseContractWrapper):
) )
# safeguard against fractional inputs # safeguard against fractional inputs
x = int(x) x = int(x)
func = self._get_contract_instance( return (x)
self.contract_address
).functions.publicAddOne( def call(self, x: int, tx_params: Optional[TxParams] = None) -> int:
x """Execute underlying contract method via eth_call.
)
return self._invoke_function_call( :param tx_params: transaction parameters
func=func,
tx_params=tx_params, """
view_only=True (x) = self.validate_and_normalize_inputs(x)
) tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method(x).call(tx_params.as_dict())
def send_transaction(self, x: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]:
"""Execute underlying contract method via eth_sendTransaction.
:param tx_params: transaction parameters
"""
(x) = self.validate_and_normalize_inputs(x)
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method(x).transact(tx_params.as_dict())
def estimate_gas(self, x: int, tx_params: Optional[TxParams] = None) -> int:
"""Estimate gas consumption of method call."""
(x) = self.validate_and_normalize_inputs(x)
tx_params = super().normalize_tx_params(tx_params)
return self.underlying_method(x).estimateGas(tx_params.as_dict())
# pylint: disable=too-many-public-methods,too-many-instance-attributes
class TestLibDummy:
"""Wrapper class for TestLibDummy Solidity contract."""
public_add_constant: PublicAddConstantMethod
public_add_one: PublicAddOneMethod
def __init__(
self,
provider: 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 contract_address: where the contract has been deployed
:param validator: for validation of method inputs.
"""
self.contract_address = contract_address
if not validator:
validator = TestLibDummyValidator(provider, contract_address)
self._web3_eth = Web3( # type: ignore # pylint: disable=no-member
provider
).eth
functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=TestLibDummy.abi()).functions
self.public_add_constant = PublicAddConstantMethod(provider, contract_address, functions.publicAddConstant, validator)
self.public_add_one = PublicAddOneMethod(provider, contract_address, functions.publicAddOne, validator)
@staticmethod @staticmethod
def abi(): def abi():

View File

@ -44,14 +44,14 @@
"homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md",
"dependencies": { "dependencies": {
"@0x/assert": "^2.1.0", "@0x/assert": "^2.1.0",
"@0x/asset-buyer": "6.1.4", "@0x/asset-buyer": "6.1.8",
"@0x/json-schemas": "^3.0.11", "@0x/json-schemas": "^3.0.11",
"@0x/order-utils": "^8.2.2", "@0x/order-utils": "^8.2.2",
"@0x/subproviders": "^4.1.2", "@0x/subproviders": "^5.0.0",
"@0x/types": "^2.4.1", "@0x/types": "^2.4.1",
"@0x/typescript-typings": "^4.2.3", "@0x/typescript-typings": "^4.2.4",
"@0x/utils": "^4.4.1", "@0x/utils": "^4.4.2",
"@0x/web3-wrapper": "^6.0.8", "@0x/web3-wrapper": "^6.0.9",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bowser": "^1.9.4", "bowser": "^1.9.4",
"copy-to-clipboard": "^3.0.8", "copy-to-clipboard": "^3.0.8",

View File

@ -49,7 +49,7 @@
}, },
"dependencies": { "dependencies": {
"@0x/types": "^2.4.1", "@0x/types": "^2.4.1",
"@0x/utils": "^4.4.1", "@0x/utils": "^4.4.2",
"@lerna/batch-packages": "^3.0.0-beta.18", "@lerna/batch-packages": "^3.0.0-beta.18",
"@types/depcheck": "^0.6.0", "@types/depcheck": "^0.6.0",
"async-child-process": "^1.1.1", "async-child-process": "^1.1.1",

View File

@ -25,7 +25,7 @@
}, },
"homepage": "https://github.com/0xProject/0x-monorepo/packages/python-contract-wrappers/README.md", "homepage": "https://github.com/0xProject/0x-monorepo/packages/python-contract-wrappers/README.md",
"devDependencies": { "devDependencies": {
"@0x/abi-gen": "^3.1.1", "@0x/abi-gen": "^3.1.2",
"shx": "^0.2.2" "shx": "^0.2.2"
}, },
"publishConfig": { "publishConfig": {

View File

@ -20,10 +20,10 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"0x.js": "^6.0.12", "0x.js": "^6.0.12",
"@0x/subproviders": "^4.1.2", "@0x/subproviders": "^5.0.0",
"@0x/typescript-typings": "^4.2.4", "@0x/typescript-typings": "^4.2.4",
"@0x/utils": "^4.4.1", "@0x/utils": "^4.4.2",
"@0x/web3-wrapper": "^6.0.8", "@0x/web3-wrapper": "^6.0.9",
"body-parser": "^1.17.1", "body-parser": "^1.17.1",
"ethereum-types": "^2.1.4", "ethereum-types": "^2.1.4",
"ethereumjs-tx": "^1.3.5", "ethereumjs-tx": "^1.3.5",

View File

@ -26,11 +26,11 @@
"@0x/contract-wrappers": "^9.1.7", "@0x/contract-wrappers": "^9.1.7",
"@0x/json-schemas": "^3.0.11", "@0x/json-schemas": "^3.0.11",
"@0x/order-utils": "^8.2.2", "@0x/order-utils": "^8.2.2",
"@0x/subproviders": "^4.1.2", "@0x/subproviders": "^5.0.0",
"@0x/types": "^2.4.1", "@0x/types": "^2.4.1",
"@0x/typescript-typings": "^4.2.4", "@0x/typescript-typings": "^4.2.4",
"@0x/utils": "^4.4.1", "@0x/utils": "^4.4.2",
"@0x/web3-wrapper": "^6.0.8", "@0x/web3-wrapper": "^6.0.9",
"@reach/dialog": "^0.1.2", "@reach/dialog": "^0.1.2",
"@types/react-lazyload": "^2.3.1", "@types/react-lazyload": "^2.3.1",
"@types/react-loadable": "^5.4.2", "@types/react-loadable": "^5.4.2",

View File

@ -109,7 +109,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
with open("README.md", "r") as file_handle: with open("README.md", "r") as file_handle:

View File

@ -138,7 +138,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
with open("README.md", "r") as file_handle: with open("README.md", "r") as file_handle:

View File

@ -0,0 +1,5 @@
[pycodestyle]
ignore = E501, W503
# E501 = line too long
# W503 = line break occurred before a binary operator
# we let black handle these things

View File

@ -74,7 +74,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
class LintCommand(distutils.command.build_py.build_py): class LintCommand(distutils.command.build_py.build_py):

View File

@ -102,13 +102,13 @@ balance:
>>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy >>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy
>>> tx = zrx_token.approve( >>> tx = zrx_token.approve.send_transaction(
... erc20_proxy_addr, ... erc20_proxy_addr,
... to_wei(100, 'ether'), ... to_wei(100, 'ether'),
... tx_params=TxParams(from_=maker_address), ... tx_params=TxParams(from_=maker_address),
... ) ... )
>>> tx = weth_token.approve( >>> tx = weth_token.approve.send_transaction(
... erc20_proxy_addr, ... erc20_proxy_addr,
... to_wei(100, 'ether'), ... to_wei(100, 'ether'),
... tx_params=TxParams(from_=taker_address), ... tx_params=TxParams(from_=taker_address),
@ -166,7 +166,7 @@ too.
... provider=ganache, ... provider=ganache,
... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange,
... ) ... )
>>> tx_hash = exchange.fill_order( >>> tx_hash = exchange.fill_order.send_transaction(
... order=order, ... order=order,
... taker_asset_fill_amount=order["takerAssetAmount"], ... taker_asset_fill_amount=order["takerAssetAmount"],
... signature=maker_signature, ... signature=maker_signature,
@ -217,7 +217,7 @@ A Maker can cancel an order that has yet to be filled.
... ) ... )
... ) ... )
>>> tx_hash = exchange.cancel_order( >>> tx_hash = exchange.cancel_order.send_transaction(
... order=order, tx_params=TxParams(from_=maker_address) ... order=order, tx_params=TxParams(from_=maker_address)
... ) ... )
@ -287,12 +287,40 @@ is an example where the taker fills two orders in one transaction:
Fill order_1 and order_2 together: Fill order_1 and order_2 together:
>>> exchange.batch_fill_orders( >>> exchange.batch_fill_orders.send_transaction(
... orders=[order_1, order_2], ... orders=[order_1, order_2],
... taker_asset_fill_amounts=[1, 2], ... taker_asset_fill_amounts=[1, 2],
... signatures=[signature_1, signature_2], ... signatures=[signature_1, signature_2],
... tx_params=TxParams(from_=taker_address)) ... tx_params=TxParams(from_=taker_address))
HexBytes('0x...') HexBytes('0x...')
Estimating gas consumption
--------------------------
Before executing a transaction, you may want to get an estimate of how much gas
will be consumed.
>>> exchange.cancel_order.estimate_gas(
... order=Order(
... makerAddress=maker_address,
... takerAddress='0x0000000000000000000000000000000000000000',
... exchangeAddress=exchange_address,
... senderAddress='0x0000000000000000000000000000000000000000',
... feeRecipientAddress='0x0000000000000000000000000000000000000000',
... makerAssetData=asset_data_utils.encode_erc20(weth_address),
... takerAssetData=asset_data_utils.encode_erc20(weth_address),
... salt=random.randint(1, 100000000000000000),
... makerFee=0,
... takerFee=0,
... makerAssetAmount=1000000000000000000,
... takerAssetAmount=500000000000000000000,
... expirationTimeSeconds=round(
... (datetime.utcnow() + timedelta(days=1)).timestamp()
... )
... ),
... tx_params=TxParams(from_=maker_address),
... )
73825
""" """
from .tx_params import TxParams from .tx_params import TxParams

View File

@ -1,136 +0,0 @@
"""Base wrapper class for accessing ethereum smart contracts."""
from typing import Optional, Union
from eth_utils import to_checksum_address
from web3 import Web3
from web3.providers.base import BaseProvider
from .tx_params import TxParams
class BaseContractWrapper:
"""Base class for wrapping ethereum smart contracts.
It provides functionality for instantiating a contract instance,
calling view functions, and calling functions which require
transactions.
"""
def __init__(
self,
provider: BaseProvider,
contract_address: str,
private_key: str = None,
):
"""Create an instance of BaseContractWrapper.
:param provider: instance of :class:`web3.providers.base.BaseProvider`
:param private_key: If specified, transactions will be signed locally,
via Web3.py's `eth.account.signTransaction()`:code:, before being
sent via `eth.sendRawTransaction()`:code:.
"""
self._provider = provider
self._private_key = private_key
self._web3 = Web3(provider)
self._web3_eth = self._web3.eth # pylint: disable=no-member
self.contract_address = self._validate_and_checksum_address(
contract_address
)
self._can_send_tx = False
if self._web3_eth.defaultAccount or self._web3_eth.accounts:
self._can_send_tx = True
else:
middleware_stack = getattr(self._web3, "middleware_stack")
if middleware_stack.get("sign_and_send_raw_middleware"):
self._can_send_tx = True
elif private_key:
self._private_key = private_key
self._web3_eth.defaultAccount = to_checksum_address(
self._web3_eth.account.privateKeyToAccount(
private_key
).address
)
self._can_send_tx = True
def _contract_instance(self, address: str, abi: dict):
"""Get a contract instance.
:param address: string address of contract
:param abi: dict contract ABI
:returns: instance of contract
"""
return self._web3_eth.contract(
address=to_checksum_address(address), abi=abi
)
def _validate_and_checksum_address(self, address: str):
if not self._web3.isAddress(address):
raise TypeError("Invalid address provided: {}".format(address))
return to_checksum_address(address)
def _invoke_function_call(self, func, tx_params, view_only):
if view_only:
return func.call()
if not self._can_send_tx:
raise Exception(
"Cannot send transaction because no local private_key"
" or account found."
)
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._validate_and_checksum_address(tx_params.from_)
if self._private_key:
res = self._sign_and_send_raw_direct(func, tx_params)
else:
res = func.transact(tx_params.as_dict())
return res
def _sign_and_send_raw_direct(self, func, tx_params):
transaction = func.buildTransaction(tx_params.as_dict())
signed_tx = self._web3_eth.account.signTransaction(
transaction, private_key=self._private_key
)
return self._web3_eth.sendRawTransaction(signed_tx.rawTransaction)
# pylint: disable=too-many-arguments
def execute_method(
self,
abi: dict,
method: str,
args: Optional[Union[list, tuple]] = None,
tx_params: Optional[TxParams] = None,
view_only: bool = False,
) -> str:
"""Execute the method on a contract instance.
:param abi: dict of contract ABI
:param method: string name of method to call
:param args: default None, list or tuple of arguments for the method
:param tx_params: default None, :class:`TxParams` transaction params
:param view_only: default False, boolean of whether the transaction
should only be validated.
:returns: str of transaction hash
"""
contract_instance = self._contract_instance(
address=self.contract_address, abi=abi
)
if args is None:
args = []
if hasattr(contract_instance.functions, method):
func = getattr(contract_instance.functions, method)(*args)
return self._invoke_function_call(
func=func, tx_params=tx_params, view_only=view_only
)
raise Exception(
"No method {} found on contract {}.".format(
self.contract_address, method
)
)

View File

@ -0,0 +1,66 @@
"""Base wrapper class for accessing ethereum smart contracts."""
from typing import Any
from eth_utils import is_address, to_checksum_address
from web3 import Web3
from web3.providers.base import BaseProvider
from .tx_params import TxParams
class Validator:
"""Base class for validating inputs to methods."""
def __init__(self, provider: BaseProvider, contract_address: str):
"""Initialize the instance."""
def assert_valid(
self, method_name: str, parameter_name: str, argument_value: Any
):
"""Raise an exception if method input is not valid.
:param method_name: Name of the method whose input is to be validated.
:param parameter_name: Name of the parameter whose input is to be
validated.
:param argument_value: Value of argument to parameter to be validated.
"""
class ContractMethod:
"""Base class for wrapping an Ethereum smart contract method."""
def __init__(
self,
provider: BaseProvider,
contract_address: str,
validator: Validator = None,
):
"""Instantiate the object.
:param provider: Instance of :class:`web3.providers.base.BaseProvider`
: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
if validator is None:
validator = Validator(provider, contract_address)
self.validator = validator
@staticmethod
def validate_and_checksum_address(address: str):
"""Validate the given address, and return it's checksum address."""
if not is_address(address):
raise TypeError("Invalid address provided: {}".format(address))
return to_checksum_address(address)
def normalize_tx_params(self, tx_params) -> TxParams:
"""Normalize and return the given transaction parameters."""
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.validate_and_checksum_address(tx_params.from_)
return tx_params

View File

@ -6,21 +6,16 @@ from web3.providers.base import BaseProvider
from zero_ex import json_schemas from zero_ex import json_schemas
from . import ExchangeValidatorBase from ..bases import Validator
from .types import order_to_jsdict from .types import order_to_jsdict
class ExchangeValidator(ExchangeValidatorBase): class ExchangeValidator(Validator):
"""Validate inputs to Exchange methods.""" """Validate inputs to Exchange methods."""
def __init__( def __init__(self, provider: BaseProvider, contract_address: str):
self,
provider: BaseProvider,
contract_address: str,
private_key: str = None,
):
"""Initialize the class.""" """Initialize the class."""
super().__init__(provider, contract_address, private_key) super().__init__(provider, contract_address)
self.contract_address = contract_address self.contract_address = contract_address
def assert_valid( def assert_valid(

View File

@ -1,3 +1,7 @@
from typing import Union
def to_checksum_address(address: str) -> str: ... def to_checksum_address(address: str) -> str: ...
def remove_0x_prefix(hex_string: str) -> str: ... def remove_0x_prefix(hex_string: str) -> str: ...
def is_address(address: Union[str, bytes]) -> bool: ...

View File

@ -0,0 +1,5 @@
class ContractFunction:
def __call__(self, *args, **kwargs):
...
...

View File

@ -1,3 +1,10 @@
from typing import Any
class Contract: class Contract:
def call(self): ... def call(self): ...
functions: Any
events: Any
... ...

View File

@ -0,0 +1,15 @@
"""Tests for :class:`ContractMethod`."""
import pytest
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
from zero_ex.contract_wrappers.bases import ContractMethod
@pytest.fixture(scope="module")
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,
)

View File

@ -1,48 +0,0 @@
"""Tests for :class:`BaseContractWrapper`."""
import pytest
from eth_utils import to_checksum_address
from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId
from zero_ex.contract_artifacts import abi_by_name
from zero_ex.contract_wrappers._base_contract_wrapper import (
BaseContractWrapper,
)
@pytest.fixture(scope="module")
def contract_wrapper(ganache_provider):
"""Get a BaseContractWrapper instance for testing."""
return BaseContractWrapper(
provider=ganache_provider,
contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token,
)
def test_contract_wrapper__execute_method(
accounts,
contract_wrapper, # pylint: disable=redefined-outer-name
erc20_proxy_address,
):
"""Test :function:`BaseContractWrapper.execute` method."""
acc1_allowance = contract_wrapper.execute_method(
abi=abi_by_name("WETH9"),
method="allowance",
view_only=True,
args=(
to_checksum_address(accounts[3]),
to_checksum_address(erc20_proxy_address),
),
)
assert acc1_allowance == 0
with pytest.raises(Exception):
contract_wrapper.execute_method(
abi=abi_by_name("WETH9"),
method="send",
view_only=True,
args=[
to_checksum_address(accounts[3]),
to_checksum_address(erc20_proxy_address),
],
)

View File

@ -25,8 +25,8 @@ def test_erc20_wrapper__balance_of(
weth_instance, # pylint: disable=redefined-outer-name weth_instance, # pylint: disable=redefined-outer-name
): ):
"""Test getting baance of an account for an ERC20 token.""" """Test getting baance of an account for an ERC20 token."""
acc1_original_weth_balance = erc20_wrapper.balance_of(accounts[0]) acc1_original_weth_balance = erc20_wrapper.balance_of.call(accounts[0])
acc2_original_weth_balance = erc20_wrapper.balance_of(accounts[1]) acc2_original_weth_balance = erc20_wrapper.balance_of.call(accounts[1])
expected_difference = 1 * 10 ** 18 expected_difference = 1 * 10 ** 18
@ -36,8 +36,8 @@ def test_erc20_wrapper__balance_of(
weth_instance.functions.deposit().transact( weth_instance.functions.deposit().transact(
{"from": accounts[1], "value": expected_difference} {"from": accounts[1], "value": expected_difference}
) )
acc1_weth_balance = erc20_wrapper.balance_of(accounts[0]) acc1_weth_balance = erc20_wrapper.balance_of.call(accounts[0])
acc2_weth_balance = erc20_wrapper.balance_of(accounts[1]) acc2_weth_balance = erc20_wrapper.balance_of.call(accounts[1])
assert ( assert (
acc1_weth_balance - acc1_original_weth_balance == expected_difference acc1_weth_balance - acc1_original_weth_balance == expected_difference
@ -53,21 +53,21 @@ def test_erc20_wrapper__approve(
erc20_wrapper, # pylint: disable=redefined-outer-name erc20_wrapper, # pylint: disable=redefined-outer-name
): ):
"""Test approving one account to spend balance from another account.""" """Test approving one account to spend balance from another account."""
erc20_wrapper.approve( erc20_wrapper.approve.send_transaction(
erc20_proxy_address, erc20_proxy_address,
MAX_ALLOWANCE, MAX_ALLOWANCE,
tx_params=TxParams(from_=accounts[0]), tx_params=TxParams(from_=accounts[0]),
) )
erc20_wrapper.approve( erc20_wrapper.approve.send_transaction(
erc20_proxy_address, erc20_proxy_address,
MAX_ALLOWANCE, MAX_ALLOWANCE,
tx_params=TxParams(from_=accounts[1]), tx_params=TxParams(from_=accounts[1]),
) )
acc_1_weth_allowance = erc20_wrapper.allowance( acc_1_weth_allowance = erc20_wrapper.allowance.call(
accounts[0], erc20_proxy_address accounts[0], erc20_proxy_address
) )
acc_2_weth_allowance = erc20_wrapper.allowance( acc_2_weth_allowance = erc20_wrapper.allowance.call(
accounts[1], erc20_proxy_address accounts[1], erc20_proxy_address
) )

View File

@ -77,7 +77,7 @@ def test_exchange_wrapper__fill_order(
) )
order_signature = sign_hash_to_bytes(ganache_provider, maker, order_hash) order_signature = sign_hash_to_bytes(ganache_provider, maker, order_hash)
tx_hash = exchange_wrapper.fill_order( tx_hash = exchange_wrapper.fill_order.send_transaction(
order=order, order=order,
taker_asset_fill_amount=order["takerAssetAmount"], taker_asset_fill_amount=order["takerAssetAmount"],
signature=order_signature, signature=order_signature,
@ -114,7 +114,7 @@ def test_exchange_wrapper__batch_fill_orders(
for order_hash in order_hashes for order_hash in order_hashes
] ]
taker_amounts = [order["takerAssetAmount"] for order in orders] taker_amounts = [order["takerAssetAmount"] for order in orders]
tx_hash = exchange_wrapper.batch_fill_orders( tx_hash = exchange_wrapper.batch_fill_orders.send_transaction(
orders=orders, orders=orders,
taker_asset_fill_amounts=taker_amounts, taker_asset_fill_amounts=taker_amounts,
signatures=order_signatures, signatures=order_signatures,

View File

@ -40,7 +40,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
class LintCommand(distutils.command.build_py.build_py): class LintCommand(distutils.command.build_py.build_py):

View File

@ -20,7 +20,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
class LintCommand(distutils.command.build_py.build_py): class LintCommand(distutils.command.build_py.build_py):

View File

@ -21,7 +21,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
class LintCommand(distutils.command.build_py.build_py): class LintCommand(distutils.command.build_py.build_py):

View File

@ -20,9 +20,9 @@ $ ./parallel pip uninstall $(basename $(pwd))
>>>""" >>>"""
from concurrent.futures import ProcessPoolExecutor from concurrent.futures import ProcessPoolExecutor, wait
from os import chdir from os import chdir
from subprocess import check_call from subprocess import CalledProcessError, check_output
from sys import argv from sys import argv
PACKAGES = [ PACKAGES = [
@ -38,7 +38,18 @@ PACKAGES = [
def run_cmd_on_package(package: str): def run_cmd_on_package(package: str):
"""cd to the package dir, ./setup.py lint, cd ..""" """cd to the package dir, ./setup.py lint, cd .."""
chdir(package) chdir(package)
check_call(f"{' '.join(argv[1:])}".split()) try:
chdir("..") 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("..")
ProcessPoolExecutor().map(run_cmd_on_package, PACKAGES) with ProcessPoolExecutor() as executor:
for future in executor.map(run_cmd_on_package, PACKAGES):
# iterate over map()'s return value, to resolve the futures.
# but we don't actually care what the return values are, so just `pass`.
# if any exceptions were raised by the underlying task, they'll be
# raised as the iteration encounters them.
pass

View File

@ -33,7 +33,8 @@ class TestCommandExtension(TestCommand):
"""Invoke pytest.""" """Invoke pytest."""
import pytest import pytest
exit(pytest.main(["--doctest-modules"])) exit(pytest.main(["--doctest-modules", "-rapP"]))
# show short test summary at end ^
class TestPublishCommand(distutils.command.build_py.build_py): class TestPublishCommand(distutils.command.build_py.build_py):

View File

@ -319,7 +319,7 @@ book. Now let's have the taker fill it:
... provider=eth_node, ... provider=eth_node,
... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange
... ) ... )
>>> exchange.fill_order( >>> exchange.fill_order.send_transaction(
... order=order, ... order=order,
... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill ... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill
... signature=order['signature'].replace('0x', '').encode('utf-8'), ... signature=order['signature'].replace('0x', '').encode('utf-8'),
@ -333,7 +333,7 @@ Cancelling
Note that the above fill was partial: it only filled half of the order. Now Note that the above fill was partial: it only filled half of the order. Now
we'll have our maker cancel the remaining order: we'll have our maker cancel the remaining order:
>>> exchange.cancel_order( >>> exchange.cancel_order.send_transaction(
... order=order, ... order=order,
... tx_params=TxParams(from_=maker_address) ... tx_params=TxParams(from_=maker_address)
... ) ... )

View File

@ -655,21 +655,22 @@
dependencies: dependencies:
"@0x/base-contract" "^5.1.0" "@0x/base-contract" "^5.1.0"
"@0x/asset-buyer@6.1.4": "@0x/asset-buyer@6.1.8":
version "6.1.4" version "6.1.8"
resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.4.tgz#ea863b860fbae6f633846bdcf23cacbb345aefd1" resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.8.tgz#71f6abb366e89e62457c256644edb37e12113e94"
integrity sha512-JOMu38EsQexDVyzOGYJamlLDigVoGOBNHAVYIkPn4r3SLY75Shcxlb9AhUfKEMDPv3kjk70r0Iy1DmIh4HBjdA==
dependencies: dependencies:
"@0x/assert" "^2.0.10" "@0x/assert" "^2.1.0"
"@0x/connect" "^5.0.9" "@0x/connect" "^5.0.13"
"@0x/contract-wrappers" "^9.1.3" "@0x/contract-wrappers" "^9.1.7"
"@0x/json-schemas" "^3.0.10" "@0x/json-schemas" "^3.0.11"
"@0x/order-utils" "^8.1.0" "@0x/order-utils" "^8.2.2"
"@0x/subproviders" "^4.0.6" "@0x/subproviders" "^4.1.1"
"@0x/types" "^2.2.2" "@0x/types" "^2.4.0"
"@0x/typescript-typings" "^4.2.2" "@0x/typescript-typings" "^4.2.3"
"@0x/utils" "^4.3.3" "@0x/utils" "^4.4.0"
"@0x/web3-wrapper" "^6.0.6" "@0x/web3-wrapper" "^6.0.7"
ethereum-types "^2.1.2" ethereum-types "^2.1.3"
lodash "^4.17.11" lodash "^4.17.11"
"@0x/base-contract@^4.0.1", "@0x/base-contract@^4.0.3": "@0x/base-contract@^4.0.1", "@0x/base-contract@^4.0.3":
@ -716,7 +717,7 @@
lodash "^4.17.11" lodash "^4.17.11"
uuid "^3.3.2" uuid "^3.3.2"
"@0x/contract-wrappers@^9.1.3", "@0x/contract-wrappers@^9.1.7": "@0x/contract-wrappers@^9.1.7":
version "9.1.8" version "9.1.8"
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356" resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356"
integrity sha512-+qx0lwDAuTitP8sXA4bxRS+W81i5TaXWziD/Fcraj40XGeuHFnRXVY5FEl9V7TC0sEDyvnnF2VFc5Y+v2CnYVQ== integrity sha512-+qx0lwDAuTitP8sXA4bxRS+W81i5TaXWziD/Fcraj40XGeuHFnRXVY5FEl9V7TC0sEDyvnnF2VFc5Y+v2CnYVQ==
@ -829,7 +830,7 @@
ethers "~4.0.4" ethers "~4.0.4"
lodash "^4.17.11" lodash "^4.17.11"
"@0x/subproviders@^4.0.4", "@0x/subproviders@^4.0.6", "@0x/subproviders@^4.1.2": "@0x/subproviders@^4.0.4", "@0x/subproviders@^4.1.1":
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-4.1.2.tgz#ab7bb0f482b11ccb4615fb5dd8ca85199cd0ae23" resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-4.1.2.tgz#ab7bb0f482b11ccb4615fb5dd8ca85199cd0ae23"
integrity sha512-PaK/+cC6+o3glVITnBdb/AN/ej7ulfr49KGftNRATB8Y/yI6Xa3adqgFvDh7jiKBoB/auTRFQ/TabQTcieKl6g== integrity sha512-PaK/+cC6+o3glVITnBdb/AN/ej7ulfr49KGftNRATB8Y/yI6Xa3adqgFvDh7jiKBoB/auTRFQ/TabQTcieKl6g==
@ -2011,8 +2012,9 @@
"@types/react" "*" "@types/react" "*"
"@types/react-dom@^16.0.6": "@types/react-dom@^16.0.6":
version "16.0.11" version "16.8.5"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.11.tgz#bd10ccb0d9260343f4b9a49d4f7a8330a5c1f081" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.5.tgz#3e3f4d99199391a7fb40aa3a155c8dd99b899cbd"
integrity sha512-idCEjROZ2cqh29+trmTmZhsBAUNQuYrF92JHKzZ5+aiFM1mlSk3bb23CK7HhYuOY75Apgap5y2jTyHzaM2AJGA==
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
@ -2051,6 +2053,7 @@
"@types/react-redux@^4.4.37": "@types/react-redux@^4.4.37":
version "4.4.47" version "4.4.47"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-4.4.47.tgz#12af1677116e08d413fe2620d0a85560c8a0536e" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-4.4.47.tgz#12af1677116e08d413fe2620d0a85560c8a0536e"
integrity sha512-wyFTmLtEymHCjOmVVvsbNqJaGM9Q0x6sZTQfz4XkDj06P8Xe+ys9wKSQHx2Jt9J5Mi7HZnGcJaMFktn60sXluw==
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
redux "^3.6.0" redux "^3.6.0"
@ -4300,7 +4303,7 @@ buffer-equal@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
buffer-from@1.x, buffer-from@^1.1.0: buffer-from@1.x, buffer-from@>=1.1, buffer-from@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@ -9287,6 +9290,13 @@ hoist-non-react-statics@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40"
hoist-non-react-statics@^3.1.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
dependencies:
react-is "^16.7.0"
home-or-tmp@^2.0.0: home-or-tmp@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@ -11428,10 +11438,15 @@ locate-path@^3.0.0:
p-locate "^3.0.0" p-locate "^3.0.0"
path-exists "^3.0.0" path-exists "^3.0.0"
lodash-es@^4.17.5, lodash-es@^4.2.1: lodash-es@^4.17.5:
version "4.17.8" version "4.17.8"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45"
lodash-es@^4.2.1:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
lodash._basecopy@^3.0.0: lodash._basecopy@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
@ -14956,11 +14971,16 @@ react-is@^16.6.0:
version "16.6.3" version "16.6.3"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0"
react-is@^16.7.0:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
react-lazyload@^2.3.0: react-lazyload@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/react-lazyload/-/react-lazyload-2.3.0.tgz#ccb134223012447074a96543954f44b055dc6185" resolved "https://registry.yarnpkg.com/react-lazyload/-/react-lazyload-2.3.0.tgz#ccb134223012447074a96543954f44b055dc6185"
react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@ -15000,7 +15020,20 @@ react-popper@^1.0.0-beta.6:
typed-styles "^0.0.5" typed-styles "^0.0.5"
warning "^3.0.0" warning "^3.0.0"
react-redux@^5.0.3, react-redux@^5.0.7: react-redux@^5.0.3:
version "5.1.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f"
integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==
dependencies:
"@babel/runtime" "^7.1.2"
hoist-non-react-statics "^3.1.0"
invariant "^2.2.4"
loose-envify "^1.1.0"
prop-types "^15.6.1"
react-is "^16.6.0"
react-lifecycles-compat "^3.0.0"
react-redux@^5.0.7:
version "5.0.7" version "5.0.7"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8"
dependencies: dependencies:
@ -15414,8 +15447,9 @@ reduce-function-call@^1.0.1:
balanced-match "^0.4.2" balanced-match "^0.4.2"
redux-devtools-extension@^2.13.2: redux-devtools-extension@^2.13.2:
version "2.13.2" version "2.13.8"
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d" resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1"
integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==
redux-devtools-extension@^2.13.5: redux-devtools-extension@^2.13.5:
version "2.13.5" version "2.13.5"
@ -15431,6 +15465,7 @@ redux@*, redux@^4.0.0:
redux@^3.6.0: redux@^3.6.0:
version "3.7.2" version "3.7.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==
dependencies: dependencies:
lodash "^4.2.1" lodash "^4.2.1"
lodash-es "^4.2.1" lodash-es "^4.2.1"
@ -15916,16 +15951,19 @@ rollbar-sourcemap-webpack-plugin@^2.4.0:
verror "^1.6.1" verror "^1.6.1"
rollbar@^2.4.7: rollbar@^2.4.7:
version "2.4.7" version "2.10.0"
resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.4.7.tgz#9b1de1a0fab6b6e63fcfcd322c26081a1d8242e8" resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.10.0.tgz#a18c333eb45ebd21be128afdf9bf3a0093be7218"
integrity sha512-Ifr+KB8cXsaE9Lae3o2X7jx+WAAHMxul44Bj+P+0uGxbTO4TcHFRloUWgPVtmHxuFjpDchHbhwMwMicTseEb1Q==
dependencies: dependencies:
async "~1.2.1" async "~1.2.1"
buffer-from ">=1.1"
console-polyfill "0.3.0" console-polyfill "0.3.0"
debug "2.6.9" debug "2.6.9"
error-stack-parser "1.3.3" error-stack-parser "1.3.3"
json-stringify-safe "~5.0.0" json-stringify-safe "~5.0.0"
lru-cache "~2.2.1" lru-cache "~2.2.1"
request-ip "~2.0.1" request-ip "~2.0.1"
source-map ">=0.5.0"
uuid "3.0.x" uuid "3.0.x"
optionalDependencies: optionalDependencies:
decache "^3.0.5" decache "^3.0.5"
@ -16733,6 +16771,11 @@ source-map-url@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
source-map@>=0.5.0:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
source-map@^0.4.2: source-map@^0.4.2:
version "0.4.4" version "0.4.4"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" resolved "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"