def get_filter_args_for_specific_event_from_channel( token_network_address: Address, channel_identifier: ChannelID, event_name: str, from_block: BlockSpecification = 0, to_block: BlockSpecification = 'latest', ): """ Return the filter params for a specific event of a given channel. """ if not event_name: raise ValueError('Event name must be given') contract_manager = ContractManager(CONTRACTS_PRECOMPILED_PATH) event_abi = contract_manager.get_event_abi(CONTRACT_TOKEN_NETWORK, event_name) # Here the topics for a specific event are created # The first entry of the topics list is the event name, then the first parameter is encoded, # in the case of a token network, the first parameter is always the channel identifier _, event_filter_params = construct_event_filter_params( event_abi=event_abi, contract_address=to_checksum_address(token_network_address), argument_filters={ 'channel_identifier': channel_identifier, }, fromBlock=from_block, toBlock=to_block, ) return event_filter_params
def get_logs(self, event_name, from_block=0, to_block='latest', filters=None): filter_kwargs = { 'fromBlock': from_block, 'toBlock': to_block, 'address': self.contract_addr } event_abi = [ i for i in self.auction_contract.abi if i['type'] == 'event' and i['name'] == event_name ][0] assert event_abi filters = filters if filters else {} filter_ = construct_event_filter_params(event_abi, argument_filters=filters, **filter_kwargs)[1] filter_params = input_filter_params_formatter(filter_) response = self.chain.web3._requestManager.request_blocking( 'eth_getLogs', [filter_params]) logs = log_array_formatter(response) logs = [dict(log) for log in logs] for log in logs: log['args'] = get_event_data(event_abi, log)['args'] return logs
def create_event_filter( web3: Web3, event_name: str, event_abi: Dict, filter_params: Dict = {} ) -> LogFilter: """Create filter object that tracks events emitted. Args: web3: A web3 client event_name: The name of the event to track event_abi: The ABI of the event to track filter_params: Other parameters to limit the events Returns: A LogFilter instance""" filter_meta_params = dict(filter_params) data_filter_set, event_filter_params = construct_event_filter_params( event_abi, **filter_meta_params ) log_data_extract_fn = functools.partial(get_event_data, event_abi) log_filter = web3.eth.filter(event_filter_params) log_filter.set_data_filters(data_filter_set) log_filter.log_entry_formatter = log_data_extract_fn log_filter.filter_params = event_filter_params return log_filter
def get_logs( contract: Contract, event_name: str, from_block: Union[int, str] = 0, to_block: Union[int, str] = 'pending', argument_filters: Dict[str, Any] = None ): event_abi = [ abi_element for abi_element in contract.abi if abi_element['type'] == 'event' and abi_element['name'] == event_name ] assert len(event_abi) == 1, 'No event found matching name {}.'.format(event_name) event_abi = event_abi[0] if argument_filters is None: argument_filters = {} filter_params = input_filter_params_formatter(construct_event_filter_params( event_abi, argument_filters=argument_filters, address=contract.address, fromBlock=from_block, toBlock=to_block )[1]) response = _get_logs_raw(contract, filter_params) logs = log_array_formatter(response) logs = [dict(log) for log in logs] for log in logs: log['args'] = get_event_data(event_abi, log)['args'] return logs
def __init__(self, web3, abi, address, event_name, from_block=0, to_block='latest', filters=None, callback=None): self.web3 = web3 filter_kwargs = { 'fromBlock': from_block, 'toBlock': to_block, 'address': address } self.event_abi = [ i for i in abi if i['type'] == 'event' and i['name'] == event_name ][0] assert self.event_abi filters = filters if filters else {} self.filter = construct_event_filter_params(self.event_abi, argument_filters=filters, **filter_kwargs)[1] filter_params = input_filter_params_formatter(self.filter) self.filter = web3.eth.filter(filter_params) for log in self.get_logs(): callback(log) self.watch_logs(callback)
def get_filter_args_for_channel_from_token_network( token_network_address: Address, channel_identifier: ChannelID, from_block: BlockSpecification = 0, to_block: BlockSpecification = 'latest', ) -> Dict: event_abi = CONTRACT_MANAGER.get_event_abi(CONTRACT_TOKEN_NETWORK, EVENT_CHANNEL_OPENED) # Here the topics for a specific event are created # The first entry of the topics list is the event name, then the first parameter is encoded, # in the case of a token network, the first parameter is always the channel identifier data_filter_set, event_filter_params = construct_event_filter_params( event_abi=event_abi, contract_address=to_checksum_address(token_network_address), argument_filters={ 'channel_identifier': channel_identifier, }, fromBlock=from_block, toBlock=to_block, ) # As we want to get all events for a certain channel we remove the event specific code here # and filter just for the channel identifier # We also have to remove the trailing topics to get all filters event_filter_params['topics'] = [None, event_filter_params['topics'][1]] return event_filter_params
def on(self, event_name, filter_params=None, *callbacks): """ register a callback to be triggered on the appropriate events. """ if filter_params is None: filter_params = {} argument_filters = filter_params.pop('filter', {}) argument_filter_names = list(argument_filters.keys()) event_abi = self._find_matching_event_abi( event_name, argument_filter_names, ) data_filter_set, event_filter_params = construct_event_filter_params( event_abi, contract_address=self.address, argument_filters=argument_filters, **filter_params ) log_data_extract_fn = functools.partial(get_event_data, event_abi) log_filter = self.web3.eth.filter(event_filter_params) log_filter.set_data_filters(data_filter_set) log_filter.log_entry_formatter = log_data_extract_fn log_filter.filter_params = event_filter_params if callbacks: log_filter.watch(*callbacks) return log_filter
def __init__(self, web3, abi, address, event_name, from_block=0, to_block='latest', filters=None, callback=None): self.web3 = web3 self.event_name = event_name # Callback for every registered log self.callback = callback filter_kwargs = { 'fromBlock': from_block, 'toBlock': to_block, 'address': address } event_abi = [i for i in abi if i['type'] == 'event' and i['name'] == event_name] if len(event_abi) == 0: return None self.event_abi = event_abi[0] assert self.event_abi filters = filters if filters else {} self.filter = construct_event_filter_params( self.event_abi, argument_filters=filters, **filter_kwargs)[1] filter_params = input_filter_params_formatter(self.filter) self.filter = web3.eth.filter(filter_params)
def get_filter_args_for_specific_event_from_channel( token_network_address: TokenNetworkAddress, channel_identifier: ChannelID, event_name: str, contract_manager: ContractManager, from_block: BlockSpecification = GENESIS_BLOCK_NUMBER, to_block: BlockSpecification = 'latest', ): """ Return the filter params for a specific event of a given channel. """ if not event_name: raise ValueError('Event name must be given') event_abi = contract_manager.get_event_abi(CONTRACT_TOKEN_NETWORK, event_name) # Here the topics for a specific event are created # The first entry of the topics list is the event name, then the first parameter is encoded, # in the case of a token network, the first parameter is always the channel identifier _, event_filter_params = construct_event_filter_params( event_abi=event_abi, contract_address=to_checksum_address(token_network_address), argument_filters={ 'channel_identifier': channel_identifier, }, fromBlock=from_block, toBlock=to_block, ) return event_filter_params
def on(self, event_name, filter_params=None, *callbacks): """ Register a callback to be triggered on the appropriate events. """ if filter_params is None: filter_params = {} argument_filters = filter_params.pop('filter', {}) argument_filter_names = list(argument_filters.keys()) event_abi = self._find_matching_event_abi( event_name, argument_filter_names, ) data_filter_set, event_filter_params = construct_event_filter_params( event_abi, contract_address=self.address, argument_filters=argument_filters, **filter_params) log_data_extract_fn = functools.partial(get_event_data, event_abi) log_filter = self.web3.eth.filter(event_filter_params) log_filter.set_data_filters(data_filter_set) log_filter.log_entry_formatter = log_data_extract_fn log_filter.filter_params = event_filter_params if callbacks: log_filter.watch(*callbacks) return log_filter
def get_filter_args_for_specific_event_from_channel( token_network_address: TokenNetworkAddress, channel_identifier: ChannelID, event_name: str, contract_manager: ContractManager, from_block: BlockSpecification = GENESIS_BLOCK_NUMBER, to_block: BlockSpecification = "latest", ): """ Return the filter params for a specific event of a given channel. """ if not event_name: raise ValueError("Event name must be given") event_abi = contract_manager.get_event_abi(CONTRACT_TOKEN_NETWORK, event_name) # Here the topics for a specific event are created # The first entry of the topics list is the event name, then the first parameter is encoded, # in the case of a token network, the first parameter is always the channel identifier _, event_filter_params = construct_event_filter_params( event_abi=event_abi, contract_address=to_checksum_address(token_network_address), argument_filters={"channel_identifier": channel_identifier}, fromBlock=from_block, toBlock=to_block, ) return event_filter_params
def eventFilter(self, event_name, filter_params={}): """ Create filter object that tracks events emitted by this contract. :param event_name: the name of the event to track :param filter_params: other parameters to limit the events """ filter_meta_params = dict(filter_params) argument_filters = filter_meta_params.pop('filter', {}) argument_filter_names = list(argument_filters.keys()) event_abi = self._find_matching_event_abi( event_name, argument_filter_names, ) data_filter_set, event_filter_params = construct_event_filter_params( event_abi, contract_address=self.address, argument_filters=argument_filters, **filter_meta_params ) log_data_extract_fn = functools.partial(get_event_data, event_abi) log_filter = self.web3.eth.filter(event_filter_params) log_filter.set_data_filters(data_filter_set) log_filter.log_entry_formatter = log_data_extract_fn log_filter.filter_params = event_filter_params return log_filter
def eventFilter(self, event_name, filter_params={}): """ Create filter object that tracks events emitted by this contract. :param event_name: the name of the event to track :param filter_params: other parameters to limit the events """ filter_meta_params = dict(filter_params) argument_filters = filter_meta_params.pop('filter', {}) argument_filter_names = list(argument_filters.keys()) event_abi = self._find_matching_event_abi( event_name, argument_filter_names, ) data_filter_set, event_filter_params = construct_event_filter_params( event_abi, contract_address=self.address, argument_filters=argument_filters, **filter_meta_params) log_data_extract_fn = functools.partial(get_event_data, event_abi) log_filter = self.web3.eth.filter(event_filter_params) log_filter.set_data_filters(data_filter_set) log_filter.log_entry_formatter = log_data_extract_fn log_filter.filter_params = event_filter_params return log_filter
def make_filter(web3, event_abi, filters={}, **filter_kwargs): assert event_abi != [] log_data_extract_fn = functools.partial(get_event_data, event_abi) data_filter_set, filter_params = construct_event_filter_params( event_abi, argument_filters=filters, **filter_kwargs) event_filter = web3.eth.filter(filter_params) event_filter.log_entry_formatter = log_data_extract_fn event_filter.set_data_filters(data_filter_set) event_filter.filter_params = filter_params return event_filter
def getLogs(self, argument_filters=None, fromBlock=None, toBlock="latest", address=None, topics=None): """Get events using eth_getLogs API. This is a stateless method, as opposite to createFilter. It can be safely called against nodes which do not provide eth_newFilter API, like Infura. :param argument_filters: :param fromBlock: :param toBlock: :param address: :param topics: :return: """ if fromBlock is None: raise TypeError( "Missing mandatory keyword argument to getLogs: fromBlock") abi = self._get_event_abi() argument_filters = dict() _filters = dict(**argument_filters) # Construct JSON-RPC raw filter presentation based on human readable Python descriptions # Namely, convert event names to their keccak signatures data_filter_set, event_filter_params = construct_event_filter_params( abi, abi_codec=self.web3.codec, contract_address=self.address, argument_filters=_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics, ) # Call JSON-RPC API logs = self.web3.eth.getLogs(event_filter_params) # Convert raw binary data to Python proxy objects as described by ABI for entry in logs: yield get_event_data(self.web3.codec, abi, entry)
def get_event_logs( web3: Web3, contract_abi: Dict, event_name: str, from_block: int = 0, to_block: int = None ): """Helper function to get all event logs in a given range""" abi = find_matching_event_abi(contract_abi, event_name) log_data_extract_fn = functools.partial(get_event_data, abi) data_filter_set, filter_params = construct_event_filter_params( abi, argument_filters=None, fromBlock=from_block, toBlock=to_block ) event_filter = web3.eth.filter(filter_params) event_filter.log_entry_formatter = log_data_extract_fn event_filter.set_data_filters(data_filter_set) event_filter.filter_params = filter_params entries = event_filter.get_all_entries() web3.eth.uninstallFilter(event_filter.filter_id) return entries
def createFilter( self, *, # PEP 3102 argument_filters=None, fromBlock=None, toBlock="latest", address=None, topics=None): """ Create filter object that tracks logs emitted by this contract event. :param filter_params: other parameters to limit the events """ if fromBlock is None: raise TypeError( "Missing mandatory keyword argument to createFilter: fromBlock" ) if argument_filters is None: argument_filters = dict() if not address: address = self.address _filters = dict(**argument_filters) data_filter_set, event_filter_params = construct_event_filter_params( self._get_event_abi(), contract_address=self.address, argument_filters=_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics, ) log_data_extract_fn = functools.partial(get_event_data, self._get_event_abi()) log_filter = self.web3.eth.filter(event_filter_params) log_filter.set_data_filters(data_filter_set) log_filter.log_entry_formatter = log_data_extract_fn log_filter.filter_params = event_filter_params return log_filter
def get_logs( # pylint: disable=too-many-arguments self, contract, event_name: str, args, from_block: Union[int, str], to_block: Union[int, str]): event_abi = list( filter( lambda e: e['type'] == 'event' and e['name'] == event_name, contract.abi, ))[0] for name in args: assert any(name == event['name'] for event in event_abi['inputs']) _, filter_args = construct_event_filter_params( event_abi, contract_address=contract.address, argument_filters=args, fromBlock=from_block, toBlock=to_block, ) return self.web3.eth.getLogs(filter_args)
def __init__( self, web3: Web3, abi: List[Any], address: HexAddress, event_name: str, from_block: int = 0, to_block: Union[int, str] = "latest", filters: Any = None, callback: Optional[Callable[..., Any]] = None, ): self.web3 = web3 self.event_name = event_name # Callback for every registered log self.callback = callback filter_kwargs = { "fromBlock": from_block, "toBlock": to_block, "address": address } event_abi = [ i for i in abi if i["type"] == "event" and i["name"] == event_name ] if len(event_abi) == 0: raise ValueError(f"Event of name {event_name} not found") self.event_abi = event_abi[0] assert self.event_abi filters = filters if filters else {} data_filter_set, filter_params = construct_event_filter_params( event_abi=self.event_abi, argument_filters=filters, **filter_kwargs) log_data_extract_fn = functools.partial(get_event_data, event_abi) self.filter = web3.eth.filter(filter_params) self.filter.set_data_filters(data_filter_set) self.filter.log_entry_formatter = log_data_extract_fn self.filter.filter_params = filter_params
async def get_new_entries_from_logs( self, event_name: str, block_hashes: List[HexBytes]) -> List[AttributeDict]: event_abi: Dict[str, any] = self._event_abi_map.get(event_name, None) if event_abi is None: event_abi = find_matching_event_abi(self._contract_abi, event_name=event_name) self._event_abi_map[event_name] = event_abi _, event_filter_params = construct_event_filter_params( event_abi, contract_address=self._address) tasks = [] for block_hash in block_hashes: event_filter_params["blockHash"] = block_hash.hex() tasks.append(self._get_logs(event_filter_params)) raw_logs = await asyncio.gather(*tasks, return_exceptions=True) logs: List[any] = list(cytoolz.concat(raw_logs)) new_entries = [] for log in logs: event_data: AttributeDict = get_event_data(event_abi, log) event_data_block_number: int = event_data["blockNumber"] event_data_tx_hash: HexBytes = event_data["transactionHash"] if event_data_tx_hash not in self._event_cache: if event_data_block_number not in self._block_events: self._block_events[event_data_block_number] = [ event_data_tx_hash ] else: self._block_events[event_data_block_number].append( event_data_tx_hash) self._event_cache.add(event_data_tx_hash) new_entries.append(event_data) else: self.logger().debug( f"Duplicate event transaction hash found - '{event_data_tx_hash.hex()}'." ) while len(self._block_events) > self._block_events_window_size: tx_hashes: List[HexBytes] = self._block_events.popitem( last=False)[1] for tx_hash in tx_hashes: self._event_cache.remove(tx_hash) return new_entries
def __init__(self, web3, abi, address, event_name, from_block=0, to_block='latest', filters=None, callback=None): self.web3 = web3 self.event_name = event_name # Callback for every registered log self.callback = callback filter_kwargs = { 'fromBlock': from_block, 'toBlock': to_block, 'address': address, } event_abi = [ i for i in abi if i['type'] == 'event' and i['name'] == event_name ] if len(event_abi) == 0: return None self.event_abi = event_abi[0] assert self.event_abi filters = filters if filters else {} data_filter_set, filter_params = construct_event_filter_params( self.event_abi, argument_filters=filters, **filter_kwargs, ) log_data_extract_fn = functools.partial(get_event_data, event_abi) self.filter = web3.eth.filter(filter_params) self.filter.set_data_filters(data_filter_set) self.filter.log_entry_formatter = log_data_extract_fn self.filter.filter_params = filter_params
def decorator(handler_func): event_abi = self.contract._find_matching_event_abi(event_name, {}) _, event_filter_params = construct_event_filter_params( event_abi, contract_address=self.contract.address) if iscoroutinefunction(handler_func): async def decorated(payload): event_data = get_event_data(event_abi, payload) return await handler_func(event_name, event_data) else: def decorated(payload): event_data = get_event_data(event_abi, payload) return handler_func(event_name, event_data) self.topic_filters.append(event_filter_params) self.handlers[event_filter_params["topics"][0]] = decorated return decorated
def __init__(self, web3, abi, address, event_name, from_block=0, to_block='latest', filters=None, callback=None): self.web3 = web3 self.event_name = event_name # Callback for every registered log self.callback = callback filter_kwargs = { 'fromBlock': from_block, 'toBlock': to_block, 'address': address } event_abi = [i for i in abi if i['type'] == 'event' and i['name'] == event_name] if len(event_abi) == 0: return None self.event_abi = event_abi[0] assert self.event_abi filters = filters if filters else {} data_filter_set, filter_params = construct_event_filter_params( self.event_abi, argument_filters=filters, **filter_kwargs ) log_data_extract_fn = functools.partial(get_event_data, event_abi) self.filter = web3.eth.filter(filter_params) self.filter.set_data_filters(data_filter_set) self.filter.log_entry_formatter = log_data_extract_fn self.filter.filter_params = filter_params
def test_construct_event_filter_params_for_data_filters(event_abi, fn_kwargs, expected): actual, _ = construct_event_filter_params(event_abi, **fn_kwargs) assert actual == expected
def test_construct_event_filter_params(event_abi, fn_kwargs, expected): _, actual = construct_event_filter_params(event_abi, **fn_kwargs) assert actual == expected
def getLogs( self, event, web3, argument_filters: Optional[Dict[str, Any]] = None, fromBlock: Optional[BlockIdentifier] = None, toBlock: Optional[BlockIdentifier] = None, blockHash: Optional[HexBytes] = None, ): """Get events for this contract instance using eth_getLogs API. This is a stateless method, as opposed to createFilter. It can be safely called against nodes which do not provide eth_newFilter API, like Infura nodes. If there are many events, like ``Transfer`` events for a popular token, the Ethereum node might be overloaded and timeout on the underlying JSON-RPC call. Example - how to get all ERC-20 token transactions for the latest 10 blocks: .. code-block:: python from = max(mycontract.web3.eth.blockNumber - 10, 1) to = mycontract.web3.eth.blockNumber events = mycontract.events.Transfer.getLogs(fromBlock=from, toBlock=to) for e in events: print(e["args"]["from"], e["args"]["to"], e["args"]["value"]) The returned processed log values will look like: .. code-block:: python ( AttributeDict({ 'args': AttributeDict({}), 'event': 'LogNoArguments', 'logIndex': 0, 'transactionIndex': 0, 'transactionHash': HexBytes('...'), 'address': '0xF2E246BB76DF876Cef8b38ae84130F4F55De395b', 'blockHash': HexBytes('...'), 'blockNumber': 3 }), AttributeDict(...), ... ) See also: :func:`web3.middleware.filter.local_filter_middleware`. :param argument_filters: :param fromBlock: block number or "latest", defaults to "latest" :param toBlock: block number or "latest". Defaults to "latest" :param blockHash: block hash. blockHash cannot be set at the same time as fromBlock or toBlock :yield: Tuple of :class:`AttributeDict` instances """ if not self.address: raise TypeError( "This method can be only called on " "an instated contract with an address" ) abi = event._get_event_abi() if argument_filters is None: argument_filters = dict() _filters = dict(**argument_filters) blkhash_set = blockHash is not None blknum_set = fromBlock is not None or toBlock is not None if blkhash_set and blknum_set: raise ValidationError( "blockHash cannot be set at the same" " time as fromBlock or toBlock" ) # Construct JSON-RPC raw filter presentation based on human readable Python descriptions # Namely, convert event names to their keccak signatures _, event_filter_params = construct_event_filter_params( abi, contract_address=self.address, argument_filters=_filters, fromBlock=fromBlock, toBlock=toBlock, ) if blockHash is not None: event_filter_params["blockHash"] = blockHash # Call JSON-RPC API logs = web3.eth.getLogs(event_filter_params) # Convert raw binary data to Python proxy objects as described by ABI return tuple(get_event_data(abi, entry) for entry in logs)
def test_construct_event_filter_params_for_data_filters( event_abi, fn_kwargs, expected): actual, _ = construct_event_filter_params(event_abi, **fn_kwargs) assert actual == expected
def test_construct_event_filter_params(event_abi, fn_kwargs, expected): _, actual = construct_event_filter_params(event_abi, **fn_kwargs) assert actual == expected