Event Listening

Event Listening

Open Ethereum Indexer provides an easy to use mechanism for monitoring and processing smart contract events. There are multiple ways to handle blockchain events and blocks. This page covers the simplest approach but there is also a nestjs way to handle events.

Event Handlers

Event handlers allow you to react to specific events emitted by smart contracts on the blockchain.

Pattern Matching

In order for handlers to respond to events, they need to be matched by a pattern. The pattern is a string that matches the contract name and event name delimited by a colon.

For example, the pattern USDT:Transfer will match the Transfer event from the USDT contract where USDT is the key in the contracts section of the config.

The pattern *:Transfer will match all Transfer events from any contract. When specifying a wildcard your config should NOT include an address field.

Some examples:

  • *:Transfer - Match all Transfer events from any contract
  • USDT:* - Match all events from the USDT contract
  • *:* - Match all events from all contracts

Registering Event Handlers

You can register event handlers using the onEvent function which takes an onIndex method which runs when the event is captured on chain. You can also add an onDeindex method which runs when the event is deindexed (during a chain reorganization). This is for notification purposes - you do NOT need to remove events that you added in the index method. This will be handled by the indexer.

import { onEvent } from '@open-ethereum/indexer';
 
// Register a handler for a specific event
onEvent('ContractName:EventName', {
  onIndex: async (payload) => {
    // This code runs when the event is indexed
    console.log('Event indexed:', payload);
 
    // Process event data
    const { log, parsedEvent, block } = payload;
    const { args } = parsedEvent;
 
    // Access event arguments
    console.log('Event arguments:', args);
 
    // Store event data in database or trigger other actions
  },
  onDeindex: async (payload) => {
    // This code runs if the event is deindexed (during a chain reorganization)
    console.log('Event deindexed:', payload);
 
    // You don't need to remove the previously indexed event here - it will be handled automatically.
    // But this mechanism serves as a notification in case you need to know that an event was deindexed.
  },
});

Event Payload

The event handler receives a payload with the following structure:

interface LogEvent {
  log: ethers.providers.Log; // Raw log data
  parsedEvent: ethers.utils.LogDescription; // Parsed event data
}

Order of Execution

Event handlers are run in the order they are registered. Each event (which is matched by a listener) is processed in the order it happens in the block. This is important to keep in mind when building dependencies between handlers.

Saving to Database

To save event data to the database, you'll use TypeORM entities:

import { getRepository } from 'typeorm';
import { Transfer } from './entities/Transfer.entity';
 
onEvent('ERC20:Transfer', {
  onIndex: async (payload) => {
    const { from, to, value } = payload.parsedEvent.args;
    const transferRepo = getRepository(Transfer);
 
    // Create and save entity
    const transfer = new Transfer();
    transfer.from = from;
    transfer.to = to;
    transfer.value = value.toString();
    transfer.blockNumber = payload.block.number;
    transfer.timestamp = payload.block.timestamp;
 
    await transferRepo.save(transfer);
  },
});

Example: Monitoring ERC-20 Transfers

Here's a complete example of monitoring all ERC-20 Transfer events:

import { onEvent } from '@open-ethereum/indexer';
import { getRepository } from 'typeorm';
import { Transfer } from './entities/Transfer.entity';
 
onEvent('*:Transfer', {
  onIndex: async (payload) => {
    const { log, parsedEvent, block } = payload;
    const { from, to, value } = parsedEvent.args;
 
    // Get contract name or address
    const contractAddress = log.address;
 
    const transferRepo = getRepository(Transfer);
 
    const transfer = new Transfer();
    transfer.contractAddress = contractAddress;
    transfer.from = from;
    transfer.to = to;
    transfer.value = value.toString();
    transfer.blockNumber = block.number;
    transfer.transactionHash = log.transactionHash;
    transfer.logIndex = log.logIndex;
    transfer.timestamp = block.timestamp;
 
    await transferRepo.save(transfer);
  },
});