Skip to main content
Version: FireSquid

BatchContext for EVM

A EvmBatchProcessor instance expects a single user-defined batch handler to be implemented by the run() method:

processor.run<Store>(db: Database<Store>, batchHandler: (ctx: BatchContext<Store>) => Promise<void>)

The batch handler is an async void function. It repeatedly receives batches of archive data stored in ctx.blocks, transforms them and persists the results to the target database using the ctx.store interface.

BatchContext interface

The batch handler accepts a single argument of type BatchContext. It has the following structure:

export interface BatchContext<Store, Item> {
// an internal handle
_chain: Chain
// a logger to be used within the handler
log: Logger
// the facade interface for the target database
store: Store
// input on-chain data as requested by the subscriptions
blocks: BatchBlock<Item>[]
}

BatchBlock

The blocks field holds the log items data to be processed, aligned at the block level.

export interface BatchBlock<Item> {
header: EvmBlock
items: Item[]
}

BatchBlock.header contains the block header data. BatchBlock.items is a unified log containing the event and the transaction data items. It is canonically ordered following the EVM execution trace:

  • all transaction items respect the execution order with the block
  • all events emitted by a transaction are placed before the transaction item (if is requested);

Each data Item has the following structure:

{ 
// either it's an `event` or `transaction` item
kind: 'evmLog' | 'transaction',
// address of the contract that emitted the log or the transaction destination
address: string,
// the evm log data as specified by the corresponding `addLog()` or `addTransaction()` data selectors
evmLog?: {},
// the transaction data as specified by the corresponding `addLog()` or `addTransaction()` data selectors
transaction?: {}
}

The block header

Here is the full list of fields for the EvmBlock type:

export interface EvmBlock {
id: string
height: number
hash: string
parentHash: string
nonce?: bigint
sha3Uncles: string
logsBloom: string
transactionsRoot: string
stateRoot: string
receiptsRoot: string
miner: string
difficulty?: string
totalDifficulty?: string
extraData: string
size: bigint
gasLimit: bigint
gasUsed: bigint
timestamp: number
mixHash?: string
baseFeePerGas?: bigint
}

evmLog items

Here is a full list of fields for items with item.kind==='evmLog'.

{
kind: 'evmLog'
address: string
evmLog: {
id: string
blockNumber: number
address?: string
data?: string
index?: number
removed?: boolean
topics?: string[]
transactionIndex?: number
},
// transaction emitted the log
transaction: {
id?: string
from?: string
gas?: biging
gasPrice?: bigint
hash?: string
input?: string
nonce?: bigint
to?: string
index?: number
value?: bigint
type?: number
chainId?: number
v?: bigint
r?: string
s?: string
maxPriorityFeePerGas?: bigint
maxFeePerGas?: bigint
}
}

Note that to make the properties of item.evmLog and item.transaction available, one has to specify the corresponding data selectors in the addLog() configuration method.

transaction items

Here is a full list of fields for items with item.kind==='transaction'.

{
kind: 'transaction'
address: string // transaction.to
transaction: {
id?: string
from?: string
gas?: biging
gasPrice?: bigint
hash?: string
input?: string
nonce?: bigint
to?: string
index?: number
value?: bigint
type?: number
chainId?: number
v?: bigint
r?: string
s?: string
maxPriorityFeePerGas?: bigint,
maxFeePerGas?: bigint,
}
}

Note that to make the properties of item.transaction available, one has to specify the corresponding data selectors in the addTransaction() configuration method.

Store

A concrete ctx.store instance is derived at runtime from the run() method argument via

processor.run<Store>(db: Database<Store>, batchHandler: (ctx: BatchContext<Store>) => Promise<void>)

For Postgres-compatible Databases, ctx.store has a TypeORM EntityManager-like interface extended with additional support for batch updates. The interface may differ for other Database implementations, including the experimental @subsquid/file-store package.

See Processor Store for details.

Logger

The log field is a dedicated Logger instance. See Logging for more details.

Example

The handler below simply outputs all the log items emitted by the contract 0x2E645469f354BB4F5c8a05B3b30A929361cf77eC and saves some bogus data to the store:

import { Store, TypeormDatabase } from '@subsquid/typeorm-store';
import { EvmBatchProcessor } from '@subsquid/evm-processor'
import { MyEntity } from './model/generated/myEntity.model';

const processor = new EvmBatchProcessor()
.setDataSource({
archive: 'https://eth.archive.subsquid.io',
})
.setBlockRange({ from: 6175243 })
.addLog('0x2E645469f354BB4F5c8a05B3b30A929361cf77eC', {
filter: [[ ]],
data: {
evmLog: {
topics: true,
data: true,
},
} as const,
});

processor.run(new TypeormDatabase(), async (ctx) => {
for (const c of ctx.blocks) {
for (const i of c.items) {
ctx.log.info(i, `Item:`)
}
}
await ctx.store.save([
new MyEntity({id: '1', foo: 'bar'}),
new MyEntity({id: '2', foo: 'baz'})
])
});

One can experiment with the data selectors and see how the output changes.

For more elaborate examples, check the Gravatar squid and EVM Examples.