Fuel

Fuel is a high-performance modular execution layer with its own virtual machine (FuelVM) and Sway programming language.

Core Concepts

Processors

Fuel integration primarily uses one processor type:

  1. FuelProcessor<TContract extends Contract>: Binds to a specific Fuel contract ID (address) and requires its JSON ABI. It processes transactions involving this contract.
    • Typical Use Case: Monitoring a specific Sway contract (dApp, token, etc.).
    • Binding: FuelProcessor.bind({ address: 'fuel1...', abi: MyContractAbi, chainId: FuelNetwork.TESTNET, startBlock: 10000n })
    • The ABI (MyContractAbi) is typically imported from the JSON file generated by the forc build command.
    • The TContract type parameter can be inferred or explicitly set using the contract type generated by fuels typegen for enhanced type safety.

Handlers

Handlers are defined within the FuelProcessor to react to specific activities:

  • onTransaction(handler(transaction, ctx), fetchConfig?): Triggered for every transaction that interacts with the bound contract ID. The transaction object contains decoded information about the transaction, including operations, inputs, outputs, and receipts.
  • onLog<T>(handler(log, ctx), logIdFilter): Triggered when a specific log (identified by its ID derived from the ABI) is emitted within a transaction processed by the contract. The log object contains the decoded log data (log.data).
  • onTransfer(handler(transfer, ctx), filter): Triggered when a Transfer or TransferOut receipt matches the provided filter criteria (contract ID, asset ID, sender (from), recipient (to)). Useful for tracking asset movements involving the bound contract.
  • onBlockInterval(handler(block, ctx), interval?, backfillInterval?): Processes blocks based on block height intervals.
  • onTimeInterval(handler(block, ctx), intervalMinutes?, backfillIntervalMinutes?): Processes blocks based on time intervals.
  • onCall(handler(call, ctx), nameFilter) (Currently Internal/Experimental): Aims to trigger on specific function calls within a transaction, similar to EVM onTrace. Requires the contract ABI.

Context (ctx)

Handler functions receive a FuelContractContext<TContract> object providing:

  • Chain information: chainId.
  • Contract details: contractAddress, contractName, contract (a fuels-ts Contract instance for on-chain reads).
  • Block/Transaction details: block, transaction, timestamp.
  • Helper methods: Access to the fuels-ts provider (ctx.provider).
  • Standard SDK outputs: ctx.meter.Counter('...'), ctx.eventLogger.emit('...').

Fetch Configuration (fetchConfig)

The onTransaction handler accepts an optional fetchConfig (currently focused on call filters for the internal onCall).

Getting Started Example (Hypothetical Counter Contract)

import { FuelProcessor, FuelContractContext, FuelNetwork } from "@sentio/sdk/fuel";
import { CounterContractAbi } from "./abis/counter-contract-abi"; // ABI from forc build
import { CounterContract } from "./types/CounterContract"; // Types from fuels typegen
import { FuelLog } from "@sentio/sdk/fuel";

// Define the structure of the CountIncremented log based on the ABI
interface CountIncrementedLog {
  counter: bigint;
  amount: bigint;
}

const CONTRACT_ID = "fuel1qvhrq08r886ua2u6z0j3a5j0z3z5f7k6f7z3q5"; // Replace with your contract ID

FuelProcessor.bind({
  address: CONTRACT_ID,
  abi: CounterContractAbi,
  chainId: FuelNetwork.TESTNET, // Or FuelNetwork.BETA_5
  startBlock: 150000n,
})
  .onTransaction(async (tx, ctx: FuelContractContext<CounterContract>) => {
    // Process every transaction interacting with the contract
    ctx.meter.Counter("transaction_cnt").add(1);

    // Example: Check transaction status
    if (tx.status === "success") {
      ctx.meter.Counter("successful_txns").add(1);
    } else {
      ctx.meter.Counter("failed_txns").add(1);
    }
  })
  .onLog<CountIncrementedLog>(async (log, ctx: FuelContractContext<CounterContract>) => {
    // Process CountIncremented logs
    ctx.eventLogger.emit("CountIncremented", {
      distinctId: log.sender?.address, // Use sender if available in log context
      newCounterValue: log.data.counter.toString(),
      incrementAmount: log.data.amount.toString(),
    });
    ctx.meter.Gauge("latest_cnt").record(log.data.counter);
    ctx.meter.Counter("total_incremented").add(log.data.amount);
  }, "0x0000000000000001") // Replace with the actual Log ID for CountIncremented
  .onBlockInterval(async (block, ctx: FuelContractContext<CounterContract>) => {
    // Example: Reading contract state periodically
    try {
      const countResult = await ctx.contract.functions.count().get();
      const currentCount = countResult.value.toBigInt();
      ctx.meter.Gauge("onchain_count_snapshot").record(currentCount);
    } catch (error) {
      console.error("Failed to read count from contract:", error);
    }
  }, 100); // Check every 100 blocks

This overview covers the Fuel integration within the Sentio SDK. Refer to the fuel-processor.ts, context.ts, and the fuels-ts documentation for more detailed information.