Filters and Events in web3j

·

When building decentralized applications (dApps) on the Ethereum blockchain, real-time interaction with network activity is essential. This is where filters and events in web3j come into play. Designed for Java and Android developers, web3j simplifies Ethereum integration by offering powerful tools to monitor blockchain activity through asynchronous event handling. Whether you're tracking new blocks, pending transactions, or smart contract events, web3j’s event-driven model streamlines development and enhances responsiveness.

This guide explores how web3j manages Ethereum filters—block filters, pending transaction filters, and topic-based event filters—while abstracting away the complexity of manual polling and JSON-RPC intricacies.

Understanding Ethereum Filters

Ethereum provides three primary types of filters to monitor network activity:

  1. Block filters – Notify when a new block is added to the chain.
  2. Pending transaction filters – Capture transactions before they are included in a block.
  3. Topic filters – Monitor specific smart contract events based on indexed parameters.

Traditionally, interacting with these filters via JSON-RPC requires constant polling over HTTP or IPC connections—a tedious and inefficient process. Moreover, basic filters only return hashes; retrieving full transaction or block data demands additional API calls.

👉 Discover how modern blockchain tools simplify real-time data streaming

web3j solves these challenges by wrapping filter operations in RxJava Observables, enabling fully asynchronous, reactive programming patterns. This allows developers to respond to blockchain events in real time with clean, composable code.

Note: Filter functionality is not supported by Infura when using standard HTTP endpoints. For full filter support, consider using WebSocket connections or alternative node providers.

Listening to Blocks and Transactions

With web3j, subscribing to live blockchain events becomes straightforward using observable streams.

Monitor New Blocks

To receive notifications whenever a new block is mined (without transaction details):

Subscription subscription = web3j.blockObservable(false)
    .subscribe(block -> {
        System.out.println("New block mined: " + block.getBlock().getNumber());
    });

Setting the parameter to true includes full transaction objects within each block.

Track New Transactions

To observe all transactions as they are added to the blockchain:

Subscription subscription = web3j.transactionObservable()
    .subscribe(tx -> {
        System.out.println("Transaction confirmed: " + tx.getHash());
    });

Watch Pending Transactions

To capture transactions as soon as they enter the mempool (before confirmation):

Subscription subscription = web3j.pendingTransactionObservable()
    .subscribe(tx -> {
        System.out.println("Pending transaction: " + tx.getHash());
    });

These observables emit items asynchronously, allowing your application to react instantly without blocking threads.

Always remember to unsubscribe when no longer needed:

subscription.unsubscribe();

For lower-level access—such as receiving only block or transaction hashes—you can use the Web3jRx interface to build custom workflows.

Replaying Historical Blockchain Data

Beyond real-time monitoring, web3j supports replaying historical blockchain data, which is crucial for syncing dApps or analyzing past activity.

Replay Blocks in a Range

To iterate through blocks within a specific range:

Subscription subscription = web3j.replayBlocksObservable(
        startBlock, endBlock, true)
    .subscribe(block -> {
        // Process each block
    });

Replay Transactions in Blocks

To extract individual transactions from historical blocks:

Subscription subscription = web3j.replayTransactionsObservable(
        startBlock, endBlock)
    .subscribe(tx -> {
        // Handle each transaction
    });

Catch Up to Latest Block

To process all missed blocks up to the current chain tip:

Subscription subscription = web3j.catchUpToLatestBlockObservable(
        startBlock, true, block -> {
            // Handle historical and latest blocks
        });

Continuous Sync: Replay + Real-Time Subscription

For applications requiring both historical sync and ongoing updates:

Subscription subscription = web3j.catchUpToLatestAndSubscribeToNewBlocksObservable(
        startBlock, true)
    .subscribe(block -> {
            // Handles both past and future blocks
        });

Similarly, use catchUpToLatestAndSubscribeToNewTransactionsObservable() to process all past transactions and stay updated on new ones in real time.

All of these methods are accessible via the Web3jRx interface, giving developers fine-grained control over data flow.

Working with Topic Filters and EVM Events

Smart contracts emit EVM events—log entries stored in transaction receipts—that can be filtered and monitored. These events are critical for tracking state changes, user interactions, and off-chain indexing.

According to the Solidity documentation, events are declared in contracts and emitted during execution. Only indexed parameters appear as topics in logs and can be filtered.

Use the EthFilter class to define what events you want to monitor:

EthFilter filter = new EthFilter(
    DefaultBlockParameterName.EARLIEST,
    DefaultBlockParameterName.LATEST,
    contractAddress)
    .addSingleTopic("0x...") // Keccak256 hash of the event signature
    .addOptionalTopics(topic1, topic2);

Then subscribe to matching logs:

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

Key Limitations of Topic Filtering

This makes careful event design in smart contracts essential for efficient off-chain monitoring.

👉 Learn how advanced filtering powers scalable dApp backends

Functional Composition with Observables

One of web3j’s strengths lies in its use of functional reactive programming (FRP) via RxJava. Every JSON-RPC method that supports it exposes an .observable() variant, enabling elegant composition of blockchain queries.

For example, the blockObservable is built by combining two RPC calls:

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

Here’s how it works:

  1. Poll for new block hashes (eth_blockHash).
  2. For each hash, fetch full block details (eth_getBlockByHash).
  3. Emit complete Block objects downstream.

This pattern enables powerful workflows—like filtering transactions by sender, decoding event data, or aggregating balances—using standard RxJava operators such as map, filter, merge, and reduce.

Practical Use Cases and Best Practices

Always:

For hands-on examples, refer to:

Frequently Asked Questions (FAQ)

Q: Can I use filters with Infura?
A: Not over HTTP. Infura disables filter methods on REST endpoints. Use WebSockets or alternative node services for full filter support.

Q: What are the performance implications of broad filters?
A: Filters without address or topic restrictions generate high volumes of data. Always narrow scope using contract addresses and specific topics.

Q: How does web3j handle network disconnections?
A: With standard HTTP polling, reconnection logic must be handled manually. WebSockets offer better resilience but require additional setup.

Q: Can I filter non-indexed event parameters?
A: No. Only indexed parameters are stored as topics and can be filtered at the node level.

Q: Are RxJava observables thread-safe?
A: Yes, but ensure proper synchronization when updating shared UI or state from multiple streams.

Q: Is there a limit to how far back I can replay blocks?
A: It depends on your Ethereum node’s pruning settings. Archive nodes retain full history; pruned nodes may not serve very old blocks.

👉 Explore how reactive programming transforms blockchain data handling

Core Keywords

By leveraging web3j’s robust filtering system and reactive design, developers can build responsive, scalable applications that seamlessly interact with the Ethereum ecosystem—whether pulling historical data or reacting to live network events.