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 test_contract_estimateGas(web3, math_contract): if isinstance(web3.currentProvider, TestRPCProvider): pytest.skip("The testrpc server doesn't implement `eth_estimateGas`") increment_abi = math_contract.find_matching_abi("increment", []) call_data = function_abi_to_4byte_selector(increment_abi) gas_estimate = math_contract.estimateGas().increment() assert abs(gas_estimate - 21272) < 200
def test_contract_estimateGas(web3, math_contract): increment_abi = math_contract._find_matching_fn_abi('increment', []) call_data = function_abi_to_4byte_selector(increment_abi) gas_estimate = math_contract.estimateGas().increment() try: assert abs(gas_estimate - 21472) < 200 # Geth except AssertionError: assert abs(gas_estimate - 43020) < 200 # TestRPC pass
def test_contract_estimateGas(web3, math_contract): increment_abi = math_contract._find_matching_fn_abi('increment', []) call_data = function_abi_to_4byte_selector(increment_abi) gas_estimate = math_contract.estimateGas().increment() try: assert abs(gas_estimate - 21272) < 200 # Geth except AssertionError: assert abs(gas_estimate - 42820) < 200 # TestRPC pass
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 test_eth_estimateGas(web3, math_contract): if isinstance(web3.currentProvider, TestRPCProvider): pytest.skip("The testrpc server doesn't implement `eth_estimateGas`") increment_abi = math_contract.find_matching_abi('increment', []) call_data = function_abi_to_4byte_selector(increment_abi) gas_estimate = web3.eth.estimateGas({ 'to': math_contract.address, 'from': web3.eth.coinbase, 'data': call_data, }) assert abs(gas_estimate - 21272) < 200
def test_eth_estimateGas(web3, math_contract): increment_abi = math_contract.find_matching_fn_abi('increment', []) call_data = function_abi_to_4byte_selector(increment_abi) gas_estimate = web3.eth.estimateGas({ 'to': math_contract.address, 'from': web3.eth.coinbase, 'data': call_data, }) try: assert abs(gas_estimate - 21272) < 200 # Geth except AssertionError: assert abs(gas_estimate - 42820) < 200 # TestRPC pass
def test_eth_estimateGas(web3, math_contract): increment_abi = math_contract._find_matching_fn_abi('increment', []) call_data = function_abi_to_4byte_selector(increment_abi) gas_estimate = web3.eth.estimateGas({ 'to': math_contract.address, 'from': web3.eth.coinbase, 'data': call_data, }) try: assert abs(gas_estimate - 21272) < 200 # Geth except AssertionError: assert abs(gas_estimate - 42820) < 200 # TestRPC pass
def execute( self, to_contract: Contract, func: str, args=None, amount_in_eth: Optional[Decimal] = None, max_gas=100000 ): """Calls a smart contract from the hosted wallet. Creates a transaction that is proxyed through hosted wallet execute method. We need to have ABI as Populus Contract instance. :param wallet_address: Wallet address :param contract: Contract to called as address bound Populus Contract class :param func: Method name to be called :param args: Arguments passed to the method :param value: Additional value carried in the call in ETH :param gas: The max amount of gas the coinbase account is allowed to pay for this transaction. :return: txid of the execution as hex string """ assert isinstance(to_contract, Contract) if amount_in_eth: assert isinstance(amount_in_eth, Decimal) # Don't let floats slip through value = to_wei(amount_in_eth) else: value = 0 # Encode function arguments function_abi = to_contract._find_matching_fn_abi(func, args) # 4 byte function hash function_selector = function_abi_to_4byte_selector(function_abi) # data payload passed to the function arg_data = to_contract.encodeABI(func, args=args) call_data = function_selector + arg_data[2:] # test_event_execute() call data should look like # function selector + random int as 256-bit # 0x5093dc7d000000000000000000000000000000000000000000000000000000002a3f58fe # web3 takes bytes argument as actual bytes, not hex call_data = binascii.unhexlify(call_data[2:]) tx_info = { # The Ethereum account that pays the gas for this operation "from": self.contract.web3.eth.coinbase, "gas": max_gas, } txid = self.contract.transact(tx_info).execute(to_contract.address, value, max_gas, call_data) return txid
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 estimate_gas_for_function(contract=None, function_name=None, transaction=None, *arguments): if not arguments: arguments = [] function_abi = contract.find_matching_abi(function_name, arguments) function_selector = function_abi_to_4byte_selector(function_abi) transaction['data'] = contract.encodeABI( function_name, arguments, data=function_selector, ) gas_estimate = contract.web3.eth.estimateGas(transaction) return gas_estimate
def transact_with_contract_function(contract=None, function_name=None, transaction=None, *arguments): if not arguments: arguments = [] function_abi = contract.find_matching_abi(function_name, arguments) function_selector = function_abi_to_4byte_selector(function_abi) transaction['data'] = contract.encodeABI( function_name, arguments, data=function_selector, ) txn_hash = contract.web3.eth.sendTransaction(transaction) return txn_hash
def call_contract_function(contract=None, function_name=None, transaction=None, *arguments): if not arguments: arguments = [] function_abi = contract.find_matching_abi(function_name, arguments) function_selector = function_abi_to_4byte_selector(function_abi) transaction['data'] = contract.encodeABI( function_name, arguments, data=function_selector, ) return_data = contract.web3.eth.call(transaction) output_types = get_abi_output_types(function_abi) output_data = decode_abi(output_types, return_data) if len(output_data) == 1: return output_data[0] else: return output_data
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