Docs

eth_getFilterLogs - Robinhood RPC Method

Returns all logs matching a previously created filter on Robinhood Chain. Essential for initial log retrieval, backfilling event data, and one-time historical queries for tokenized assets, DeFi applications, historical state analysis, and EVM smart contracts.

Returns an array of all logs matching the filter that was previously created with eth_newFilter on Robinhood Chain. Unlike eth_getFilterChanges which returns only new logs since the last poll, this method returns the complete set of matching logs for the filter's block range.

Why Robinhood? Build on an Ethereum-compatible Layer 2 built with Arbitrum Nitro for tokenized real-world assets with Arbitrum Nitro compatibility, ETH gas, sub-second soft confirmations, archive history, and WebSocket access.

When to Use This Method

eth_getFilterLogs is essential for Robinhood Chain developers, financial application teams, and EVM builders:

  • Initial Log Retrieval - Fetch the complete set of matching logs when you first create a filter on Robinhood
  • Backfilling Event Data - Recover historical events after an indexer restart or gap in polling
  • One-Time Queries - Retrieve all logs for a specific block range without incremental polling
  • Data Reconciliation - Compare against incrementally collected data from eth_getFilterChanges to detect missed events

Best Practices

  • Prefer eth_getLogs for most use cases; this method is designed for filters created with eth_newFilter
  • Results are subject to node log retention limits; historical queries may return incomplete data
  • Always call eth_uninstallFilter after retrieving logs to free filter resources

Robinhood backend affinity

Filter IDs are backend-local. The archive endpoint is served by multiple backends, so create, poll, and uninstall a filter over one persistent WebSocket connection. Separate HTTP requests can reach another backend and return filter not found. Use eth_getLogs for stateless historical log queries.

Code Examples

Common Use Cases

1. Backfill Event Data After Indexer Restart

Recover missed events when your indexer goes down and comes back:

JavaScript
async function backfillEvents(provider, contractAddress, topics, lastProcessedBlock) {
  const currentBlock = await provider.getBlockNumber();

  // Create a filter covering the gap
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: '0x' + (lastProcessedBlock + 1).toString(16),
    toBlock: '0x' + currentBlock.toString(16),
    address: contractAddress,
    topics: topics
  }]);

  // Retrieve all logs in the gap
  const logs = await provider.send('eth_getFilterLogs', [filterId]);
  console.log(`Backfilling ${logs.length} events from blocks ${lastProcessedBlock + 1} to ${currentBlock}`);

  for (const log of logs) {
    await processEvent(log);
  }

  // Clean up and switch to incremental polling
  await provider.send('eth_uninstallFilter', [filterId]);
  return currentBlock;
}

2. Compare Filter Results with Direct Query

Verify data consistency between filter-based and direct log queries:

JavaScript
async function verifyFilterResults(provider, filterParams) {
  // Create filter and get logs via eth_getFilterLogs
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const filterLogs = await provider.send('eth_getFilterLogs', [filterId]);

  // Get logs directly via eth_getLogs
  const directLogs = await provider.send('eth_getLogs', [filterParams]);

  console.log(`Filter logs: ${filterLogs.length}, Direct logs: ${directLogs.length}`);

  if (filterLogs.length !== directLogs.length) {
    console.warn('Mismatch detected - investigate missing events');
  }

  await provider.send('eth_uninstallFilter', [filterId]);
  return { filterLogs, directLogs };
}

3. Paginated Historical Event Loader

Load large volumes of historical events in manageable chunks:

JavaScript
async function loadHistoricalEvents(provider, contractAddress, startBlock, endBlock, chunkSize = 2000) {
  const allLogs = [];

  for (let from = startBlock; from <= endBlock; from += chunkSize) {
    const to = Math.min(from + chunkSize - 1, endBlock);

    const filterId = await provider.send('eth_newFilter', [{
      fromBlock: '0x' + from.toString(16),
      toBlock: '0x' + to.toString(16),
      address: contractAddress
    }]);

    const logs = await provider.send('eth_getFilterLogs', [filterId]);
    allLogs.push(...logs);

    console.log(`Blocks ${from}-${to}: ${logs.length} events (total: ${allLogs.length})`);

    await provider.send('eth_uninstallFilter', [filterId]);
  }

  return allLogs;
}

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32000Filter not foundFilter expired or was uninstalled - recreate with eth_newFilter
-32000Query returned more than 10000 resultsNarrow the block range in your filter - use smaller fromBlock/toBlock windows
-32600Invalid requestVerify the filter ID is a valid hex string
-32603Internal errorNode may be overloaded - retry with exponential backoff
-32005Rate limit exceededReduce request frequency or implement client-side rate limiting
JavaScript
async function safeGetFilterLogs(provider, filterId, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await provider.send('eth_getFilterLogs', [filterId]);
    } catch (error) {
      if (error.message.includes('filter not found')) {
        throw new Error('Filter expired - must recreate before retrying');
      }
      if (error.message.includes('more than 10000 results')) {
        throw new Error('Too many results - narrow your block range');
      }
      if (i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000;
        await new Promise(r => setTimeout(r, delay));
      } else {
        throw error;
      }
    }
  }
}