> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# BlockFilter

> Block hash filter for monitoring new blocks

## Overview

`BlockFilter` represents a filter created by `eth_newBlockFilter` that notifies of new block hashes. Used for monitoring blockchain progress and detecting reorgs without polling full blocks.

## Type Definition

```typescript theme={null}
type BlockFilterType = {
  readonly filterId: FilterIdType;
  readonly type: "block";
} & {
  readonly [brand]: "BlockFilter";
};
```

## Creating BlockFilter

### from

```typescript theme={null}
import * as BlockFilter from './primitives/BlockFilter/index.js';
import * as FilterId from './primitives/FilterId/index.js';

// Create filter on node
const filterIdStr = await rpc.eth_newBlockFilter();
const filterId = FilterId.from(filterIdStr);

// Wrap in BlockFilter type
const filter = BlockFilter.from(filterId);
```

**Parameters:**

* `filterId`: `FilterIdType` - Filter identifier from `eth_newBlockFilter`

**Returns:** `BlockFilterType`

## JSON-RPC Usage

### Create Filter

```typescript theme={null}
// Returns filter ID
const filterIdStr = await rpc.eth_newBlockFilter();
const filterId = FilterId.from(filterIdStr);
const filter = BlockFilter.from(filterId);
```

### Poll for Changes

```typescript theme={null}
// Returns array of new block hashes since last poll
const blockHashes = await rpc.eth_getFilterChanges(filter.filterId);

for (const hash of blockHashes) {
  console.log(`New block: ${hash}`);
}
```

### Uninstall Filter

```typescript theme={null}
const success = await rpc.eth_uninstallFilter(filter.filterId);
```

## Example: Block Monitor

```typescript theme={null}
import * as BlockFilter from './primitives/BlockFilter/index.js';
import * as FilterId from './primitives/FilterId/index.js';
import { Hash } from './primitives/Hash/index.js';

// Install block filter
const filterIdStr = await rpc.eth_newBlockFilter();
const filterId = FilterId.from(filterIdStr);
const filter = BlockFilter.from(filterId);

console.log(`Block filter installed: ${filterId.toString()}`);

// Poll every 15 seconds
const interval = setInterval(async () => {
  try {
    const hashes = await rpc.eth_getFilterChanges(filter.filterId);

    if (hashes.length > 0) {
      console.log(`New blocks: ${hashes.length}`);

      for (const hashStr of hashes) {
        const hash = Hash.from(hashStr);
        const block = await rpc.eth_getBlockByHash(hash, false);
        console.log(`Block ${block.number}: ${hashes.length} txs`);
      }
    }
  } catch (error) {
    console.error('Filter error:', error);
    clearInterval(interval);
  }
}, 15000);

// Cleanup on exit
process.on('SIGINT', async () => {
  await rpc.eth_uninstallFilter(filter.filterId);
  clearInterval(interval);
  process.exit();
});
```

## Example: Reorg Detector

```typescript theme={null}
import * as BlockFilter from './primitives/BlockFilter/index.js';
import * as FilterId from './primitives/FilterId/index.js';

// Track block chain
const blockChain = new Map(); // hash -> block number
let lastBlockNumber = 0n;

// Install filter
const filterId = FilterId.from(await rpc.eth_newBlockFilter());
const filter = BlockFilter.from(filterId);

setInterval(async () => {
  const hashes = await rpc.eth_getFilterChanges(filter.filterId);

  for (const hashStr of hashes) {
    const hash = Hash.from(hashStr);
    const block = await rpc.eth_getBlockByHash(hash, false);

    // Check for reorg
    if (block.number <= lastBlockNumber) {
      console.warn(`REORG DETECTED at block ${block.number}`);
      console.warn(`Old hash: ${blockChain.get(block.number)}`);
      console.warn(`New hash: ${hash.toHex()}`);
    }

    blockChain.set(block.number, hash);
    lastBlockNumber = block.number;
  }
}, 15000);
```

## Comparison with eth\_subscribe

### BlockFilter (eth\_newBlockFilter)

**Pros:**

* HTTP compatible (no WebSocket required)
* Simple request-response pattern
* Works with all RPC providers

**Cons:**

* Polling-based (less efficient)
* Delayed notifications (poll interval)
* Filter expiration if not polled

```typescript theme={null}
// HTTP polling
const filterId = FilterId.from(await rpc.eth_newBlockFilter());
setInterval(async () => {
  const hashes = await rpc.eth_getFilterChanges(filterId);
  // Process hashes...
}, 15000);
```

### eth\_subscribe

**Pros:**

* Real-time push notifications
* More efficient (no polling)
* No filter expiration

