Understanding Filters and Events in Ethereum with web3j

·

Filters are a crucial mechanism for receiving notifications about specific events occurring on the Ethereum blockchain. They enable developers to track real-time updates without constant manual polling. The Ethereum network supports three primary types of filters:

While block and pending transaction filters provide basic notifications, topic filters offer advanced customization for specific use cases. However, using filters via JSON-RPC can be cumbersome, requiring synchronous HTTP or IPC requests to check for updates. Additionally, basic filters only return transaction or block hashes, necessitating further requests to retrieve full details.

web3j simplifies this process with its asynchronous filter implementation, leveraging RxJava Observables for a seamless experience. This approach eliminates manual polling and provides a composable API for handling blockchain events efficiently.

⚠️ Note: Filters are not supported when using Infura endpoints.

Block and Transaction Filters

web3j allows you to observe new blocks and transactions as they are added to the blockchain. Here’s how to set up subscribers for these events:

New Blocks Observation
To monitor new blocks (without including full transaction details):

Subscription subscription = web3j.blockObservable(false).subscribe(block -> {
    // Process block data
});

New Transactions Observation
To track all new transactions added to the blockchain:

Subscription subscription = web3j.transactionObservable().subscribe(tx -> {
    // Process transaction data
});

Pending Transactions Observation
To detect transactions submitted to the network but not yet mined:

Subscription subscription = web3j.pendingTransactionObservable().subscribe(tx -> {
    // Process pending transaction data
});

Always unsubscribe from observables when they are no longer needed to free up resources:

subscription.unsubscribe();

For more granular control, the Web3jRx interface provides additional callbacks returning only hashes, which can be used to fetch detailed data on demand.

Replay Filters

web3j enables historical data replay through replay filters, allowing you to process past blocks or transactions within a specified range.

Replay Blocks in a Range
To process all blocks between two block numbers:

Subscription subscription = web3j.replayBlocksObservable(
        startBlockNumber, endBlockNumber, fullTxObjects)
    .subscribe(block -> {
        // Process replayed block
    });

Replay Transactions in a Range
To iterate through transactions in a block range:

Subscription subscription = web3j.replayTransactionsObservable(
        startBlockNumber, endBlockNumber)
    .subscribe(tx -> {
        // Process replayed transaction
    });

Catch-Up to Latest Block
To replay historical blocks and then receive notifications for new ones:

Subscription subscription = web3j.catchUpToLatestBlockObservable(
        startBlockNumber, fullTxObjects, pollingInterval)
    .subscribe(block -> {
        // Process block
    });

Continuous Block Monitoring
To replay past blocks and subscribe to future ones:

Subscription subscription = web3j.catchUpToLatestAndSubscribeToNewBlocksObservable(
        startBlockNumber, fullTxObjects)
    .subscribe(block -> {
        // Process block
    });

Continuous Transaction Monitoring
To replay past transactions and subscribe to new ones:

Subscription subscription = web3j.catchUpToLatestAndSubscribeToNewTransactionsObservable(
        startBlockNumber)
    .subscribe(tx -> {
        // Process transaction
    });

These methods are part of the Web3jRx interface, providing flexible options for data retrieval and real-time monitoring.

Topic Filters and EVM Events

Topic filters capture EVM events emitted by smart contracts and stored in transaction logs. These events are defined in Solidity contracts and provide insights into contract state changes.

According to the Solidity documentation, events are inheritable members of contracts that facilitate logging and external monitoring.

Creating a Topic Filter
Use the EthFilter class to specify filter parameters, including contract addresses and topics:

EthFilter filter = new EthFilter(
        DefaultBlockParameterName.EARLIEST,
        DefaultBlockParameterName.LATEST,
        "0xContractAddress"
);

You can narrow down events using topics:

Subscribing to EVM Events
Once the filter is configured, subscribe to logs:

web3j.ethLogObservable(filter).subscribe(log -> {
    // Process event log
});

Important Limitations:

Functional Composition Explained

web3j supports functional composition through Observables, enabling elegant chaining of JSON-RPC calls. Besides send() and sendAsync(), all web3j methods implement an observable() method for asynchronous execution.

For example, the blockObservable method combines multiple JSON-RPC calls:

public Observable<Block> blockObservable(boolean fullTransactionObjects, long pollingInterval) {
    return this.ethBlockHashObservable(pollingInterval)
        .flatMap(blockHash ->
            web3j.ethGetBlockByHash(blockHash, fullTransactionObjects).observable());
}

This implementation first observes new block hashes, then uses flatMap to fetch full block details for each hash. This pattern simplifies complex data retrieval workflows.

👉 Explore advanced filtering techniques

Frequently Asked Questions

What are the main types of filters in Ethereum?
Ethereum supports block filters, pending transaction filters, and topic filters. Block and transaction filters provide basic notifications, while topic filters enable custom event tracking from smart contracts.

Why does web3j use Observables for filters?
web3j leverages RxJava Observables to handle asynchronous event streaming efficiently. This approach eliminates manual polling and allows developers to compose complex event-handling logic with minimal boilerplate code.

Can I filter non-indexed event parameters?
No, topic filters only work with indexed parameters. Non-indexed parameters are stored in logs but cannot be used for filtering. Additionally, variable-length parameters are hashed before storage.

How do I unsubscribe from an Observable?
Call subscription.unsubscribe() when the Observable is no longer needed. This prevents memory leaks and unnecessary network requests.

Are filters supported on Infura?
No, Infura does not support filters due to architectural limitations. Consider running your own node or using alternative services for filter functionality.

What is the purpose of replay filters?
Replay filters allow processing historical blockchain data between specific blocks. This is useful for analytics, auditing, or syncing off-chain databases with on-chain events.

Additional Examples

For practical implementations, refer to the following resources in the web3j codebase:

These examples provide hands-on guidance for integrating filters into your Ethereum applications.