def test_error_when_invalid_args_kwargs_combo_provided(): with pytest.raises(TypeError): merge_args_and_kwargs(GENERATED_FUNCTION_ABI, ( 1, 2, ), { 'a': 1, 'b': 2 })
def get_constructor_arguments(contract: Contract, args: Optional[list] = None, kwargs: Optional[dict] = None): """Get constructor arguments for Etherscan verify. https://etherscanio.freshdesk.com/support/solutions/articles/16000053599-contract-verification-constructor-arguments """ # return contract._encode_constructor_data(args=args, kwargs=kwargs) constructor_abi = get_constructor_abi(contract.abi) # constructor_abi can be none in case of libraries if constructor_abi is None: return to_hex(contract.bytecode) if args is not None: return contract.encodeABI(constructor_abi['name'], args)[2:] # No 0x else: constructor_abi = get_constructor_abi(contract.abi) kwargs = kwargs or {} arguments = merge_args_and_kwargs(constructor_abi, [], kwargs) # deploy_data = add_0x_prefix( # contract._encode_abi(constructor_abi, arguments) # ) # TODO: Looks like recent Web3.py ABI change deploy_data = encode_abi(contract.web3, constructor_abi, arguments) return deploy_data
def estimate_gas_for_function(contract=None, function_name=None, transaction=None, *args, **kwargs): """Estimates gas cost a function call would take. Don't call this directly, instead use :meth:`Contract.estimateGas` on your contract instance. """ if transaction is None: estimate_transaction = {} else: estimate_transaction = dict(**transaction) function_abi = contract.find_matching_fn_abi(function_name, args, kwargs) function_selector = function_abi_to_4byte_selector(function_abi) arguments = merge_args_and_kwargs(function_abi, args, kwargs) estimate_transaction['data'] = contract.encodeABI( function_name, arguments, data=function_selector, ) gas_estimate = contract.web3.eth.estimateGas(estimate_transaction) return gas_estimate
def _get_function_info(cls, fn_name, args=None, kwargs=None): if args is None: args = tuple() if kwargs is None: kwargs = {} fn_abi = cls._find_matching_fn_abi(fn_name, args, kwargs) fn_selector = function_abi_to_4byte_selector(fn_abi) fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs) return fn_abi, fn_selector, fn_arguments
def _get_function_info(cls, fn_name, args=None, kwargs=None): if args is None: args = tuple() if kwargs is None: kwargs = {} fn_abi = cls._find_matching_fn_abi(fn_name, args, kwargs) fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi)) fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs) return fn_abi, fn_selector, fn_arguments
def get_function_info(abi, fn_name, args=None, kwargs=None): if args is None: args = tuple() if kwargs is None: kwargs = {} fn_abi = find_matching_fn_abi(abi, fn_name, args, kwargs) fn_selector = encode_hex(function_abi_to_4byte_selector(fn_abi)) fn_arguments = merge_args_and_kwargs(fn_abi, args, kwargs) return fn_abi, fn_selector, fn_arguments
def _set_function_info(self): self.abi = find_matching_fn_abi(self.contract_abi, self.function_identifier, self.args, self.kwargs) if self.function_identifier is FallbackFn: self.selector = encode_hex(b'') elif is_text(self.function_identifier): self.selector = encode_hex(function_abi_to_4byte_selector(self.abi)) else: raise TypeError("Unsupported function identifier") self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs)
def get_constructor_arguments(contract: Contract, args: Optional[list]=None, kwargs: Optional[dict]=None): """Get constructor arguments for Etherscan verify. https://etherscanio.freshdesk.com/support/solutions/articles/16000053599-contract-verification-constructor-arguments """ constructor_abi = get_constructor_abi(contract.abi) if args is not None: return contract._encode_abi(constructor_abi, args)[2:] # No 0x else: constructor_abi = get_constructor_abi(contract.abi) arguments = merge_args_and_kwargs(constructor_abi, [], kwargs) deploy_data = add_0x_prefix( contract._encode_abi(constructor_abi, arguments) ) return deploy_data
def call_contract_function(contract, function_name, transaction, *args, **kwargs): """Calls a contract constant or function. The function must not have state changing effects. For those see :func:`transact_with_contract_function` For usual cases, you do not want to call this directly, but interact with your contract through :meth:`Contract.call` method. :param contract: :class:`web3.contract.Contract` object instance :param function_name: Contract function name to call :param transaction: Transaction parameters to pass to underlying ``web3.eth.call`` :param *arguments: Arguments to be passed to contract function. Automatically encoded :return: Function call results, encoded to Python object """ if transaction is None: call_transaction = {} else: call_transaction = dict(**transaction) function_abi = contract.find_matching_fn_abi(function_name, args, kwargs) function_selector = function_abi_to_4byte_selector(function_abi) arguments = merge_args_and_kwargs(function_abi, args, kwargs) call_transaction['data'] = contract.encodeABI( function_name, arguments, data=function_selector, ) return_data = contract.web3.eth.call(call_transaction) output_types = get_abi_output_types(function_abi) output_data = decode_abi(output_types, return_data) normalized_data = [ normalize_return_type(data_type, data_value) for data_type, data_value in zip(output_types, output_data) ] if len(normalized_data) == 1: return normalized_data[0] else: return normalized_data
def _encode_data_in_transaction(self, *args, **kwargs): constructor_abi = get_constructor_abi(self.abi) if constructor_abi: if not args: args = tuple() if not kwargs: kwargs = {} arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) data = add_0x_prefix( encode_abi(self.web3, constructor_abi, arguments, data=self.bytecode) ) else: data = to_hex(self.bytecode) return data
def _encode_constructor_data(cls, args=None, kwargs=None): constructor_abi = get_constructor_abi(cls.abi) if constructor_abi: if args is None: args = tuple() if kwargs is None: kwargs = {} arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) deploy_data = add_0x_prefix( cls._encode_abi(constructor_abi, arguments, data=cls.bytecode)) else: deploy_data = add_0x_prefix(cls.bytecode) return deploy_data
def _encode_constructor_data(cls, args=None, kwargs=None): constructor_abi = get_constructor_abi(cls.abi) if constructor_abi: if args is None: args = tuple() if kwargs is None: kwargs = {} arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) deploy_data = add_0x_prefix( cls._encode_abi(constructor_abi, arguments, data=cls.bytecode) ) else: deploy_data = add_0x_prefix(cls.bytecode) return deploy_data
def encode_constructor_data(abi, bytecode, web3, args=None, kwargs=None): constructor_abi = get_constructor_abi(abi) if constructor_abi: if args is None: args = tuple() if kwargs is None: kwargs = {} arguments = merge_args_and_kwargs(constructor_abi, args, kwargs) deploy_data = add_0x_prefix( encode_abi(web3, constructor_abi, arguments, data=bytecode)) else: deploy_data = to_hex(bytecode) return deploy_data
def test_error_when_invalid_args_kwargs_combo_provided(): with pytest.raises(TypeError): merge_args_and_kwargs(GENERATED_FUNCTION_ABI, (1, 2,), {'a': 1, 'b': 2})
def test_kwargs_is_disallowed_when_merging_with_unnamed_inputs(): with pytest.raises(TypeError): merge_args_and_kwargs(GENERATED_FUNCTION_ABI, tuple(), { 'x': 1, 'y': 2 })
def test_merging_of_args_and_kwargs_with_no_inputs(): actual = merge_args_and_kwargs(NO_INPUTS_FUNCTION_ABI, tuple(), {}) assert actual == tuple()
def test_merging_of_args_and_kwargs(args, kwargs, expected_args): actual_args = merge_args_and_kwargs(FUNCTION_ABI, args, kwargs) assert actual_args == expected_args
def _set_function_info(self): self.abi = find_matching_fn_abi(self.contract_abi, self.fn_name, self.args, self.kwargs) self.selector = encode_hex(function_abi_to_4byte_selector(self.abi)) self.arguments = merge_args_and_kwargs(self.abi, self.args, self.kwargs)
def test_kwargs_is_disallowed_when_merging_with_unnamed_inputs(): with pytest.raises(TypeError): merge_args_and_kwargs(GENERATED_FUNCTION_ABI, tuple(), {'x': 1, 'y': 2})
def transact_with_contract_function(contract=None, function_name=None, transaction=None, *args, **kwargs): """Transacts with a contract. Sends in a transaction that interacts with the contract. You should specify the account that pays the gas for this transaction in `transaction`. Example: .. code-block:: python def withdraw(self, to_address: str, amount_in_eth: Decimal, from_account=None, max_gas=50000) -> str: '''Withdraw funds from a wallet contract. :param amount_in_eth: How much as ETH :param to_address: Destination address we are withdrawing to :param from_account: Which Geth accout pays the gas :return: Transaction hash ''' assert isinstance(amount_in_eth, Decimal) # Don't let floats slip through wei = to_wei(amount_in_eth) if not from_account: # Default to coinbase for transaction fees from_account = self.contract.web3.eth.coinbase tx_info = { # The Ethereum account that pays the gas for this operation "from": from_account, "gas": max_gas, } # Interact with underlying wrapped contract txid = transact_with_contract_function( self.contract, "withdraw", tx_info, to_address, wei, ) return txid The transaction is created in the Ethereum node memory pool. Transaction receipt is not available until the transaction has been mined. See :func:`populus.utils.transactions.wait_for_transaction`. Usually there is no reason to call directly. Instead use :meth:`Contract.transact` interface. :param contract: :class:`web3.contract.Contract` object instance :param function_name: Contract function name to call :param transaction: Dictionary of transaction parameters to pass to underlying ``web3.eth.sendTransaction`` :param *arguments: Arguments to be passed to contract function. Automatically encoded :return: String, 0x formatted transaction hash. """ if transaction is None: transact_transaction = {} else: transact_transaction = dict(**transaction) function_abi = contract.find_matching_fn_abi(function_name, args, kwargs) function_selector = function_abi_to_4byte_selector(function_abi) arguments = merge_args_and_kwargs(function_abi, args, kwargs) transact_transaction['data'] = contract.encodeABI( function_name, arguments, data=function_selector, ) txn_hash = contract.web3.eth.sendTransaction(transact_transaction) return txn_hash
def test_args_allowed_when_duplicate_named_inputs(): actual = merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1, 2, 3), {}) assert actual == (1, 2, 3)
def test_kwargs_allowed_if_no_intersections_with_duplicate_input_names(): with pytest.raises(TypeError): merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1, ), { 'a': 2, 'b': 3 })
def test_args_works_when_merging_with_unnamed_inputs(): actual = merge_args_and_kwargs(GENERATED_FUNCTION_ABI, (1, 2), {}) assert actual == (1, 2)
def test_kwargs_not_allowed_for_duplicate_input_names(): with pytest.raises(TypeError): merge_args_and_kwargs(DUPLICATE_NAMES_FUNCTION_ABI, (1,), {'a': 2, 'b': 3})