Docs
Docs
Home
Getting Started
Agent Tooling
CLI
Supported Chains
Acala
Aptos
Arbitrum
Arc
Asset Hub
Astar
Avalanche
Base
Berachain
Bifrost
Binance Smart Chain
Bittensor
Blast
Boba Network
Bridge Hub
Celo
Centrifuge
Chiliz
Cronos
Enjin
Ethereum
Etherlink
Flow EVM Gateway
Gnosis
Hydration
Hyperliquid
Immutable
Ink
IoTeX
Kusama
Linea
LISK
Manta Atlantic
Manta Pacific
Mantle
MegaETH
Monad
Movement
Neuroweb
opBNB
Optimism
Plasma
Polkadot
Polygon
Pulsechain
Robinhood
Ronin
Scroll
Sonic
Starknet
Taiko
Tempo
Sui
TRON
Unichain
Viction
World Chain
XDC Network
XX Network
Zetachain
zkSync Era
Zora
Supported ChainsPlasmaJSON-RPC APISmart Contract Methods

eth_getFilterChanges - Plasma RPC Method

Poll a filter for new results since the last poll on Plasma. Essential for event streaming, real-time monitoring, and efficient log indexing for stablecoin transfers, payment apps, and wallets that need 1 second confirmation.

Plasma RPC endpoint: endpoint URLs, plans and pricing.

Polls a filter on Plasma for changes since the last poll. Log filters return log objects. Block filters return block hashes. Pending transaction filters return transaction hashes.

Why Plasma? Build on an EVM Layer 1 for stablecoin payments with PlasmaBFT consensus and Reth execution with 1 second blocks, XPL gas, full EVM JSON-RPC, and debug plus txpool on Dwellir.

When to Use This Method

eth_getFilterChanges is essential for payment app developers, wallet teams, and teams shipping Solidity contracts for dollar transfers:

  • Event Streaming - Incrementally consume new contract events without re-fetching the entire log history on Plasma
  • Real-Time Monitoring - Track contract activity, token transfers, or governance votes for stablecoin transfers, payment apps, and wallets that need 1 second confirmation
  • 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

Best Practices

  • Poll filters at most every 1-5 seconds; excessive polling wastes bandwidth and may trigger rate limiting
  • Always call eth_uninstallFilter when monitoring is complete to release server-side resources
  • Handle null or empty array returns gracefully as they indicate no new results since the last poll
  • Use WebSocket subscriptions (eth_subscribe) instead of polling for real-time production applications
JSON-RPC request
{
  "jsonrpc": "2.0",
  "method": "eth_getFilterChanges",
  "params": ["0x1a"],
  "id": 1
}

Code Examples

Common Use Cases

1. Real-Time Token Transfer Monitor

Stream ERC-20 transfer events on Plasma:

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

Related Methods

  • eth_newFilter - Create a log/event filter to poll with this method
  • eth_newBlockFilter - Create a block filter for new block notifications
  • eth_getFilterLogs - Get all logs matching a filter (full history, not incremental)
  • eth_uninstallFilter - Remove a filter when no longer needed
  • eth_getLogs - Query logs directly without creating a filter

eth_newFilter

Create an event log filter on Plasma. Essential for event monitoring, contract activity tracking, and DeFi event streaming for stablecoin transfers, payment apps, and wallets that need 1 second confirmation.

eth_getFilterLogs

Returns all logs matching a previously created filter on Plasma. Essential for initial log retrieval, backfilling event data, and one-time historical queries for stablecoin transfers, payment apps, and wallets that need 1 second confirmation.

On this page

When to Use This MethodBest PracticesCode ExamplesCommon Use Cases1. Real-Time Token Transfer Monitor2. Multi-Contract Event Aggregator3. Block-Aware Event Processor with Reorg HandlingError HandlingRelated Methods