def _call_contract( self, web3: Optional[Web3], contract_address: ChecksumEthAddress, abi: List, method_name: str, arguments: Optional[List[Any]] = None, ) -> Any: """Performs an eth_call to an ethereum contract May raise: - RemoteError if etherscan is used and there is a problem with reaching it or with the returned result - BlockchainQueryError if web3 is used and there is a VM execution error """ if web3 is None: return self._call_contract_etherscan( contract_address=contract_address, abi=abi, method_name=method_name, arguments=arguments, ) contract = web3.eth.contract(address=contract_address, abi=abi) try: method = getattr(contract.caller, method_name) result = method(*arguments if arguments else []) except (ValueError, BadFunctionCallOutput) as e: raise BlockchainQueryError( f'Error doing call on contract {contract_address}: {str(e)}', ) from e return result
def _call_contract_etherscan( self, contract_address: ChecksumEthAddress, abi: List, method_name: str, arguments: Optional[List[Any]] = None, ) -> Any: """Performs an eth_call to an ethereum contract via etherscan May raise: - RemoteError if there is a problem with reaching etherscan or with the returned result """ web3 = Web3() contract = web3.eth.contract(address=contract_address, abi=abi) input_data = contract.encodeABI(method_name, args=arguments if arguments else []) result = self.etherscan.eth_call( to_address=contract_address, input_data=input_data, ) if result == '0x': raise BlockchainQueryError( f'Error doing call on contract {contract_address} for {method_name} ' f'with arguments: {str(arguments)} via etherscan. Returned 0x result', ) fn_abi = contract._find_matching_fn_abi( fn_identifier=method_name, args=arguments, ) output_types = get_abi_output_types(fn_abi) output_data = web3.codec.decode_abi(output_types, bytes.fromhex(result[2:])) if len(output_data) == 1: # due to https://github.com/PyCQA/pylint/issues/4114 return output_data[0] # pylint: disable=unsubscriptable-object return output_data