Links

Event Processors

Learn about structure of a custom event processor script
As defined in manifest.yml each custom event processor has a handler.ts and an abi.json. When a custom processor is deployed it listens for all the "event" topics defined in the abi.json, and executes handler.js for every single event.
As mentioned in Getting Started guide, you can see the basic structure of a processor in the starter-boilerplate repo.

Example of an empty processor

src/my-awesome-processor/handler.ts
manifest.yml
import { EventHandlerInput, database } from "flair-sdk";
​
export async function (event: EventHandlerInput) {
console.debug(
'Processing my custom event: ',
{ event, myVar: process.env.SOME_VARIABLE }
);
};
manifest: 1.0.0
​
# ... rest of your manifest.yml
​
processors:
- id: my-awesome-processor
handler: ./src/my-awesome-processor/handler.ts
abi: ./src/my-awesome-processor/abi.json
env:
- name: SOME_VARIABLE
value: xxxxxxxxxxxx

handler.ts

  • handler.js must export a function named processEvent
  • processEvent function will receive 1 argument:
    • An object that gives you event that gives you access to the received transaction log and parsed event.
  • processEvent function must return either:
    • true which means event was processed
    • false which means event was skipped for any reason
    • undefined which is considered a success
  • processEvent has a timeout of 60 seconds (a soft-limit that can be increased if you needed).
  • The boolean value returned by processEvent is only used for statistics reasons, so it won't impact your processor logic if anything (or nothing) is returned.
`event` object
export type EventHandlerInput<ArgsType = Record<string, any>> = {
chainId: number
blockHash: string
blockNumber: number
blockTimestamp: number
txHash: string
txIndex: number
// Incoming EVM event log object with additional information.
// See below.
log: IngestedLog
// An object used for chronological ordering and handling re-orgs,
// When using database integration you just pass this object as-is.
horizon: Horizon;
// If this log was processed correctly (based on abi.json), then
// this object contains human-readable "name" and named "args" of the event.
parsed: {
name?: string
topic?: string
args?: ArgsType
}
// Exact event object found in your abi.json for this log topic
abi: {
type: 'event';
name: string;
inputs: Record<string, any>[];
// ... any other user-defined field (.e.g "entityName")
}[];
}
​
export type IngestedLog = {
// Normalized Data from RPC nodes
blockNumber: `0x${string}`
blockHash: string
address: string
data: string
logIndex: number
topics: string[]
transactionHash: string
transactionIndex: number
​
// Augmented data from RPC nodes
blockTimestamp: number
​
//////
// Additional Flair-specific data:
//////
chainId: number
// If there was a re-org for this block blockForkIndex
// will be increased incrementally. So you can depend on this value
// if you need to know which log is the latest.
// Our database integration automatically takes care of upserting
// the correct entity because this value is part of "horizon" desribed
// above.
blockForkIndex: number
// Index of this log within the current transaction,
// which calculated by Flair before sending the log to your processor.
localIndex: number
// The time when this log was delivered to Flair by the RPC node.
ingestionReceivedAt: number
// Indicates how this event log was ingested.
// - "realtime" means it was captured via RPC websocket or http-based listener
// - "redrive" means RPC node failed to emit but Flair redriver caught the event after a delay
// - "backfill" means this log is received via a user-initiated backfill request
delivery: 'realtime' | 'redrive' | 'backfill'
}
​
export type Horizon = {
blockNumber: number
forkIndex: number
transactionIndex: number
// Exact logIndex reported by RPC,
// which is index of this event in the whole block.
logIndex: number
// Index of this log within the current transaction,
// which calculated by Flair before sending the log to your processor.
localIndex: number
}
Example usage:
import { EventHandlerInput, database } from "flair-sdk";
​
export async function (event: EventHandlerInput) {
console.debug(
`My custom event block number: ${event.blockNumber.toString()}`,
`My custom event tx hash: ${event.txHash}`,
`My custom event name: ${event.parsed.name}`,
`My custom event topic hash: ${event.log.topics[0]}`,
`My custom event args object: ${event.parsed.args}`,
);
};

Need help how to work with certain data?

Reach out to our engineers 🙂

abi.json

This file is used to know which event topics to listen to and parse incoming transaction logs, before they are handed over to your processor handler function above.

Example of an abi.json:

[
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
// and more events...
]
​

Importing others files or libraries

You can import any .ts, .js, and .json file relative your handler.ts processor as long as they are under the same src/ directory. If you need any help ping our engineers 🙂
Last modified 29d ago