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 allTransfer
events from any contractUSDT:*
- Match all events from theUSDT
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);
},
});