Skip to content

Subsquid Processor:

Imports

import {
  BlockHeader,
  DataHandlerContext,
  EvmBatchProcessor,
  EvmBatchProcessorFields,
  Log as _Log,
  Transaction as _Transaction,
} from '@subsquid/evm-processor';
import * as univ2Abi from './abi/univ2';
import { ProtocolType, validateEnv } from '@absinthe/common';

What's happening?

  • Subsquid EVM Processor: Imports types and classes for block, log, transaction, and the batch processor itself.
  • Uniswap V2 ABI: Imports the ABI definitions for Uniswap V2 events (Swap, Transfer).
  • Absinthe Common: Imports protocol type enum and config/environment validation.

Environment & Config Validation

const env = validateEnv();
  • Loads and validates your environment variables and config (including ABS_CONFIG).
  • Ensures all required fields are present and correctly typed.

Select Uniswap V2 Protocol Config

const uniswapV2DexProtocol = env.dexProtocols.find(dexProtocol => {
  return dexProtocol.type === ProtocolType.UNISWAP_V2;
});
 
if (!uniswapV2DexProtocol) {
  throw new Error('Uniswap V2 protocol not found');
}
  • Finds the Uniswap V2 protocol config from your validated config.
  • Throws an error if not found (prevents silent misconfiguration).

Prepare Pool Addresses and Block Range

const contractAddresses = uniswapV2DexProtocol.protocols.map(protocol => protocol.contractAddress);
const earliestFromBlock = Math.min(...uniswapV2DexProtocol.protocols.map(protocol => protocol.fromBlock));
  • contractAddresses: Array of all Uniswap V2 pool contract addresses to index.
  • earliestFromBlock: Finds the lowest fromBlock among all pools—this is where indexing starts.

Configure the EVM Batch Processor

export const processor = new EvmBatchProcessor()
  .setGateway(uniswapV2DexProtocol.gatewayUrl)
  .setRpcEndpoint(uniswapV2DexProtocol.rpcUrl)
  .setBlockRange({
    from: earliestFromBlock,
    ...(uniswapV2DexProtocol.toBlock !== 0 ? { to: Number(uniswapV2DexProtocol.toBlock) } : {}),
  })
  .setFinalityConfirmation(75)
  .addLog({
    address: contractAddresses,
    topic0: [univ2Abi.events.Transfer.topic, univ2Abi.events.Swap.topic],
    transaction: true,
  })
  .setFields({
    log: {
      transactionHash: true,
    },
    transaction: {
      to: true,
      from: true,
      gas: true,
      gasPrice: true,
      gasUsed: true,
    },
  });

What does each part do?

  • setGateway: Sets the Subsquid gateway URL for fast block data access (from config).

  • setRpcEndpoint: Sets the fallback RPC endpoint for on-demand data (from config).

  • setBlockRange:

    • from: Start indexing from the earliest pool's fromBlock.
    • to: If toBlock is set in config, stop at that block; otherwise, keep syncing to latest.
  • setFinalityConfirmation(75): Waits for 75 block confirmations before processing (prevents reorg issues).

  • addLog:

    • address: Listens for logs from all specified pool contract addresses.
    • topic0: Filters for only Transfer and Swap events (Uniswap V2 core events).
    • transaction: true: Also fetches the full transaction for each log (needed for gas, sender, etc).
  • setFields:

    • log: Only extracts the transactionHash field from logs (for linking events to transactions).
    • transaction: Only extracts the fields you need (to, from, gas, gasPrice, gasUsed) for analytics and cost calculations.

Type Exports for Strong Typing

export type Fields = EvmBatchProcessorFields<typeof processor>;
export type Block = BlockHeader<Fields>;
export type Log = _Log<Fields>;
export type Transaction = _Transaction<Fields>;
export type ProcessorContext<Store> = DataHandlerContext<Store, Fields>;
  • Fields: Type representing the selected fields for logs and transactions.
  • Block, Log, Transaction: Strongly-typed objects for each block, log, and transaction, parameterized by your field selection.
  • ProcessorContext: Context object for event handlers, includes store access and selected fields.

How Does This All Work Together?

  1. Config-driven: All pool addresses, chain info, and block ranges come from your config—no hardcoding.

  2. Efficient event filtering: Only listens for relevant events (Swap, Transfer) from the pools you care about.

  3. Lean data extraction: Only pulls the fields you need for analytics, keeping processing fast and memory usage low.

  4. Strong typing: All downstream code (event handlers, analytics, API integration) can use these types for safety and autocompletion.

  5. Ready for extension:

    • Add more pools by editing your config, not your code.
    • Add more event types by updating the topic0 array.