# eth_newFilter - Moonbase RPC Method

Creates a filter object on Moonbase Alpha based on the given filter options, to notify when the state changes (new logs). The filter monitors for log entries that match the specified criteria - contract addresses, topics, and block ranges. Use `eth_getFilterChanges` to poll for new matching logs or `eth_getFilterLogs` to retrieve all matching logs at once.

> **Why Moonbase?** Build on the Moonbeam testnet for risk-free EVM dApp development and deployment testing with free testnet tokens, full EVM compatibility, XCM testing capabilities, and identical feature set to Moonbeam mainnet.

## When to Use This Method

`eth_newFilter` is essential for Moonbeam developers, dApp testers, and teams validating cross-chain integrations:

- **Event Monitoring** - Subscribe to specific contract events on Moonbase such as token transfers, approvals, or governance votes
- **Contract Activity Tracking** - Watch one or multiple contracts for any emitted events relevant to EVM dApp testing, XCM integration validation, and pre-mainnet deployment verification
- **DeFi Event Streaming** - Monitor swap events, liquidity changes, or oracle price updates in real time
- **Incremental Indexing** - Build event indexes by creating a filter and polling with `eth_getFilterChanges` for only new logs

The filter's block range follows the same plan limits and validation rules as `eth_getLogs`.
See [Rate Limits](https://www.dwellir.com/docs/getting-started/rate-limits#eth_getlogs-block-range-limits) before creating a filter over historical blocks.

## Best Practices

- Prefer `eth_getLogs` for one-time queries instead of creating and then immediately uninstalling a filter
- Always call `eth_uninstallFilter` when a filter is no longer needed to free node resources
- Handle timeout errors gracefully; filters expire after approximately 5 minutes of inactivity on most nodes
- Limit the block range in filter options to avoid query errors on nodes with range restrictions

## Request Parameters

- `fromBlock` (`QUANTITY|TAG, optional`): Starting block number (hex) or tag ("latest", "earliest", "pending"). Defaults to "latest"
- `toBlock` (`QUANTITY|TAG, optional`): Ending block number (hex) or tag. Defaults to "latest"
- `address` (`DATA, required`): No
- `topics` (`Array<DATA, required`): null>

## Request Example

```json
{
  "jsonrpc": "2.0",
  "method": "eth_newFilter",
  "params": [{
    "fromBlock": "latest",
    "toBlock": "latest",
    "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
    "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
  }],
  "id": 1
}
```

## Response Fields

- `result` (`QUANTITY, required`): A hex-encoded filter ID used to poll for matching logs via eth_getFilterChanges or eth_getFilterLogs

## Successful Response

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1a2b3c"
}
```

## Error Responses

### Error Response

- Code: `-32000`

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "filter block range too large"
  }
}
```

## Code Examples

cURL
JavaScript
Python
Go

```bash
# Create a filter for Transfer events on a specific contract
curl -X POST https://api-moonbase-alpha.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "latest",
      "address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id": 1
  }'
```

```javascript
import { JsonRpcProvider } from 'ethers';

const provider = new JsonRpcProvider('https://api-moonbase-alpha.n.dwellir.com/YOUR_API_KEY');

// Create a filter for Transfer events
const filterId = await provider.send('eth_newFilter', [{
  fromBlock: 'latest',
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}]);

console.log('Filter created:', filterId);

// Poll for new events
const interval = setInterval(async () => {
  const logs = await provider.send('eth_getFilterChanges', [filterId]);
  if (logs.length > 0) {
    console.log(`${logs.length} new events found`);
    for (const log of logs) {
      console.log(`  Block ${parseInt(log.blockNumber, 16)}: ${log.transactionHash}`);
    }
  }
}, 3000);

// Clean up when done
// clearInterval(interval);
// await provider.send('eth_uninstallFilter', [filterId]);
```

