Decode Transfers and Approvals from same contract.Use case: Index multiple event types from one contract.
Use @subsquid/evm-typegen to generate typed ABIs instead of manually finding
raw event signatures. This approach provides type safety and eliminates the
need to look up topic0 hashes. See the Custom ABI section
below.
Copy
import { evmPortalSource, evmDecoder, commonAbis,} from "@subsquid/pipes/evm";async function main() { const source = evmPortalSource({ portal: "https://portal.sqd.dev/datasets/ethereum-mainnet", }); const decoder = evmDecoder({ range: { from: 20000000, to: 20000100 }, contracts: ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"], events: { transfer: commonAbis.erc20.events.Transfer, approval: commonAbis.erc20.events.Approval, }, }); for await (const { data } of source.pipe(decoder)) { console.log({ transfers: data.transfer.length, approvals: data.approval.length, }); for (const t of data.transfer) { console.log( `Transfer: ${t.event.from} -> ${t.event.to}: ${t.event.value}` ); } for (const a of data.approval) { console.log( `Approval: ${a.event.owner} approved ${a.event.spender}: ${a.event.value}` ); } }}void main();
Filter events by indexed parameters to reduce data transfer and processing.Use case: Monitor specific wallet activity or track events with specific indexed values.
Copy
import { evmPortalSource, evmDecoder, commonAbis,} from "@subsquid/pipes/evm";async function main() { const SPECIFIC_SENDER = "0x87482e84503639466fad82d1dce97f800a410945"; const SPECIFIC_RECEIVER = "0x10b32a54eeb05d2c9cd1423b4ad90c3671a2ed5f"; const source = evmPortalSource({ portal: "https://portal.sqd.dev/datasets/ethereum-mainnet", }); const decoder = evmDecoder({ range: { from: 24171448, to: 24171449 }, events: { // Capture all Approval events (no filtering) approvals: commonAbis.erc20.events.Approval, // Filter Transfer events by indexed parameters transfers: { event: commonAbis.erc20.events.Transfer, params: { from: [SPECIFIC_SENDER], // Array of addresses (OR logic) to: SPECIFIC_RECEIVER, // Single address }, }, }, }); for await (const { data } of source.pipe(decoder)) { console.log({ approvals: data.approvals.length, transfers: data.transfers.length, // Only transfers matching params }); for (const transfer of data.transfers) { console.log({ from: transfer.event.from, to: transfer.event.to, value: transfer.event.value.toString(), block: transfer.block.number, }); } }}void main();
Array Values (OR Logic)Match any value in the array:
Copy
const MONITORED_ADDRESSES = [ "0x87482e84503639466fad82d1dce97f800a410945", "0x10b32a54eeb05d2c9cd1423b4ad90c3671a2ed5f", "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",];events: { transfers: { event: commonAbis.erc20.events.Transfer, params: { from: MONITORED_ADDRESSES, // Match any of these addresses }, },}
When to use parameter filtering:
Monitoring activity from specific addresses or contracts
Tracking events with specific indexed values (token IDs, pool addresses, etc.)
Reducing data transfer when you only need a subset of events
When to use client-side filtering:
Filtering by non-indexed parameters
Complex filtering logic that can’t be expressed in topic filters
When you need to filter based on multiple events’ relationships
Only indexed event parameters can be used in the params object. Non-indexed parameters cannot be filtered at the Portal API level and must be filtered client-side. Parameter matching for addresses is case-insensitive.