diff --git a/packages/abi-gen-templates/Python/contract.handlebars b/packages/abi-gen-templates/Python/contract.handlebars index 25eaa2980b..a71adfd9e1 100644 --- a/packages/abi-gen-templates/Python/contract.handlebars +++ b/packages/abi-gen-templates/Python/contract.handlebars @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict 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 -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, # declare one that we can instantiate for the default argument to the # constructor for {{contractName}} below. @@ -53,56 +34,52 @@ try: ) except ImportError: - class {{contractName}}Validator({{contractName}}ValidatorBase): # type: ignore + class {{contractName}}Validator(Validator): # type: ignore """No-op input validator.""" {{tupleDefinitions ABIString}} +{{#each methods}} +{{> method_class contractName=../contractName}} +{{/each}} -# pylint: disable=too-many-public-methods -class {{contractName}}(BaseContractWrapper): +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class {{contractName}}: """Wrapper class for {{contractName}} Solidity contract.{{docBytesIfNecessary ABIString}}""" +{{#each methods}} + {{toPythonIdentifier this.name}}: {{toPythonClassname this.name}}Method +{{/each}} def __init__( self, provider: BaseProvider, contract_address: str, validator: {{contractName}}Validator = None, - private_key: str = 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 private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. + :param validator: for validation of method inputs. """ - super().__init__( - provider=provider, - contract_address=contract_address, - private_key=private_key, - ) + self.contract_address = contract_address 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): - """Get an instance of the smart contract at a specific address. + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions - :returns: contract object - """ - return self._contract_instance( - address=token_address, abi={{contractName}}.abi() - ) -{{#each methods}} -{{> call contractName=../contractName}} -{{/each}} + {{#each methods}} + self.{{toPythonIdentifier this.name}} = {{toPythonClassname this.name}}Method(provider, contract_address, functions.{{this.name}}, validator) + + {{/each}} {{#each events}} -{{> event}} +{{> event contractName=../contractName}} {{/each}} @staticmethod diff --git a/packages/abi-gen-templates/Python/partials/call.handlebars b/packages/abi-gen-templates/Python/partials/call.handlebars deleted file mode 100644 index f0756937df..0000000000 --- a/packages/abi-gen-templates/Python/partials/call.handlebars +++ /dev/null @@ -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}} - ) diff --git a/packages/abi-gen-templates/Python/partials/event.handlebars b/packages/abi-gen-templates/Python/partials/event.handlebars index 461f38b3ec..c33d9ce1b2 100644 --- a/packages/abi-gen-templates/Python/partials/event.handlebars +++ b/packages/abi-gen-templates/Python/partials/event.handlebars @@ -6,8 +6,4 @@ {{makeEventParameterDocstringRole name 8}} """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) - return ( - self._get_contract_instance(self.contract_address) - .events.{{name}}() - .processReceipt(tx_receipt) - ) + return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi={{contractName}}.abi()).events.{{name}}().processReceipt(tx_receipt) diff --git a/packages/abi-gen-templates/Python/partials/method_class.handlebars b/packages/abi-gen-templates/Python/partials/method_class.handlebars new file mode 100644 index 0000000000..e2abdf1958 --- /dev/null +++ b/packages/abi-gen-templates/Python/partials/method_class.handlebars @@ -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()) diff --git a/packages/abi-gen-templates/Python/partials/params.handlebars b/packages/abi-gen-templates/Python/partials/params.handlebars index 1b2b86e6a5..98b3dfa702 100644 --- a/packages/abi-gen-templates/Python/partials/params.handlebars +++ b/packages/abi-gen-templates/Python/partials/params.handlebars @@ -1,3 +1,3 @@ {{#each inputs}} -{{toPythonIdentifier name}}{{#if @last}}{{else}},{{/if}} -{{/each}} +{{toPythonIdentifier name}}{{#if @last}}{{else}}, {{/if~}} +{{/each~}} diff --git a/packages/abi-gen-templates/Python/partials/typed_params.handlebars b/packages/abi-gen-templates/Python/partials/typed_params.handlebars index 640d1188e5..a67cd0c201 100644 --- a/packages/abi-gen-templates/Python/partials/typed_params.handlebars +++ b/packages/abi-gen-templates/Python/partials/typed_params.handlebars @@ -1,3 +1,3 @@ {{#each inputs}} -{{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}, -{{/each}} +{{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}{{^if @last}}, {{/if~}} +{{/each~}} diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 4ea1b6c6ce..5ba78c5a37 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -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", "changes": [ { "note": "Dependencies updated" } - ] + ], + "timestamp": 1564604963 }, { "version": "3.1.1", diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index be33f6a38d..56a62d4cf5 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -23,6 +23,7 @@ "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: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", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index f650c3553c..b103f6a805 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -225,6 +225,10 @@ function registerPythonHelpers(): void { } return ''; }); + Handlebars.registerHelper( + 'toPythonClassname', + (sourceName: string) => new Handlebars.SafeString(changeCase.pascal(sourceName)), + ); } if (args.language === 'TypeScript') { registerTypeScriptHelpers(); diff --git a/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py index 0b33e533be..79e6e79406 100644 --- a/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict 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 -class AbiGenDummyValidatorBase: - """Base class for validating inputs to AbiGenDummy 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, # declare one that we can instantiate for the default argument to the # constructor for AbiGenDummy below. @@ -53,7 +34,7 @@ try: ) except ImportError: - class AbiGenDummyValidator(AbiGenDummyValidatorBase): # type: ignore + class AbiGenDummyValidator(Validator): # type: ignore """No-op input validator.""" @@ -102,81 +83,47 @@ class Tuple0xcf8ad995(TypedDict): aString: str -# pylint: disable=too-many-public-methods -class AbiGenDummy(BaseContractWrapper): - """Wrapper class for AbiGenDummy Solidity contract. +class SimpleRequireMethod(ContractMethod): + """Various interfaces to the simpleRequire method.""" - All method parameters of type `bytes`:code: should be encoded as UTF-8, - which can be accomplished via `str.encode("utf_8")`:code:. - """ + 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 __init__( - self, - provider: BaseProvider, - contract_address: str, - validator: AbiGenDummyValidator = None, - private_key: str = 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 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 = AbiGenDummyValidator(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=AbiGenDummy.abi() - ) - - def simple_require( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.simpleRequire( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) - def accepts_an_array_of_bytes( - self, - a: List[bytes], - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. - a method that accepts an array of bytes - - :param a: the array of bytes being accepted :param tx_params: transaction parameters """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class AcceptsAnArrayOfBytesMethod(ContractMethod): + """Various interfaces to the acceptsAnArrayOfBytes 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, a: List[bytes]): + """Validate the inputs to the acceptsAnArrayOfBytes method.""" self.validator.assert_valid( method_name='acceptsAnArrayOfBytes', parameter_name='a', @@ -186,32 +133,50 @@ class AbiGenDummy(BaseContractWrapper): bytes.fromhex(a_element.decode("utf-8")) for a_element in a ] - func = self._get_contract_instance( - self.contract_address - ).functions.acceptsAnArrayOfBytes( - a - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (a) - def withdraw( - self, - wad: int, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> Union[None, Union[HexBytes, bytes]]: - """Execute underlying, same-named contract method. + def call(self, a: List[bytes], tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. + a method that accepts an array of bytes + + :param a: the array of bytes being accepted :param tx_params: transaction parameters - :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. """ + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).call(tx_params.as_dict()) + + def send_transaction(self, a: List[bytes], tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + a method that accepts an array of bytes + + :param a: the array of bytes being accepted + :param tx_params: transaction parameters + + """ + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).transact(tx_params.as_dict()) + + def estimate_gas(self, a: List[bytes], tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).estimateGas(tx_params.as_dict()) + +class WithdrawMethod(ContractMethod): + """Various interfaces to the withdraw 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, wad: int): + """Validate the inputs to the withdraw method.""" self.validator.assert_valid( method_name='withdraw', parameter_name='wad', @@ -219,30 +184,43 @@ class AbiGenDummy(BaseContractWrapper): ) # safeguard against fractional inputs wad = int(wad) - func = self._get_contract_instance( - self.contract_address - ).functions.withdraw( - wad - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=view_only - ) + return (wad) - def ecrecover_fn( - self, - _hash: bytes, - v: int, - r: bytes, - s: bytes, - tx_params: Optional[TxParams] = None, - ) -> str: - """Execute underlying, same-named contract method. + def call(self, wad: int, tx_params: Optional[TxParams] = None) -> Union[None, Union[HexBytes, bytes]]: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters - + :returns: the return value of the underlying method. """ + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(wad).call(tx_params.as_dict()) + + def send_transaction(self, wad: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + """ + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(wad).transact(tx_params.as_dict()) + + def estimate_gas(self, wad: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(wad).estimateGas(tx_params.as_dict()) + +class EcrecoverFnMethod(ContractMethod): + """Various interfaces to the ecrecoverFn 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, _hash: bytes, v: int, r: bytes, s: bytes): + """Validate the inputs to the ecrecoverFn method.""" self.validator.assert_valid( method_name='ecrecoverFn', parameter_name='hash', @@ -263,143 +241,218 @@ class AbiGenDummy(BaseContractWrapper): parameter_name='s', argument_value=s, ) - func = self._get_contract_instance( - self.contract_address - ).functions.ecrecoverFn( - _hash, - v, - r, - s - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (_hash, v, r, s) - def accepts_bytes( - self, - a: bytes, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, _hash: bytes, v: int, r: bytes, s: bytes, tx_params: Optional[TxParams] = None) -> str: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (_hash, v, r, s) = self.validate_and_normalize_inputs(_hash, v, r, s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(_hash, v, r, s).call(tx_params.as_dict()) + + def send_transaction(self, _hash: bytes, v: int, r: bytes, s: bytes, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (_hash, v, r, s) = self.validate_and_normalize_inputs(_hash, v, r, s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(_hash, v, r, s).transact(tx_params.as_dict()) + + def estimate_gas(self, _hash: bytes, v: int, r: bytes, s: bytes, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (_hash, v, r, s) = self.validate_and_normalize_inputs(_hash, v, r, s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(_hash, v, r, s).estimateGas(tx_params.as_dict()) + +class AcceptsBytesMethod(ContractMethod): + """Various interfaces to the acceptsBytes 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, a: bytes): + """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")) - func = self._get_contract_instance( - self.contract_address - ).functions.acceptsBytes( - a - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (a) - def revert_with_constant( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, a: bytes, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.revertWithConstant( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).call(tx_params.as_dict()) - def simple_revert( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, a: bytes, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.simpleRevert( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).transact(tx_params.as_dict()) - def nested_struct_output( - self, - tx_params: Optional[TxParams] = None, - ) -> Tuple0xc9bdd2d5: - """Execute underlying, same-named contract method. + def estimate_gas(self, a: bytes, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).estimateGas(tx_params.as_dict()) + +class RevertWithConstantMethod(ContractMethod): + """Various interfaces to the revertWithConstant 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 call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.nestedStructOutput( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) - def require_with_constant( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.requireWithConstant( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) - def with_address_input( - self, - x: str, - a: int, - b: int, - y: str, - c: int, - tx_params: Optional[TxParams] = None, - ) -> str: - """Execute underlying, same-named contract method. + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class SimpleRevertMethod(ContractMethod): + """Various interfaces to the simpleRevert 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 call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class NestedStructOutputMethod(ContractMethod): + """Various interfaces to the nestedStructOutput 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 call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class RequireWithConstantMethod(ContractMethod): + """Various interfaces to the requireWithConstant 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 call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class WithAddressInputMethod(ContractMethod): + """Various interfaces to the withAddressInput 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: str, a: int, b: int, y: str, c: int): + """Validate the inputs to the withAddressInput method.""" self.validator.assert_valid( method_name='withAddressInput', parameter_name='x', argument_value=x, ) - x = self._validate_and_checksum_address(x) + x = self.validate_and_checksum_address(x) self.validator.assert_valid( method_name='withAddressInput', parameter_name='a', @@ -419,7 +472,7 @@ class AbiGenDummy(BaseContractWrapper): parameter_name='y', argument_value=y, ) - y = self._validate_and_checksum_address(y) + y = self.validate_and_checksum_address(y) self.validator.assert_valid( method_name='withAddressInput', parameter_name='c', @@ -427,81 +480,117 @@ class AbiGenDummy(BaseContractWrapper): ) # safeguard against fractional inputs c = int(c) - func = self._get_contract_instance( - self.contract_address - ).functions.withAddressInput( - x, - a, - b, - y, - c - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x, a, b, y, c) - def struct_input( - self, - s: Tuple0xcf8ad995, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, x: str, a: int, b: int, y: str, c: int, tx_params: Optional[TxParams] = None) -> str: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (x, a, b, y, c) = self.validate_and_normalize_inputs(x, a, b, y, c) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x, a, b, y, c).call(tx_params.as_dict()) + + def send_transaction(self, x: str, a: int, b: int, y: str, c: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (x, a, b, y, c) = self.validate_and_normalize_inputs(x, a, b, y, c) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x, a, b, y, c).transact(tx_params.as_dict()) + + def estimate_gas(self, x: str, a: int, b: int, y: str, c: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (x, a, b, y, c) = self.validate_and_normalize_inputs(x, a, b, y, c) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x, a, b, y, c).estimateGas(tx_params.as_dict()) + +class StructInputMethod(ContractMethod): + """Various interfaces to the structInput 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, s: Tuple0xcf8ad995): + """Validate the inputs to the structInput method.""" self.validator.assert_valid( method_name='structInput', parameter_name='s', argument_value=s, ) - func = self._get_contract_instance( - self.contract_address - ).functions.structInput( - s - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (s) - def non_pure_method( - self, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> Union[int, Union[HexBytes, bytes]]: - """Execute underlying, same-named contract method. - - :param tx_params: transaction parameters - :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. - """ - func = self._get_contract_instance( - self.contract_address - ).functions.nonPureMethod( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=view_only - ) - - def simple_pure_function_with_input( - self, - x: int, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def call(self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (s) = self.validate_and_normalize_inputs(s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(s).call(tx_params.as_dict()) + + def send_transaction(self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (s) = self.validate_and_normalize_inputs(s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(s).transact(tx_params.as_dict()) + + def estimate_gas(self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (s) = self.validate_and_normalize_inputs(s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(s).estimateGas(tx_params.as_dict()) + +class NonPureMethodMethod(ContractMethod): + """Various interfaces to the nonPureMethod 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 call(self, tx_params: Optional[TxParams] = None) -> Union[int, Union[HexBytes, bytes]]: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + :returns: the return value of the underlying method. + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class SimplePureFunctionWithInputMethod(ContractMethod): + """Various interfaces to the simplePureFunctionWithInput 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 simplePureFunctionWithInput method.""" self.validator.assert_valid( method_name='simplePureFunctionWithInput', parameter_name='x', @@ -509,125 +598,289 @@ class AbiGenDummy(BaseContractWrapper): ) # safeguard against fractional inputs x = int(x) - func = self._get_contract_instance( - self.contract_address - ).functions.simplePureFunctionWithInput( - x - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x) - def non_pure_method_that_returns_nothing( - self, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> Union[None, Union[HexBytes, bytes]]: - """Execute underlying, same-named contract method. - - :param tx_params: transaction parameters - :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. - """ - func = self._get_contract_instance( - self.contract_address - ).functions.nonPureMethodThatReturnsNothing( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=view_only - ) - - def simple_pure_function( - self, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def call(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.simplePureFunction( - ) - return self._invoke_function_call( - 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 nested_struct_input( - self, - n: Tuple0xc9bdd2d5, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + 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 NonPureMethodThatReturnsNothingMethod(ContractMethod): + """Various interfaces to the nonPureMethodThatReturnsNothing 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 call(self, tx_params: Optional[TxParams] = None) -> Union[None, Union[HexBytes, bytes]]: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + :returns: the return value of the underlying method. + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class SimplePureFunctionMethod(ContractMethod): + """Various interfaces to the simplePureFunction 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 call(self, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class NestedStructInputMethod(ContractMethod): + """Various interfaces to the nestedStructInput 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, n: Tuple0xc9bdd2d5): + """Validate the inputs to the nestedStructInput method.""" self.validator.assert_valid( method_name='nestedStructInput', parameter_name='n', argument_value=n, ) - func = self._get_contract_instance( - self.contract_address - ).functions.nestedStructInput( - n - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (n) - def struct_output( - self, - tx_params: Optional[TxParams] = None, - ) -> Tuple0xcf8ad995: - """Execute underlying, same-named contract method. + def call(self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + (n) = self.validate_and_normalize_inputs(n) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(n).call(tx_params.as_dict()) + + def send_transaction(self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (n) = self.validate_and_normalize_inputs(n) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(n).transact(tx_params.as_dict()) + + def estimate_gas(self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (n) = self.validate_and_normalize_inputs(n) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(n).estimateGas(tx_params.as_dict()) + +class StructOutputMethod(ContractMethod): + """Various interfaces to the structOutput 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 call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995: + """Execute underlying contract method via eth_call. a method that returns a struct :param tx_params: transaction parameters :returns: a Struct struct """ - func = self._get_contract_instance( - self.contract_address - ).functions.structOutput( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) - def pure_function_with_constant( - self, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + a method that returns a struct + + :param tx_params: transaction parameters + :returns: a Struct struct + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class PureFunctionWithConstantMethod(ContractMethod): + """Various interfaces to the pureFunctionWithConstant 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 call(self, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.pureFunctionWithConstant( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class AbiGenDummy: + """Wrapper class for AbiGenDummy Solidity contract. + + All method parameters of type `bytes`:code: should be encoded as UTF-8, + which can be accomplished via `str.encode("utf_8")`:code:. + """ + simple_require: SimpleRequireMethod + accepts_an_array_of_bytes: AcceptsAnArrayOfBytesMethod + withdraw: WithdrawMethod + ecrecover_fn: EcrecoverFnMethod + accepts_bytes: AcceptsBytesMethod + revert_with_constant: RevertWithConstantMethod + simple_revert: SimpleRevertMethod + nested_struct_output: NestedStructOutputMethod + require_with_constant: RequireWithConstantMethod + with_address_input: WithAddressInputMethod + struct_input: StructInputMethod + non_pure_method: NonPureMethodMethod + simple_pure_function_with_input: SimplePureFunctionWithInputMethod + non_pure_method_that_returns_nothing: NonPureMethodThatReturnsNothingMethod + simple_pure_function: SimplePureFunctionMethod + nested_struct_input: NestedStructInputMethod + struct_output: StructOutputMethod + pure_function_with_constant: PureFunctionWithConstantMethod + + def __init__( + self, + provider: 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 contract_address: where the contract has been deployed + :param validator: for validation of method inputs. + """ + self.contract_address = contract_address + + if not validator: + validator = AbiGenDummyValidator(provider, contract_address) + + self._web3_eth = Web3( # type: ignore # pylint: disable=no-member + provider + ).eth + + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=AbiGenDummy.abi()).functions + + self.simple_require = SimpleRequireMethod(provider, contract_address, functions.simpleRequire, validator) + + self.accepts_an_array_of_bytes = AcceptsAnArrayOfBytesMethod(provider, contract_address, functions.acceptsAnArrayOfBytes, validator) + + self.withdraw = WithdrawMethod(provider, contract_address, functions.withdraw, validator) + + self.ecrecover_fn = EcrecoverFnMethod(provider, contract_address, functions.ecrecoverFn, validator) + + self.accepts_bytes = AcceptsBytesMethod(provider, contract_address, functions.acceptsBytes, validator) + + self.revert_with_constant = RevertWithConstantMethod(provider, contract_address, functions.revertWithConstant, validator) + + self.simple_revert = SimpleRevertMethod(provider, contract_address, functions.simpleRevert, validator) + + self.nested_struct_output = NestedStructOutputMethod(provider, contract_address, functions.nestedStructOutput, validator) + + self.require_with_constant = RequireWithConstantMethod(provider, contract_address, functions.requireWithConstant, validator) + + self.with_address_input = WithAddressInputMethod(provider, contract_address, functions.withAddressInput, validator) + + self.struct_input = StructInputMethod(provider, contract_address, functions.structInput, validator) + + self.non_pure_method = NonPureMethodMethod(provider, contract_address, functions.nonPureMethod, validator) + + self.simple_pure_function_with_input = SimplePureFunctionWithInputMethod(provider, contract_address, functions.simplePureFunctionWithInput, validator) + + self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod(provider, contract_address, functions.nonPureMethodThatReturnsNothing, validator) + + self.simple_pure_function = SimplePureFunctionMethod(provider, contract_address, functions.simplePureFunction, validator) + + self.nested_struct_input = NestedStructInputMethod(provider, contract_address, functions.nestedStructInput, validator) + + self.struct_output = StructOutputMethod(provider, contract_address, functions.structOutput, validator) + + self.pure_function_with_constant = PureFunctionWithConstantMethod(provider, contract_address, functions.pureFunctionWithConstant, validator) + def get_withdrawal_event( self, tx_hash: Union[HexBytes, bytes] ) -> Tuple[AttributeDict]: @@ -636,11 +889,7 @@ class AbiGenDummy(BaseContractWrapper): :param tx_hash: hash of transaction emitting Withdrawal event """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) - return ( - self._get_contract_instance(self.contract_address) - .events.Withdrawal() - .processReceipt(tx_receipt) - ) + return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi=AbiGenDummy.abi()).events.Withdrawal().processReceipt(tx_receipt) def get_an_event_event( self, tx_hash: Union[HexBytes, bytes] ) -> Tuple[AttributeDict]: @@ -649,11 +898,7 @@ class AbiGenDummy(BaseContractWrapper): :param tx_hash: hash of transaction emitting AnEvent event """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) - return ( - self._get_contract_instance(self.contract_address) - .events.AnEvent() - .processReceipt(tx_receipt) - ) + return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi=AbiGenDummy.abi()).events.AnEvent().processReceipt(tx_receipt) @staticmethod def abi(): diff --git a/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py b/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py index 66bb91e528..fee8d1da38 100644 --- a/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict 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 -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, # declare one that we can instantiate for the default argument to the # constructor for LibDummy below. @@ -53,15 +34,15 @@ try: ) except ImportError: - class LibDummyValidator(LibDummyValidatorBase): # type: ignore + class LibDummyValidator(Validator): # type: ignore """No-op input validator.""" -# pylint: disable=too-many-public-methods -class LibDummy(BaseContractWrapper): +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class LibDummy: """Wrapper class for LibDummy Solidity contract.""" def __init__( @@ -69,35 +50,24 @@ class LibDummy(BaseContractWrapper): provider: BaseProvider, contract_address: str, validator: LibDummyValidator = None, - private_key: str = 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 private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. + :param validator: for validation of method inputs. """ - super().__init__( - provider=provider, - contract_address=contract_address, - private_key=private_key, - ) + self.contract_address = contract_address 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): - """Get an instance of the smart contract at a specific address. + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=LibDummy.abi()).functions - :returns: contract object - """ - return self._contract_instance( - address=token_address, abi=LibDummy.abi() - ) @staticmethod def abi(): diff --git a/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py index 5c4cab81d8..2f40a16476 100644 --- a/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict 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 -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, # declare one that we can instantiate for the default argument to the # constructor for TestLibDummy below. @@ -53,62 +34,23 @@ try: ) except ImportError: - class TestLibDummyValidator(TestLibDummyValidatorBase): # type: ignore + class TestLibDummyValidator(Validator): # type: ignore """No-op input validator.""" -# pylint: disable=too-many-public-methods -class TestLibDummy(BaseContractWrapper): - """Wrapper class for TestLibDummy Solidity contract.""" +class PublicAddConstantMethod(ContractMethod): + """Various interfaces to the publicAddConstant method.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - validator: TestLibDummyValidator = None, - private_key: str = None, - ): - """Get an instance of wrapper for smart contract. + 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 - :param provider: instance of :class:`web3.providers.base.BaseProvider` - :param contract_address: where the contract has been deployed - :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 - - """ + def validate_and_normalize_inputs(self, x: int): + """Validate the inputs to the publicAddConstant method.""" self.validator.assert_valid( method_name='publicAddConstant', parameter_name='x', @@ -116,27 +58,44 @@ class TestLibDummy(BaseContractWrapper): ) # safeguard against fractional inputs x = int(x) - func = self._get_contract_instance( - self.contract_address - ).functions.publicAddConstant( - x - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x) - def public_add_one( - self, - x: int, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def call(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. :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( method_name='publicAddOne', parameter_name='x', @@ -144,16 +103,67 @@ class TestLibDummy(BaseContractWrapper): ) # safeguard against fractional inputs x = int(x) - func = self._get_contract_instance( - self.contract_address - ).functions.publicAddOne( - x - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x) + + def call(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. + + :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()) + +# 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 def abi(): diff --git a/packages/instant/package.json b/packages/instant/package.json index 6e783a5451..ea16a2ef1a 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -44,14 +44,14 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { "@0x/assert": "^2.1.0", - "@0x/asset-buyer": "6.1.4", + "@0x/asset-buyer": "6.1.8", "@0x/json-schemas": "^3.0.11", "@0x/order-utils": "^8.2.2", - "@0x/subproviders": "^4.1.2", + "@0x/subproviders": "^5.0.0", "@0x/types": "^2.4.1", - "@0x/typescript-typings": "^4.2.3", - "@0x/utils": "^4.4.1", - "@0x/web3-wrapper": "^6.0.8", + "@0x/typescript-typings": "^4.2.4", + "@0x/utils": "^4.4.2", + "@0x/web3-wrapper": "^6.0.9", "babel-runtime": "^6.26.0", "bowser": "^1.9.4", "copy-to-clipboard": "^3.0.8", diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json index 352d2fa59a..69fe1babe8 100644 --- a/packages/monorepo-scripts/package.json +++ b/packages/monorepo-scripts/package.json @@ -49,7 +49,7 @@ }, "dependencies": { "@0x/types": "^2.4.1", - "@0x/utils": "^4.4.1", + "@0x/utils": "^4.4.2", "@lerna/batch-packages": "^3.0.0-beta.18", "@types/depcheck": "^0.6.0", "async-child-process": "^1.1.1", diff --git a/packages/python-contract-wrappers/package.json b/packages/python-contract-wrappers/package.json index 4d27dcd5ce..7773577725 100644 --- a/packages/python-contract-wrappers/package.json +++ b/packages/python-contract-wrappers/package.json @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/python-contract-wrappers/README.md", "devDependencies": { - "@0x/abi-gen": "^3.1.1", + "@0x/abi-gen": "^3.1.2", "shx": "^0.2.2" }, "publishConfig": { diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 944941f560..2aab54c149 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -20,10 +20,10 @@ "license": "Apache-2.0", "dependencies": { "0x.js": "^6.0.12", - "@0x/subproviders": "^4.1.2", + "@0x/subproviders": "^5.0.0", "@0x/typescript-typings": "^4.2.4", - "@0x/utils": "^4.4.1", - "@0x/web3-wrapper": "^6.0.8", + "@0x/utils": "^4.4.2", + "@0x/web3-wrapper": "^6.0.9", "body-parser": "^1.17.1", "ethereum-types": "^2.1.4", "ethereumjs-tx": "^1.3.5", diff --git a/packages/website/package.json b/packages/website/package.json index b112590361..ed409026c7 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -26,11 +26,11 @@ "@0x/contract-wrappers": "^9.1.7", "@0x/json-schemas": "^3.0.11", "@0x/order-utils": "^8.2.2", - "@0x/subproviders": "^4.1.2", + "@0x/subproviders": "^5.0.0", "@0x/types": "^2.4.1", "@0x/typescript-typings": "^4.2.4", - "@0x/utils": "^4.4.1", - "@0x/web3-wrapper": "^6.0.8", + "@0x/utils": "^4.4.2", + "@0x/web3-wrapper": "^6.0.9", "@reach/dialog": "^0.1.2", "@types/react-lazyload": "^2.3.1", "@types/react-loadable": "^5.4.2", diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index f197122930..1aa2231097 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -109,7 +109,8 @@ class TestCommandExtension(TestCommand): """Invoke 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: diff --git a/python-packages/contract_artifacts/setup.py b/python-packages/contract_artifacts/setup.py index 14db18fcb3..101fb2fc41 100755 --- a/python-packages/contract_artifacts/setup.py +++ b/python-packages/contract_artifacts/setup.py @@ -138,7 +138,8 @@ class TestCommandExtension(TestCommand): """Invoke 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: diff --git a/python-packages/contract_wrappers/setup.cfg b/python-packages/contract_wrappers/setup.cfg new file mode 100644 index 0000000000..e37dc107e7 --- /dev/null +++ b/python-packages/contract_wrappers/setup.cfg @@ -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 diff --git a/python-packages/contract_wrappers/setup.py b/python-packages/contract_wrappers/setup.py index 7d7da3da6c..cf50679cc7 100755 --- a/python-packages/contract_wrappers/setup.py +++ b/python-packages/contract_wrappers/setup.py @@ -74,7 +74,8 @@ class TestCommandExtension(TestCommand): """Invoke 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): diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py index a92a6d8653..059273bd53 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py @@ -102,13 +102,13 @@ balance: >>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy ->>> tx = zrx_token.approve( +>>> tx = zrx_token.approve.send_transaction( ... erc20_proxy_addr, ... to_wei(100, 'ether'), ... tx_params=TxParams(from_=maker_address), ... ) ->>> tx = weth_token.approve( +>>> tx = weth_token.approve.send_transaction( ... erc20_proxy_addr, ... to_wei(100, 'ether'), ... tx_params=TxParams(from_=taker_address), @@ -166,7 +166,7 @@ too. ... provider=ganache, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, ... ) ->>> tx_hash = exchange.fill_order( +>>> tx_hash = exchange.fill_order.send_transaction( ... order=order, ... taker_asset_fill_amount=order["takerAssetAmount"], ... 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) ... ) @@ -287,12 +287,40 @@ is an example where the taker fills two orders in one transaction: Fill order_1 and order_2 together: ->>> exchange.batch_fill_orders( +>>> exchange.batch_fill_orders.send_transaction( ... orders=[order_1, order_2], ... taker_asset_fill_amounts=[1, 2], ... signatures=[signature_1, signature_2], ... tx_params=TxParams(from_=taker_address)) 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 diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py deleted file mode 100644 index 0ac4847f8b..0000000000 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py +++ /dev/null @@ -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 - ) - ) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py new file mode 100644 index 0000000000..39280f4efe --- /dev/null +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py @@ -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 diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/.gitkeep b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py index 27932db2bd..2479ee8179 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py @@ -6,21 +6,16 @@ from web3.providers.base import BaseProvider from zero_ex import json_schemas -from . import ExchangeValidatorBase +from ..bases import Validator from .types import order_to_jsdict -class ExchangeValidator(ExchangeValidatorBase): +class ExchangeValidator(Validator): """Validate inputs to Exchange methods.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): + def __init__(self, provider: BaseProvider, contract_address: str): """Initialize the class.""" - super().__init__(provider, contract_address, private_key) + super().__init__(provider, contract_address) self.contract_address = contract_address def assert_valid( diff --git a/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi b/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi index 90e1277eb0..c541dc5e42 100644 --- a/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi +++ b/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi @@ -1,3 +1,7 @@ +from typing import Union + def to_checksum_address(address: str) -> str: ... -def remove_0x_prefix(hex_string: str) -> str: ... \ No newline at end of file +def remove_0x_prefix(hex_string: str) -> str: ... + +def is_address(address: Union[str, bytes]) -> bool: ... diff --git a/python-packages/contract_wrappers/stubs/web3/contract.pyi b/python-packages/contract_wrappers/stubs/web3/contract.pyi new file mode 100644 index 0000000000..3078722ba2 --- /dev/null +++ b/python-packages/contract_wrappers/stubs/web3/contract.pyi @@ -0,0 +1,5 @@ +class ContractFunction: + def __call__(self, *args, **kwargs): + ... + + ... diff --git a/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi b/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi index 70baff3728..f4d350d897 100644 --- a/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi +++ b/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi @@ -1,3 +1,10 @@ +from typing import Any + + class Contract: def call(self): ... + + functions: Any + + events: Any ... diff --git a/python-packages/contract_wrappers/test/test_base_contract_method.py b/python-packages/contract_wrappers/test/test_base_contract_method.py new file mode 100644 index 0000000000..6b558b9fad --- /dev/null +++ b/python-packages/contract_wrappers/test/test_base_contract_method.py @@ -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, + ) diff --git a/python-packages/contract_wrappers/test/test_base_contract_wrapper.py b/python-packages/contract_wrappers/test/test_base_contract_wrapper.py deleted file mode 100644 index a26e200cd3..0000000000 --- a/python-packages/contract_wrappers/test/test_base_contract_wrapper.py +++ /dev/null @@ -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), - ], - ) diff --git a/python-packages/contract_wrappers/test/test_erc20_wrapper.py b/python-packages/contract_wrappers/test/test_erc20_wrapper.py index 565c15aa4e..7f3d42fb2b 100644 --- a/python-packages/contract_wrappers/test/test_erc20_wrapper.py +++ b/python-packages/contract_wrappers/test/test_erc20_wrapper.py @@ -25,8 +25,8 @@ def test_erc20_wrapper__balance_of( weth_instance, # pylint: disable=redefined-outer-name ): """Test getting baance of an account for an ERC20 token.""" - acc1_original_weth_balance = erc20_wrapper.balance_of(accounts[0]) - acc2_original_weth_balance = erc20_wrapper.balance_of(accounts[1]) + acc1_original_weth_balance = erc20_wrapper.balance_of.call(accounts[0]) + acc2_original_weth_balance = erc20_wrapper.balance_of.call(accounts[1]) expected_difference = 1 * 10 ** 18 @@ -36,8 +36,8 @@ def test_erc20_wrapper__balance_of( weth_instance.functions.deposit().transact( {"from": accounts[1], "value": expected_difference} ) - acc1_weth_balance = erc20_wrapper.balance_of(accounts[0]) - acc2_weth_balance = erc20_wrapper.balance_of(accounts[1]) + acc1_weth_balance = erc20_wrapper.balance_of.call(accounts[0]) + acc2_weth_balance = erc20_wrapper.balance_of.call(accounts[1]) assert ( 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 ): """Test approving one account to spend balance from another account.""" - erc20_wrapper.approve( + erc20_wrapper.approve.send_transaction( erc20_proxy_address, MAX_ALLOWANCE, tx_params=TxParams(from_=accounts[0]), ) - erc20_wrapper.approve( + erc20_wrapper.approve.send_transaction( erc20_proxy_address, MAX_ALLOWANCE, 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 ) - acc_2_weth_allowance = erc20_wrapper.allowance( + acc_2_weth_allowance = erc20_wrapper.allowance.call( accounts[1], erc20_proxy_address ) diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index 64896234c4..4f2b70a079 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -77,7 +77,7 @@ def test_exchange_wrapper__fill_order( ) 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, taker_asset_fill_amount=order["takerAssetAmount"], signature=order_signature, @@ -114,7 +114,7 @@ def test_exchange_wrapper__batch_fill_orders( for order_hash in order_hashes ] 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, taker_asset_fill_amounts=taker_amounts, signatures=order_signatures, diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index 03a57cdbed..f18cc596ed 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -40,7 +40,8 @@ class TestCommandExtension(TestCommand): """Invoke 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): diff --git a/python-packages/middlewares/setup.py b/python-packages/middlewares/setup.py index 79dc36f64e..572f29c02f 100755 --- a/python-packages/middlewares/setup.py +++ b/python-packages/middlewares/setup.py @@ -20,7 +20,8 @@ class TestCommandExtension(TestCommand): """Invoke 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): diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 38ab5f00b6..8496a88efd 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -21,7 +21,8 @@ class TestCommandExtension(TestCommand): """Invoke 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): diff --git a/python-packages/parallel b/python-packages/parallel index 12f385baaf..c127d3cf5f 100755 --- a/python-packages/parallel +++ b/python-packages/parallel @@ -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 subprocess import check_call +from subprocess import CalledProcessError, check_output from sys import argv PACKAGES = [ @@ -38,7 +38,18 @@ PACKAGES = [ def run_cmd_on_package(package: str): """cd to the package dir, ./setup.py lint, cd ..""" chdir(package) - check_call(f"{' '.join(argv[1:])}".split()) - chdir("..") + 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("..") -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 diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index b61c6e7d92..87636337ba 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -33,7 +33,8 @@ class TestCommandExtension(TestCommand): """Invoke 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): diff --git a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py index de7e254ade..9b8b8c7a13 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -319,7 +319,7 @@ book. Now let's have the taker fill it: ... provider=eth_node, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange ... ) ->>> exchange.fill_order( +>>> 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'), @@ -333,7 +333,7 @@ Cancelling Note that the above fill was partial: it only filled half of the order. Now we'll have our maker cancel the remaining order: ->>> exchange.cancel_order( +>>> exchange.cancel_order.send_transaction( ... order=order, ... tx_params=TxParams(from_=maker_address) ... ) diff --git a/yarn.lock b/yarn.lock index 27f0e5150e..92c9d733f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -655,21 +655,22 @@ dependencies: "@0x/base-contract" "^5.1.0" -"@0x/asset-buyer@6.1.4": - version "6.1.4" - resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.4.tgz#ea863b860fbae6f633846bdcf23cacbb345aefd1" +"@0x/asset-buyer@6.1.8": + version "6.1.8" + resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.8.tgz#71f6abb366e89e62457c256644edb37e12113e94" + integrity sha512-JOMu38EsQexDVyzOGYJamlLDigVoGOBNHAVYIkPn4r3SLY75Shcxlb9AhUfKEMDPv3kjk70r0Iy1DmIh4HBjdA== dependencies: - "@0x/assert" "^2.0.10" - "@0x/connect" "^5.0.9" - "@0x/contract-wrappers" "^9.1.3" - "@0x/json-schemas" "^3.0.10" - "@0x/order-utils" "^8.1.0" - "@0x/subproviders" "^4.0.6" - "@0x/types" "^2.2.2" - "@0x/typescript-typings" "^4.2.2" - "@0x/utils" "^4.3.3" - "@0x/web3-wrapper" "^6.0.6" - ethereum-types "^2.1.2" + "@0x/assert" "^2.1.0" + "@0x/connect" "^5.0.13" + "@0x/contract-wrappers" "^9.1.7" + "@0x/json-schemas" "^3.0.11" + "@0x/order-utils" "^8.2.2" + "@0x/subproviders" "^4.1.1" + "@0x/types" "^2.4.0" + "@0x/typescript-typings" "^4.2.3" + "@0x/utils" "^4.4.0" + "@0x/web3-wrapper" "^6.0.7" + ethereum-types "^2.1.3" lodash "^4.17.11" "@0x/base-contract@^4.0.1", "@0x/base-contract@^4.0.3": @@ -716,7 +717,7 @@ lodash "^4.17.11" 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" resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356" integrity sha512-+qx0lwDAuTitP8sXA4bxRS+W81i5TaXWziD/Fcraj40XGeuHFnRXVY5FEl9V7TC0sEDyvnnF2VFc5Y+v2CnYVQ== @@ -829,7 +830,7 @@ ethers "~4.0.4" 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" resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-4.1.2.tgz#ab7bb0f482b11ccb4615fb5dd8ca85199cd0ae23" integrity sha512-PaK/+cC6+o3glVITnBdb/AN/ej7ulfr49KGftNRATB8Y/yI6Xa3adqgFvDh7jiKBoB/auTRFQ/TabQTcieKl6g== @@ -2011,8 +2012,9 @@ "@types/react" "*" "@types/react-dom@^16.0.6": - version "16.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.11.tgz#bd10ccb0d9260343f4b9a49d4f7a8330a5c1f081" + version "16.8.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.5.tgz#3e3f4d99199391a7fb40aa3a155c8dd99b899cbd" + integrity sha512-idCEjROZ2cqh29+trmTmZhsBAUNQuYrF92JHKzZ5+aiFM1mlSk3bb23CK7HhYuOY75Apgap5y2jTyHzaM2AJGA== dependencies: "@types/react" "*" @@ -2051,6 +2053,7 @@ "@types/react-redux@^4.4.37": version "4.4.47" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-4.4.47.tgz#12af1677116e08d413fe2620d0a85560c8a0536e" + integrity sha512-wyFTmLtEymHCjOmVVvsbNqJaGM9Q0x6sZTQfz4XkDj06P8Xe+ys9wKSQHx2Jt9J5Mi7HZnGcJaMFktn60sXluw== dependencies: "@types/react" "*" redux "^3.6.0" @@ -4300,7 +4303,7 @@ buffer-equal@^1.0.0: version "1.0.0" 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" 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" 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: version "2.0.0" 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" path-exists "^3.0.0" -lodash-es@^4.17.5, lodash-es@^4.2.1: +lodash-es@^4.17.5: version "4.17.8" 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: version "3.0.1" 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" 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: version "2.3.0" 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" 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" 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" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" dependencies: @@ -15414,8 +15447,9 @@ reduce-function-call@^1.0.1: balanced-match "^0.4.2" redux-devtools-extension@^2.13.2: - version "2.13.2" - resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d" + version "2.13.8" + 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: version "2.13.5" @@ -15431,6 +15465,7 @@ redux@*, redux@^4.0.0: redux@^3.6.0: version "3.7.2" resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== dependencies: lodash "^4.2.1" lodash-es "^4.2.1" @@ -15916,16 +15951,19 @@ rollbar-sourcemap-webpack-plugin@^2.4.0: verror "^1.6.1" rollbar@^2.4.7: - version "2.4.7" - resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.4.7.tgz#9b1de1a0fab6b6e63fcfcd322c26081a1d8242e8" + version "2.10.0" + resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.10.0.tgz#a18c333eb45ebd21be128afdf9bf3a0093be7218" + integrity sha512-Ifr+KB8cXsaE9Lae3o2X7jx+WAAHMxul44Bj+P+0uGxbTO4TcHFRloUWgPVtmHxuFjpDchHbhwMwMicTseEb1Q== dependencies: async "~1.2.1" + buffer-from ">=1.1" console-polyfill "0.3.0" debug "2.6.9" error-stack-parser "1.3.3" json-stringify-safe "~5.0.0" lru-cache "~2.2.1" request-ip "~2.0.1" + source-map ">=0.5.0" uuid "3.0.x" optionalDependencies: decache "^3.0.5" @@ -16733,6 +16771,11 @@ source-map-url@^0.4.0: version "0.4.0" 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: version "0.4.4" resolved "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"