EVM Chains
This covers Ethereum and other blockchains compatible with the Ethereum Virtual Machine (EVM), such as Polygon, Binance Smart Chain (BSC), Arbitrum, Optimism, Base, Avalanche, etc.
Core Concepts
Processors
EVM integration primarily uses three types of processors:
-
Contract Processors (e.g.,
EthProcessor
,PolygonProcessor
, etc.): Binds to a specific smart contract address. Use this to process logs (events), transaction calls, and traces related to that contract.- Typical Use Case: Monitoring a specific dApp, token, or protocol.
- Binding:
Erc20Processor.bind({ address: '0x...', network: EthChainId.ETHEREUM, startBlock: 12345678 })
(Example uses a generated type-safe processor) - Can be generated automatically using the Sentio CLI based on a contract ABI:
sentio gen <abi_file_or_etherscan_link>
-
GlobalProcessor
: Processes data across the entire chain without binding to a specific address. Useful for chain-wide analytics or monitoring.- Typical Use Case: Tracking overall gas usage, monitoring large transactions across the network, analyzing block data.
- Binding:
GlobalProcessor.bind({ network: EthChainId.POLYGON, startBlock: 30000000 })
-
AccountProcessor
: Monitors activities related to a specific Externally Owned Account (EOA). Currently supports tracking incoming/outgoing native token transfers and logs emitted by the account (useful for contract wallets like Gnosis Safe).- Typical Use Case: Monitoring a specific user's wallet activity or a multi-sig wallet.
- Binding:
AccountProcessor.bind({ address: '0x...', network: EthChainId.BINANCE, startBlock: 20000000 })
Handlers
Handlers are defined within processors to react to on-chain activities:
-
Contract & Account Processors:
onEvent<TEvent extends TypedEvent>(handler(event, ctx), filter?)
: Triggered when a specific Solidity event matching thefilter
is emitted. Theevent
object provides decoded event parameters (event.args
). Filters are automatically generated for type-safe processors.onTrace<TTrace extends TypedCallTrace>(handler(trace, ctx), filter?)
: Triggered for internal function calls (traces) matching the functionfilter
(signature hash). Provides decoded input/output (trace.args
,trace.returnValue
). Filters are automatically generated for type-safe processors.onBlockInterval(handler(block, ctx), interval?, backfillInterval?)
: Processes blocks based on block number intervals.onTimeInterval(handler(block, ctx), intervalMinutes?, backfillIntervalMinutes?)
: Processes blocks based on time intervals.
-
Global Processor:
onTransaction(handler(tx, ctx), fetchConfig?)
: Triggered for every transaction on the network. Provides the rawTransactionResponseParams
.onTrace(handler(trace, ctx), signature | signatures[], fetchConfig?)
: Triggered for internal function calls matching specific signature hashes across any contract.onBlockInterval(handler(block, ctx), interval?, backfillInterval?)
: Processes blocks based on block number intervals.onTimeInterval(handler(block, ctx), intervalMinutes?, backfillIntervalMinutes?)
: Processes blocks based on time intervals.
Context (ctx
)
ctx
)Handler functions receive a Context
object (ContractContext
, AccountContext
, or GlobalContext
) providing:
- Chain information:
chainId
,network
(deprecated). - Block details:
blockNumber
,block
,timestamp
. - Transaction details:
transactionHash
,transaction
,transactionReceipt
. - Source details:
address
(contract or account address),log
(foronEvent
),trace
(foronTrace
). - Helper methods: Access to bound contract views for reading state (
ctx.contract.balanceOf(...)
), ethersprovider
(ctx.contract.provider
). - Standard SDK outputs:
ctx.meter.Counter('...')
,ctx.eventLogger.emit('...')
,ctx.exporter.eth_Block(...)
.
Fetch Configuration (fetchConfig
)
fetchConfig
)Handlers can include a fetchConfig
parameter to specify whether transaction receipts (receipt: true
), logs (logs: true
), or traces (traces: true
) should be fetched for the processed block, transaction, or event. This allows access to additional data within the handler if needed.
processor.onEvent(..., { traces: true }); // Fetch traces for transactions containing this event
Getting Started Example (ERC20 Token)
import { ERC20Processor } from "@sentio/sdk/eth/builtin";
import { EthChainId } from "@sentio/sdk/eth";
import type { TransferEvent, ApprovalEvent, ERC20Context } from "@sentio/sdk/eth/builtin/erc20";
const TOKEN_ADDRESS = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"; // Lido Staked ETH (stETH)
ERC20Processor.bind({ address: TOKEN_ADDRESS, network: EthChainId.ETHEREUM, startBlock: 11365000 })
.onEventTransfer(async (event: TransferEvent, ctx: ERC20Context) => {
ctx.meter.Counter("transfer_cnt").add(1, { token: "stETH" });
ctx.meter.Counter("transfer_volume").add(event.args.value, { token: "stETH" });
ctx.eventLogger.emit("Transfer", {
distinctId: event.args.from,
to: event.args.to,
value: event.args.value.scaleDown(18), // Scale down based on decimals
token: "stETH",
});
})
.onEventApproval(async (event: ApprovalEvent, ctx: ERC20Context) => {
ctx.meter.Counter("approval_cnt").add(1, { token: "stETH" });
ctx.eventLogger.emit("Approval", {
distinctId: event.args.owner,
spender: event.args.spender,
value: event.args.value.scaleDown(18),
token: "stETH",
});
})
.onBlockInterval(async (block, ctx) => {
const totalSupply = await ctx.contract.totalSupply();
ctx.meter.Gauge("total_supply").record(totalSupply.scaleDown(18), { token: "stETH" });
}, 1000); // Check every 1000 blocks
This documentation covers the fundamentals of the Ethereum/EVM module in the Sentio SDK. Consult the specific source files (base-processor.ts
, context.ts
, etc.) and generated contract types for more detailed API information.
Updated about 12 hours ago