```python
import requests
import time

RPC_URL = 'https://api-moonbase-alpha.n.dwellir.com/YOUR_API_KEY'

def rpc_call(method, params):
    response = requests.post(RPC_URL, json={
        'jsonrpc': '2.0',
        'method': method,
        'params': params,
        'id': 1
    })
    return response.json()

# Create a filter for Transfer events
filter_result = rpc_call('eth_newFilter', [{
    'fromBlock': 'latest',
    'address': '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
    'topics': ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}])
filter_id = filter_result['result']
print(f'Filter created: {filter_id}')

# Poll for new events
try:
    while True:
        changes = rpc_call('eth_getFilterChanges', [filter_id])
        logs = changes.get('result', [])
        if logs:
            print(f'{len(logs)} new events found')
            for log in logs:
                block = int(log['blockNumber'], 16)
                print(f'  Block {block}: {log["transactionHash"]}')
        time.sleep(3)
finally:
    # Clean up
    rpc_call('eth_uninstallFilter', [filter_id])
```

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, err := ethclient.Dial("https://api-moonbase-alpha.n.dwellir.com/YOUR_API_KEY")
    if err != nil {
        log.Fatal(err)
    }

    contractAddress := common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
    transferTopic := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))

    query := ethereum.FilterQuery{
        Addresses: []common.Address{contractAddress},
        Topics:    [][]common.Hash,
    }

    // Subscribe to logs (WebSocket) or poll (HTTP)
    logsCh := make(chan types.Log)
    sub, err := client.SubscribeFilterLogs(context.Background(), query, logsCh)
    if err != nil {
        // Fallback: polling
        currentBlock, _ := client.BlockNumber(context.Background())
        ticker := time.NewTicker(3 * time.Second)

        for range ticker.C {
            latestBlock, _ := client.BlockNumber(context.Background())
            if latestBlock > currentBlock {
                query.FromBlock = new(big.Int).SetUint64(currentBlock + 1)
                query.ToBlock = new(big.Int).SetUint64(latestBlock)
                logs, err := client.FilterLogs(context.Background(), query)
                if err == nil {
                    for _, l := range logs {
                        fmt.Printf("Event in block %d: %s\n", l.BlockNumber, l.TxHash.Hex())
                    }
                }
                currentBlock = latestBlock
            }
        }
        return
    }

    for {
        select {
        case err := <-sub.Err():
            log.Fatal(err)
        case vLog := <-logsCh:
            fmt.Printf("Event in block %d: %s\n", vLog.BlockNumber, vLog.TxHash.Hex())
        }
    }
}
```

## Common Use Cases

### 1. ERC-20 Token Transfer Monitor

Watch for all transfers of a specific token on Moonbase:

```javascript
async function monitorTokenTransfers(provider, tokenAddress) {
  // keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: tokenAddress,
    topics: [transferTopic]
  }]);

  console.log(`Monitoring ${tokenAddress} transfers...`);

  setInterval(async () => {
    try {
      const logs = await provider.send('eth_getFilterChanges', [filterId]);

      for (const log of logs) {
        const from = '0x' + log.topics[1].slice(26);
        const to = '0x' + log.topics[2].slice(26);
        const value = BigInt(log.data);

        console.log(`Transfer: ${from} -> ${to} (${value})`);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired - application should recreate');
      }
    }
  }, 3000);

  return filterId;
}
```

### 2. Multi-Event DeFi Monitor

Track multiple event types across DEX contracts:

```javascript
async function monitorDeFiEvents(provider, dexRouterAddress) {
  // Watch for Swap and LiquidityAdded events
  const swapTopic = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
  const syncTopic = '0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1';

  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: dexRouterAddress,
    topics: [[swapTopic, syncTopic]]  // OR matching - either event type
  }]);

  setInterval(async () => {
    const logs = await provider.send('eth_getFilterChanges', [filterId]);

    for (const log of logs) {
      const eventType = log.topics[0] === swapTopic ? 'SWAP' : 'SYNC';
      console.log(`${eventType} at block ${parseInt(log.blockNumber, 16)}`);
    }
  }, 2000);

  return filterId;
}
```

### 3. Filter Lifecycle Manager

Manage filter creation, polling, and cleanup with automatic renewal:

```javascript
class FilterManager {
  constructor(provider) {
    this.provider = provider;
    this.filters = new Map();
  }

  async createFilter(name, filterParams, callback) {
    const filterId = await this.provider.send('eth_newFilter', [filterParams]);

    const filter = {
      id: filterId,
      params: filterParams,
      callback,
      interval: setInterval(() => this.poll(name), 3000),
      lastPoll: Date.now()
    };

    this.filters.set(name, filter);
    console.log(`Filter "${name}" created: ${filterId}`);
    return filterId;
  }

  async poll(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    try {
      const logs = await this.provider.send('eth_getFilterChanges', [filter.id]);
      filter.lastPoll = Date.now();

      if (logs.length > 0) {
        await filter.callback(logs);
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log(`Filter "${name}" expired - recreating...`);
        const newId = await this.provider.send('eth_newFilter', [filter.params]);
        filter.id = newId;
      }
    }
  }

  async removeFilter(name) {
    const filter = this.filters.get(name);
    if (!filter) return;

    clearInterval(filter.interval);
    await this.provider.send('eth_uninstallFilter', [filter.id]);
    this.filters.delete(name);
    console.log(`Filter "${name}" removed`);
  }

  async removeAll() {
    for (const name of this.filters.keys()) {
      await this.removeFilter(name);
    }
  }
}

// Usage
const manager = new FilterManager(provider);

await manager.createFilter('transfers', {
  fromBlock: 'latest',
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (logs) => {
  console.log(`${logs.length} new transfer events`);
});
```

## Related Methods

- [`eth_getFilterChanges`](https://www.dwellir.com/docs/moonbase-alpha/eth_getFilterChanges) - Poll this filter for new logs since the last poll
- [`eth_getFilterLogs`](https://www.dwellir.com/docs/moonbase-alpha/eth_getFilterLogs) - Get all logs matching this filter at once
- [`eth_getLogs`](https://www.dwellir.com/docs/moonbase-alpha/eth_getLogs) - Query logs directly without creating a filter
- [`eth_uninstallFilter`](https://www.dwellir.com/docs/moonbase-alpha/eth_uninstallFilter) - Remove this filter when no longer needed
- [`eth_newBlockFilter`](https://www.dwellir.com/docs/moonbase-alpha/eth_newBlockFilter) - Create a filter for new block notifications instead of logs
