Docs
Supported ChainsMantleJSON-RPC APISmart Contract Methods

eth_getFilterLogs - Mantle RPC Method

Returns all logs matching a previously created filter on Mantle. Essential for initial log retrieval, backfilling event data, and one-time historical queries for liquid staking (mETH $1.87B TVL), institutional DeFi via Bybit, and yield optimization strategies.

Returns an array of all logs matching the filter that was previously created with eth_newFilter on Mantle. 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 Mantle? Build on the world's largest ZK rollup by TVL with $2.5B+ secured and deep Bybit integration with near-instant ZK finality via OP Succinct, $6.2B treasury backing, mETH liquid staking, and 25% Bybit trading fee discounts.

When to Use This Method

eth_getFilterLogs is essential for DeFi developers, liquid staking builders, and teams seeking institutional exchange integration:

  • Initial Log Retrieval — Fetch the complete set of matching logs when you first create a filter on Mantle
  • 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

Request Parameters

Request
filterIdQUANTITY

The filter ID returned by eth_newFilter. Only log filters are supported — block and pending transaction filters will return an error

Response Body

Response
addressDATA

Address from which the log originated

topicsArray<DATA>

Array of 0-4 indexed log arguments (32 bytes each)

dataDATA

Non-indexed log arguments (ABI-encoded)

blockNumberQUANTITY

Block number where the log was emitted

transactionHashDATA

Hash of the transaction that generated the log

transactionIndexQUANTITY

Index position of the transaction in the block

blockHashDATA

Hash of the block containing the log

logIndexQUANTITY

Log index position in the block

removedBoolean

true if the log was removed due to a chain reorganization

Error Responses

Errors
Error Response-32000

Code Examples

Bash
# First, create a filter
FILTER_ID=$(curl -s -X POST https://api-mantle-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_newFilter",
    "params": [{
      "fromBlock": "0x1234500",
      "toBlock": "0x1234600",
      "address": "0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8"
    }],
    "id": 1
  }' | jq -r '.result')

# Then, get all matching logs
curl -X POST https://api-mantle-mainnet.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d "{
    \"jsonrpc\": \"2.0\",
    \"method\": \"eth_getFilterLogs\",
    \"params\": [\"$FILTER_ID\"],
    \"id\": 2
  }"

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;
      }
    }
  }
}