**Cons:**

* Requires WebSocket connection
* Not supported by all providers
* More complex error handling

```typescript theme={null}
// WebSocket subscription
const subscription = await ws.eth_subscribe('newHeads');
subscription.on('data', (block) => {
  console.log(`New block: ${block.hash}`);
});
```

## Filter Expiration

Block filters expire after inactivity (typically 5 minutes). Best practices:

```typescript theme={null}
async function pollBlockFilter(filterId: FilterIdType) {
  try {
    const hashes = await rpc.eth_getFilterChanges(filterId);
    return hashes;
  } catch (error) {
    if (error.message.includes('filter not found')) {
      // Recreate filter
      const newFilterId = FilterId.from(await rpc.eth_newBlockFilter());
      return pollBlockFilter(newFilterId);
    }
    throw error;
  }
}
```

## Performance Considerations

### Polling Frequency

Choose poll interval based on:

* Block time (12s for Ethereum mainnet)
* Filter expiration timeout
* Application latency requirements

```typescript theme={null}
// Conservative: Poll every 15s (safe from expiration)
const interval = 15000;

// Aggressive: Poll every 6s (faster notifications, more requests)
const interval = 6000;

// Aligned: Poll on expected block time
const interval = 12000;
```

### Batch Processing

Process multiple blocks efficiently:

```typescript theme={null}
const hashes = await rpc.eth_getFilterChanges(filterId);

// Fetch all blocks in parallel
const blocks = await Promise.all(
  hashes.map(hash => rpc.eth_getBlockByHash(hash, false))
);

// Process blocks
for (const block of blocks) {
  await processBlock(block);
}
```

## Use Cases

### Block Height Tracker

```typescript theme={null}
let currentBlock = 0n;

setInterval(async () => {
  const hashes = await rpc.eth_getFilterChanges(filterId);

  for (const hash of hashes) {
    const block = await rpc.eth_getBlockByHash(hash, false);
    currentBlock = block.number;
    console.log(`Current block: ${currentBlock}`);
  }
}, 15000);
```

### Transaction Confirmation Monitor

```typescript theme={null}
async function waitForConfirmations(txHash, confirmations = 12) {
  const receipt = await rpc.eth_getTransactionReceipt(txHash);
  let currentBlock = receipt.blockNumber;

  const filterId = FilterId.from(await rpc.eth_newBlockFilter());

  return new Promise((resolve) => {
    const interval = setInterval(async () => {
      const hashes = await rpc.eth_getFilterChanges(filterId);

      if (hashes.length > 0) {
        const block = await rpc.eth_getBlockByHash(hashes[0], false);
        currentBlock = block.number;

        const confs = currentBlock - receipt.blockNumber;
        if (confs >= confirmations) {
          await rpc.eth_uninstallFilter(filterId);
          clearInterval(interval);
          resolve(confs);
        }
      }
    }, 15000);
  });
}
```

### Network Activity Monitor

```typescript theme={null}
const filterId = FilterId.from(await rpc.eth_newBlockFilter());

setInterval(async () => {
  const hashes = await rpc.eth_getFilterChanges(filterId);

  if (hashes.length === 0) {
    console.log('No new blocks (network stalled?)');
    return;
  }

  const blocks = await Promise.all(
    hashes.map(hash => rpc.eth_getBlockByHash(hash, false))
  );

  const totalTxs = blocks.reduce((sum, b) => sum + b.transactions.length, 0);
  const avgGasUsed = blocks.reduce((sum, b) => sum + b.gasUsed, 0n) / BigInt(blocks.length);

  console.log(`Blocks: ${blocks.length}, Txs: ${totalTxs}, Avg Gas: ${avgGasUsed}`);
}, 15000);
```

## Related Types

* [FilterId](/primitives/filter-id) - Filter identifier
* [LogFilter](/primitives/log-filter) - Event log filter
* [PendingTransactionFilter](/primitives/pending-transaction-filter) - Pending tx filter
* [BlockHash](/primitives/block-hash) - Block hash type
* [BlockNumber](/primitives/block-number) - Block number type

## JSON-RPC Methods

* `eth_newBlockFilter` - Create block hash filter
* `eth_getFilterChanges` - Poll for new blocks
* `eth_uninstallFilter` - Remove filter
* `eth_getBlockByHash` - Fetch full block

## See Also

* [Ethereum JSON-RPC Specification](https://ethereum.github.io/execution-apis/api-documentation/)
* [eth\_subscribe (WebSocket alternative)](https://ethereum.github.io/execution-apis/api-documentation/#eth_subscribe)
