Docs
Supported ChainsCronosJSON-RPC APISmart Contract Methods

eth_getFilterChanges - Cronos RPC Method

Poll a filter for new results since the last poll on Cronos. Essential for event streaming, real-time monitoring, and efficient log indexing for DeFi protocols, NFT marketplaces, and Crypto.com ecosystem integrations.

Polls a filter on Cronos and returns an array of changes (logs, block hashes, or transaction hashes) that have occurred since the last poll. The return type depends on the filter that was created — log filters return log objects, block filters return block hashes, and pending transaction filters return transaction hashes.

Why Cronos? Build on the EVM-compatible Crypto.com blockchain with fast finality and deep Crypto.com ecosystem integration.

When to Use This Method

eth_getFilterChanges is essential for Cronos developers building DeFi and payment applications:

  • Event Streaming — Incrementally consume new contract events without re-fetching the entire log history on Cronos
  • Real-Time Monitoring — Track contract activity, token transfers, or governance votes for DeFi protocols, NFT marketplaces, and Crypto.com ecosystem integrations
  • Efficient Log Indexing — Process only new events since your last poll, minimizing bandwidth and compute overhead
  • Block & Transaction Tracking — When used with block or pending-transaction filters, detect new blocks or mempool activity in real time

Request Parameters

Request
filterIdQUANTITY

The filter ID returned by eth_newFilter, eth_newBlockFilter, or eth_newPendingTransactionFilter

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

resultArray<DATA>

Array of 32-byte block hashes of new blocks

resultArray<DATA>

Array of 32-byte transaction hashes of pending transactions

Error Responses

Errors
Error Response-32000

Code Examples

Bash
curl -X POST https://api-cronos-mainnet-archive.n.dwellir.com/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getFilterChanges",
    "params": ["0x1a"],
    "id": 1
  }'

Common Use Cases

1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Cronos:

JavaScript
async function monitorTransfers(provider, tokenAddress) {
  // Transfer event topic: keccak256('Transfer(address,address,uint256)')
  const transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';

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

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

  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 — recreating...');
        // Filters expire after ~5 minutes of inactivity on most nodes
      }
    }
  }, 3000);
}

2. Multi-Contract Event Aggregator

Monitor events across multiple contracts simultaneously:

JavaScript
async function aggregateEvents(provider, contracts) {
  const filterId = await provider.send('eth_newFilter', [{
    fromBlock: 'latest',
    address: contracts  // Array of contract addresses
  }]);

  const eventBuffer = [];
  let pollCount = 0;

  const interval = setInterval(async () => {
    const changes = await provider.send('eth_getFilterChanges', [filterId]);
    pollCount++;

    if (changes.length > 0) {
      eventBuffer.push(...changes);
      console.log(`Poll #${pollCount}: ${changes.length} new events (total: ${eventBuffer.length})`);

      // Process in batches
      if (eventBuffer.length >= 50) {
        await processBatch(eventBuffer.splice(0, 50));
      }
    }
  }, 2000);

  return { filterId, stop: () => clearInterval(interval) };
}

3. Block-Aware Event Processor with Reorg Handling

Detect chain reorganizations by checking the removed flag:

JavaScript
async function safeEventProcessor(provider, filterParams) {
  const filterId = await provider.send('eth_newFilter', [filterParams]);
  const processedEvents = new Map();

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

    for (const log of changes) {
      const eventKey = `${log.transactionHash}-${log.logIndex}`;

      if (log.removed) {
        // Chain reorganization — undo previously processed event
        console.warn(`Reorg detected: removing event ${eventKey}`);
        processedEvents.delete(eventKey);
        await rollbackEvent(log);
      } else {
        processedEvents.set(eventKey, log);
        await processEvent(log);
      }
    }
  }, 2000);
}

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32000Filter not foundFilter expired or was uninstalled — recreate with eth_newFilter
-32600Invalid requestVerify the filter ID is correctly formatted as a hex string
-32603Internal errorNode may be overloaded — retry with exponential backoff
-32005Rate limit exceededReduce polling frequency or implement client-side rate limiting
JavaScript
async function resilientPoll(provider, filterId, createFilter, interval = 2000) {
  let currentFilterId = filterId;
  let retries = 0;

  while (true) {
    try {
      const changes = await provider.send('eth_getFilterChanges', [currentFilterId]);
      retries = 0;

      if (changes.length > 0) {
        return changes;
      }
    } catch (error) {
      if (error.message.includes('filter not found')) {
        console.log('Filter expired — recreating...');
        currentFilterId = await createFilter();
      } else if (error.code === -32005) {
        const delay = Math.pow(2, retries) * 1000;
        retries++;
        await new Promise(r => setTimeout(r, delay));
      } else {
        throw error;
      }
    }
    await new Promise(r => setTimeout(r, interval));
  }
}