def run_trigger(new_logs): """ Trigger on governorship transfers """ events = [] for ev in get_events(new_logs): former_governor = decode_single('(address)', decode_hex(ev.topic_1))[0] new_governor = decode_single('(address)', decode_hex(ev.topic_2))[0] if ev.topic_0 == SIG_EVENT_PENDING_TRANSFER: events.append( event_high("New proxy governor pending 👮🔄", "**Contract**: {}\n" "**Current governor**: {}\n" "**Proposed governor**: {}".format( ev.address, former_governor, new_governor), log_model=ev)) if ev.topic_0 == SIG_EVENT_TRANSFER: events.append( event_high("New proxy governor claimed 👮", "**Contract**: {}\n" "**Former governor**: {}\n" "**New governor**: {}".format( ev.address, former_governor, new_governor), log_model=ev)) return events
def run_trigger(new_logs): """ Template trigger """ events = [] for ev in get_rates_events(new_logs): duration_event = get_durations_event(new_logs, ev.transaction_hash) (rates, ) = decode_single("(uint256[])", decode_hex(ev.data)) (durations, ) = decode_single("(uint256[])", decode_hex(duration_event.data)) durations_string = "" for i, duration in enumerate(durations): # Percentage of return as an 18-decimal int apy_multiple = Decimal(DAYS_365_SECONDS) / Decimal(duration) rate = (Decimal(rates[i]) / Decimal(1e18)) apy = round(rate * apy_multiple * Decimal(100), 1) duration_dt = timedelta(seconds=duration) durations_string += "\n - {} days @ {}%".format( duration_dt.days, apy) events.append( event_high("OGN Staking Rates Changed 🧮", durations_string, tags=EVENT_TAGS, log_model=ev)) return events
def verify_stake(amount, transactions): transaction_count = len(transactions) # We should add in address validation later (address, approved) = decode_single("(address,uint256)", bytes.fromhex(transactions[0]["data"][10:])) stake = decode_single("uint256", w3.toBytes(hexstr=transactions[1]["data"][10:])) return transaction_count == 2 and check_address(address) and int( stake) == int(amount) and int(approved) == int(amount)
def run_trigger(new_logs): """ Create events for governor proposals """ events = [] for ev in get_proposal_events(new_logs): if ev.topic_0 == SIG_EVENT_PROPOSAL_CREATED: (proposal_id, proposer, targets, signatures, calldatas, description) = decode_single( '(uint256,address,address[],string[],bytes[],string)', decode_hex(ev.data)) title = "New Proposal 🗳️" details = ("A new proposal ({}) was created: {}\n" "\n" "Proposer: {}\n" "Targets: {}\n" "Calls: \n - {}\n").format( proposal_id, description, proposer, ', '.join([ CONTRACT_ADDR_TO_NAME.get(target, target) for target in set(targets) ]), '\n - '.join(decode_calls(signatures, calldatas)), ) elif ev.topic_0 == SIG_EVENT_PROPOSAL_QUEUED: (proposal_id, eta_seconds) = decode_single('(uint256,uint256)', decode_hex(ev.data)) eta = datetime.utcfromtimestamp(eta_seconds) title = "Proposal Queued 🗳️ ✔️" details = "Prop {} was accepted and queued for {}.".format( proposal_id, eta.strftime(HUMAN_DATETIME_FORMAT)) elif ev.topic_0 == SIG_EVENT_PROPOSAL_EXECUTED: (proposal_id, ) = decode_single('(uint256)', decode_hex(ev.data)) title = "Proposal Executed 🗳️ ⚙️" details = "Prop {} was executed.".format(proposal_id) elif ev.topic_0 == SIG_EVENT_PROPOSAL_CANCELLED: (proposal_id, ) = decode_single('(uint256)', decode_hex(ev.data)) title = "Proposal Cancelled 🗳️ ❌" details = "Prop {} was cancelled.".format(proposal_id) else: raise Exception("Impossible!") events.append(event_high(title, details, log_model=ev)) return events
def convert_to_dai(name, balance): token = TOKENS[name] tx = { 'to': str(token), 'data': EMN.calculateContinuousBurnReturn.encode_input(balance) } dai = decode_single('uint', web3.eth.call(tx, SNAPSHOT_BLOCK)) if name in {'eCRV', 'eLINK', 'eAAVE', 'eYFI', 'eSNX'}: tx = { 'to': str(EMN), 'data': EMN.calculateContinuousBurnReturn.encode_input(dai) } dai = decode_single('uint', web3.eth.call(tx, SNAPSHOT_BLOCK)) return dai
def parse_immutables(msig): deployed = bytes.hex(web3.eth.getCode(msig.address)) partition = deployed.partition(MINISIG_HUFF_RUNTIME) if partition[0] != '': raise ValueError("can't match bytecode") immutables = partition[2] threshold = decode_single('uint256', bytes.fromhex(immutables[:64])) dom_sep = HexString( decode_single('bytes32', bytes.fromhex(immutables[64:128])), 'bytes32') signers = decode_single('address[]', bytes.fromhex(immutables[128:])) signers = [checksum(addr) for addr in signers] return MinisigImmutables(threshold, dom_sep, signers)
def run_trigger(new_logs): """ Template trigger """ events = [] for ev in get_events(new_logs): threshold = decode_single('(uint256)', decode_hex(ev.data))[0] if ev.topic_0 == SIG_EVENT_ALLOCATE_THRESHOLD: events.append(event_normal( "Vault Allocate Threshold Changed ЁЯез", "OUSD Vault allocation deposit threshold was changed to {} " "units".format(threshold), log_model=ev )) elif ev.topic_0 == SIG_EVENT_REBASE_THRESHOLD: events.append(event_normal( "Vault Rebase Threshold Changed ЁЯН▒", "OUSD Vault rebase threshold was changed to {} " "units".format(threshold), log_model=ev )) else: raise Exception("You never saw this") return events
async def _resolve_contract_address(self, method_name) -> str: """Resolve related contract address from main contract property. :param method_name: name of the abi method/property :raise RuntimeError: if address can't be resolved :return: """ res = await self.eth.call({ "to": self.token.contract.address, "data": '0x%s' % self.token.contract.call(method_name).hex(), }) if res == '0x': self.log.error("Can't resolve %s %s", method_name, self.token.contract.address) raise Exception( "Can't resolve contract address from method %s " "contract return 0x (looks like a wrong token contract " "address %s)" % (method_name, self.token.contract.address)) address = to_checksum_address( decode_single('address', bytes.fromhex(res[2:]))) self.log.debug("Contract address %s resolved from %s method", address, method_name) return address
def call(self, transaction, block_number="latest"): # TODO: move this to the VM level. defaulted_transaction = transaction.copy() if 'gas' not in defaulted_transaction: defaulted_transaction['gas'] = self._max_available_gas() signed_evm_transaction = self._get_normalized_and_signed_evm_transaction( defaulted_transaction, block_number, ) computation = _execute_and_revert_transaction( self.chain, signed_evm_transaction, block_number, ) if computation.is_error: msg = str(computation._error) # Check to see if it's a EIP838 standard error, with ABI signature # of Error(string). If so - extract the message/reason. if self.is_eip838_error(computation._error): error_str = computation._error.args[0][36:] try: msg = decode_single('string', error_str) except DecodingError: # Invalid encoded bytes, leave msg as computation._error # byte string. pass raise TransactionFailed(msg) return computation.output
def verify_settle(guid, transactions): settle_guid = decode_single( "uint256", w3.toBytes(hexstr=transactions[0]["data"][10:])) transaction_count = len(transactions) return transaction_count == 1 and check_int_uuid( settle_guid) and check_uuid(guid) and str( UUID(int=settle_guid, version=4)) == guid
def call(self, to_address, contract_abi, fn_name, args=None): cmd = "call" if self.client_account == None: self.load_default_account() functiondata = encode_transaction_data(fn_name, contract_abi, None, args) callmap = dict() callmap["data"] = functiondata callmap["from"] = self.client_account.address callmap["to"] = to_address callmap["value"] = 0 params = [1, callmap] # 发送 response = self.common_request(cmd, params) outputdata = response["output"] #取得方法的abi,签名,参数 和返回类型,进行call返回值的解析 fn_abi, fn_selector, fn_arguments = get_function_info( fn_name, contract_abi, None, args, None, ) #print("fn_selector",fn_selector) #print("fn_arguments",fn_arguments) fn_output_types = get_fn_abi_types_single(fn_abi, "outputs") #print("output types str:", fn_output_types) decoderesult = decode_single(fn_output_types, decode_hex(outputdata)) return decoderesult
def run_trigger(new_logs): """ Template trigger """ events = [] for ev in get_events(new_logs): recipient, amount = decode_single('(address,uint256)', decode_hex(ev.data)) if amount == 0: continue contract_name = CONTRACT_ADDR_TO_NAME.get(ev.address, ev.address) """ Event doesn't include token address, so if it's not from STRATCOMP, I have no idea what it might be. """ reward_token = '[UKNOWN]' if ev.address == STRATCOMP: reward_token = 'COMP' elif ev.address == STRAT3POOL: reward_token = 'CRV' events.append( event_normal("Reward tokens have been collected 💸", "{} {} has been collected by {}\n".format( format_token_human('COMP', amount), reward_token, contract_name, ), log_model=ev)) return events
def _from_bits(val_bits, o_typ): # o_typ: the type to convert to detail = _parse_type(o_typ) try: return decode_single(detail.abi_type, val_bits) except eth_abi.exceptions.NonEmptyPaddingBytes: raise _OutOfBounds() from None
def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: namespace, _, endpoint = method.partition('_') from eth_tester.exceptions import TransactionFailed try: delegator = self.api_endpoints[namespace][endpoint] except KeyError: return RPCResponse({ "error": "Unknown RPC Endpoint: {0}".format(method), }) try: response = delegator(self.ethereum_tester, params) except NotImplementedError: return RPCResponse({ "error": "RPC Endpoint has not been implemented: {0}".format(method), }) except TransactionFailed as e: if type(e.args[0]) == str: reason = e.args[0] else: reason = decode_single('(string)', e.args[0].args[0][4:])[0] raise SolidityError(f'execution reverted: {reason}') else: return { 'result': response, }
def run_trigger(new_logs): """ Compound Timelock changes """ events = [] for ev in get_events(new_logs): admin_address = decode_single("(address)", decode_hex(ev.topic_1))[0] if ev.topic_0 == SIG_EVENT_NEW_ADMIN: events.append( event_high( "Compound Timelock admin claimed 👮", "A new admin has been set for the Compound Timelock " "contract: {}".format(admin_address), log_model=ev)) elif ev.topic_0 == SIG_EVENT_NEW_PENDING_ADMIN: events.append( event_high( "New Compound Timelock admin proposed 👮", "{} has been proposed as the new admin for the Compound " "Timelock governor contract and is currently waiting to be " "claimed.".format(admin_address), log_model=ev)) return events
def format_raw_data(logs, event_name): abi = json.loads(json.loads(open("./0x_abi.json").read())['result']) types = [] names = [] indexed_types = [] indexed_names = [] for elem in abi: if 'name' in elem and elem['name'] == event_name: for input in elem['inputs']: if input['indexed']: indexed_types.append(input["type"]) indexed_names.append(input["name"]) else: types.append(input["type"]) names.append(input["name"]) break return_list = [] for log in logs: encoded_topics = list(map(lambda x: decode_hex(x), log['topics'][1:])) indexed_values = [ eth_abi.decode_single(t, v) for t, v in zip(indexed_types, encoded_topics) ] values = eth_abi.decode_abi(types, decode_hex(log['data'])) next_dict = dict( zip(indexed_names + names, indexed_values + list(values))) next_dict["blockNumber"] = log['blockNumber'] return_list.append(next_dict) return return_list
def get_did(self, did_bytes): """return a did value and value type from the block chain event record using 'did'""" result = None block_number = self._didregistry.get_update_at(did_bytes) logger.debug('block_number %d', block_number) if block_number == 0: raise OceanDIDNotFound('cannot find DID {}'.format( Web3.toHex(did_bytes))) block_filter = self._web3.eth.filter({ 'fromBlock': block_number, 'toBlock': block_number, 'topics': [self._event_signature, Web3.toHex(did_bytes)] }) log_items = block_filter.get_all_entries() if log_items: log_item = log_items[-1] value, value_type, block_number = decode_single('(string,uint8,uint256)', \ Web3.toBytes(hexstr=log_item['data'])) topics = log_item['topics'] logger.debug('topics {}'.format(topics)) result = { 'value_type': value_type, 'value': value, 'block_number': block_number, 'did_bytes': Web3.toBytes(topics[1]), 'owner': Web3.toChecksumAddress(topics[2][-20:]), 'key': Web3.toBytes(topics[3]), } return result
def wait_for_event(self, ev, ev_argt, ev_argt_indexed=None, ev_argv_indexed=None, ev_rett=None, ev_callback=None): evsig = ev + self._build_types(ev_argt) sig = "0x" + binascii.hexlify(Web3.sha3(evsig.encode("utf-8"))).decode("utf-8") topics=[sig] if ev_argt_indexed is not None and ev_argv_indexed is not None and len(ev_argv_indexed) == len(ev_argt_indexed): for i in range(0, len(ev_argt_indexed)): topics.append("0x" + binascii.hexlify(eth_abi.encode_single(ev_argt_indexed[i], ev_argv_indexed[i])).decode("utf-8")) event_filter = self.web3.eth.filter({ "address": self.address, "topics": topics }) max_retries = 60 # 2 minutes timeout retries = 0 while retries <= max_retries: for event in event_filter.get_new_entries(): if ev_rett is not None: ret = eth_abi.decode_single(self._build_types(ev_rett), binascii.unhexlify(event["data"][2:])) if ev_callback and ev_callback(ret): return ret else: return True retries += 1 time.sleep(2) raise RuntimeError(f"event timeout: {ev}")
def call_function(self, fun, fun_argt, fun_argv, fun_rett): (data, funsig) = self._encode_function_call(fun, fun_argt, fun_argv) tx = { "from": self.wallet.checksum_address, "to": self.address, "data": data } gasprice = self.web3.eth.gasPrice gas = self.web3.eth.estimateGas(tx) tx["gasPrice"] = gasprice tx["gas"] = gas result = self.web3.eth.call(tx) decoded = eth_abi.decode_single(self._build_types(fun_rett), result) self.logger.debug(f"CALL {funsig}: {decoded}") # Return single value instead of tuple. if len(fun_rett) == 1: return decoded[0] return decoded
def call(self, to_address, contract_abi, fn_name, args=None): cmd = "call" if to_address != "": common.check_and_format_address(to_address) if self.client_account is None: self.load_default_account() functiondata = encode_transaction_data(fn_name, contract_abi, None, args) callmap = dict() callmap["data"] = functiondata callmap["from"] = self.client_account.address callmap["to"] = to_address callmap["value"] = 0 # send transaction to the given group params = [client_config.groupid, callmap] # 发送 response = self.common_request(cmd, params) if "output" in response.keys(): outputdata = response["output"] # 取得方法的abi,签名,参数 和返回类型,进行call返回值的解析 fn_abi, fn_selector, fn_arguments = get_function_info( fn_name, contract_abi, None, args, None, ) # print("fn_selector",fn_selector) # print("fn_arguments",fn_arguments) fn_output_types = get_fn_abi_types_single(fn_abi, "outputs") # print("output types str:", fn_output_types) decoderesult = decode_single(fn_output_types, decode_hex(outputdata)) return decoderesult return response
def test_single_abi_tuple_reversibility(type_and_value): """ Tests round trip encoding and decoding for tuple types. """ _type, value = type_and_value encoded_value = encode_single(_type, value) decoded_value = decode_single(_type, encoded_value) assert value == decoded_value
def call(self, call, type=bytes): res = self.w3.eth.call({ 'to': self.address, 'data': self.build(call), 'gasLimit': 10000000000, }) success, return_data = eth_abi.decode_single("(bool,bytes)", res) return self._handle_result(success, return_data, call, type)
def decode_log(logVal, abis): event_interface = abis[logVal["contract"]].funcs[logVal["id"]] ret = {} topics = [inp for inp in event_interface["inputs"] if inp["indexed"]] for (topic, topic_data) in zip(topics, logVal["topics"]): ret[topic["name"]] = eth_abi.decode_single(topic["type"], topic_data) other_inputs = [ inp for inp in event_interface["inputs"] if not inp["indexed"] ] arg_type = '(' + ','.join([inp["type"] for inp in other_inputs]) + ')' decoded = eth_abi.decode_single(arg_type, logVal["data"]) for (inp, val) in zip(other_inputs, decoded): ret[inp["name"]] = val return {"name": event_interface['name'], "args": ret}
def decode(self, events): abi = events[self.contract_id][self.event_id] ret = {} topics = [inp for inp in abi["inputs"] if inp["indexed"]] for (topic, topic_data) in zip(topics, self.topics): ret[topic["name"]] = eth_abi.decode_single(topic["type"], topic_data) other_inputs = [inp for inp in abi["inputs"] if not inp["indexed"]] arg_type = "(" + ",".join([inp["type"] for inp in other_inputs]) + ")" decoded = eth_abi.decode_single(arg_type, self.data) for (inp, val) in zip(other_inputs, decoded): ret[inp["name"]] = val self.decoded = True self.name = abi["name"] self.args = ret
def parse_tx(self, tx_data): if type(tx_data) == str: tx_data = bytes.fromhex(tx_data[10:]) elif type(tx_data) == bytes: tx_data = tx_data[4:] result = decode_single(self.args, tx_data) result = dict(zip(self.names, result)) return [result.get(want) for want in self.want_fields]
def run_trigger(new_logs): """ Template trigger """ events = [] for ev in get_events(new_logs): asset = decode_single('(address)', decode_hex(ev.topic_1))[0] ptoken = decode_single('(address)', decode_hex(ev.data))[0] asset_name = SYMBOL_FOR_CONTRACT.get(asset, asset) contract_name = CONTRACT_ADDR_TO_NAME.get(ev.address, ev.address) if ev.topic_0 == SIG_EVENT_PTOKEN_ADDED: events.append( event_normal( "Platform token has been set 🪆", "**Strategy**: {}\n" "**Asset**: {}\n" "**Platform Token**: {}\n".format( contract_name, asset_name, ptoken, ), log_model=ev ) ) elif ev.topic_0 == SIG_EVENT_PTOKEN_REMOVED: events.append( event_high( "Platform token has been unset 🕳️", "**Strategy**: {}\n" "**Asset**: {}\n" "**Platform Token**: {}\n".format( contract_name, asset_name, ptoken, ), log_model=ev ) ) else: # Theoretically impossible raise Exception('Unexpected event') return events
def parse_receipt_output(self, name, outputdata): if name not in self.func_abi_map_by_name: return None func_abi = self.func_abi_map_by_name[name] output_args = get_fn_abi_types_single(func_abi, "outputs") # print(output_args) result = decode_single(output_args, decode_hex(outputdata)) return result
def get_erc20_transfer_event_logs( node: str, from_block: int, to_block: int, token: Optional[str] = None, proxy=None, timeout=30, ) -> Result[list[ERC20TransferEventLog]]: params = [ {"topics": [TRANSFER_TOPIC], "fromBlock": hex(from_block), "toBlock": hex(to_block)}, ] if token: params[0]["address"] = token res = eth_rpc.rpc_call(node=node, method="eth_getLogs", params=params, proxy=proxy, timeout=timeout) if res.is_error(): return res try: result = [] for log in res.ok: if len(log["topics"]) != 3: continue tx_hash = log["transactionHash"].lower() block_number = int(log["blockNumber"], 16) token_address = log["address"].lower() from_address = decode_single("address", to_bytes(hexstr=HexStr(log["topics"][1]))) to_address = decode_single("address", to_bytes(hexstr=HexStr(log["topics"][2]))) value = str(decode_single("uint", to_bytes(hexstr=HexStr(log["data"])))) log_index = int(log["logIndex"], 16) result.append( ERC20TransferEventLog( tx_hash=tx_hash, block_number=block_number, token_address=token_address, log_index=log_index, from_address=from_address, to_address=to_address, value=value, ), ) return res.new_ok(result) except Exception as e: return res.new_error(f"exception: {str(e)}")
def decoder(interface, log): non_indexed = zip( interface['names'], eth_abi.decode_abi(interface['types'], decode_hex(log['data']))) indexed = zip(interface['indexed_names'], [ eth_abi.decode_single(t, decode_hex(v)) for t, v in zip(interface['indexed_types'], log['topics'][1:]) ]) return dict(chain(non_indexed, indexed))
def get_buy_price(curve_address, amount): return decode_single( "uint256", to_bytes(hexstr=eth_call( curve_address, "getBuyPrice(uint256)", data=encode_single("uint256", amount), )), )
def test_always_positive_single(_type, value): """ Tests round trip encoding and decoding for basic types and lists of basic types. """ assert value >= 0 encoded_value = encode_single(_type, value) decoded_value = decode_single(_type, encoded_value) assert decoded_value >= 0
def test_eth_call_with_0_result(self, web3, math_contract): coinbase = web3.eth.coinbase txn_params = math_contract._prepare_transaction( fn_name='add', fn_args=(0, 0), transaction={'from': coinbase, 'to': math_contract.address}, ) call_result = web3.eth.call(txn_params) assert is_string(call_result) result = decode_single('uint256', call_result) assert result == 0
def get_log_data(self, log_entry, indexed=False): values = self.cast_return_data(log_entry['data'], raw=True) event_data = { output['name']: value for output, value in zip(self.outputs, values) } if indexed: for idx, _input in enumerate(self.inputs): if _input['indexed']: event_data[_input['name']] = decode_single( _input['type'], log_entry['topics'][idx + 1], ) return event_data
def get_event_data(event_abi, log_entry): """ Given an event ABI and a log entry for that event, return the decoded """ if event_abi['anonymous']: log_topics = log_entry['topics'] else: log_topics = log_entry['topics'][1:] log_topics_abi = get_indexed_event_inputs(event_abi) log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) log_topic_names = get_abi_input_names({'inputs': log_topics_abi}) if len(log_topics) != len(log_topic_types): raise ValueError("Expected {0} log topics. Got {1}".format( len(log_topic_types), len(log_topics), )) log_data = log_entry['data'] log_data_abi = exclude_indexed_event_inputs(event_abi) log_data_normalized_inputs = normalize_event_input_types(log_data_abi) log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) log_data_names = get_abi_input_names({'inputs': log_data_abi}) # sanity check that there are not name intersections between the topic # names and the data argument names. duplicate_names = set(log_topic_names).intersection(log_data_names) if duplicate_names: raise ValueError( "Invalid Event ABI: The following argument names are duplicated " "between event inputs: '{0}'".format(', '.join(duplicate_names)) ) decoded_log_data = decode_abi(log_data_types, log_data) normalized_log_data = [ normalize_return_type(data_type, data_value) for data_type, data_value in zip(log_data_types, decoded_log_data) ] decoded_topic_data = [ decode_single(topic_type, topic_data) for topic_type, topic_data in zip(log_topic_types, log_topics) ] normalized_topic_data = [ normalize_return_type(data_type, data_value) for data_type, data_value in zip(log_topic_types, decoded_topic_data) ] event_args = dict(itertools.chain( zip(log_topic_names, normalized_topic_data), zip(log_data_names, normalized_log_data), )) event_data = { 'args': event_args, 'event': event_abi['name'], 'logIndex': log_entry['logIndex'], 'transactionIndex': log_entry['transactionIndex'], 'transactionHash': log_entry['transactionHash'], 'address': log_entry['address'], 'blockHash': log_entry['blockHash'], 'blockNumber': log_entry['blockNumber'], } return event_data
def get_event_data(event_abi, log_entry): """ Given an event ABI and a log entry for that event, return the decoded event data """ if event_abi['anonymous']: log_topics = log_entry['topics'] elif not log_entry['topics']: raise MismatchedABI("Expected non-anonymous event to have 1 or more topics") elif event_abi_to_log_topic(event_abi) != log_entry['topics'][0]: raise MismatchedABI("The event signature did not match the provided ABI") else: log_topics = log_entry['topics'][1:] log_topics_abi = get_indexed_event_inputs(event_abi) log_topic_normalized_inputs = normalize_event_input_types(log_topics_abi) log_topic_types = get_event_abi_types_for_decoding(log_topic_normalized_inputs) log_topic_names = get_abi_input_names({'inputs': log_topics_abi}) if len(log_topics) != len(log_topic_types): raise ValueError("Expected {0} log topics. Got {1}".format( len(log_topic_types), len(log_topics), )) log_data = hexstr_if_str(to_bytes, log_entry['data']) log_data_abi = exclude_indexed_event_inputs(event_abi) log_data_normalized_inputs = normalize_event_input_types(log_data_abi) log_data_types = get_event_abi_types_for_decoding(log_data_normalized_inputs) log_data_names = get_abi_input_names({'inputs': log_data_abi}) # sanity check that there are not name intersections between the topic # names and the data argument names. duplicate_names = set(log_topic_names).intersection(log_data_names) if duplicate_names: raise ValueError( "Invalid Event ABI: The following argument names are duplicated " "between event inputs: '{0}'".format(', '.join(duplicate_names)) ) decoded_log_data = decode_abi(log_data_types, log_data) normalized_log_data = map_abi_data( BASE_RETURN_NORMALIZERS, log_data_types, decoded_log_data ) decoded_topic_data = [ decode_single(topic_type, topic_data) for topic_type, topic_data in zip(log_topic_types, log_topics) ] normalized_topic_data = map_abi_data( BASE_RETURN_NORMALIZERS, log_topic_types, decoded_topic_data ) event_args = dict(itertools.chain( zip(log_topic_names, normalized_topic_data), zip(log_data_names, normalized_log_data), )) event_data = { 'args': event_args, 'event': event_abi['name'], 'logIndex': log_entry['logIndex'], 'transactionIndex': log_entry['transactionIndex'], 'transactionHash': log_entry['transactionHash'], 'address': log_entry['address'], 'blockHash': log_entry['blockHash'], 'blockNumber': log_entry['blockNumber'], } return AttributeDict.recursive(event_data)