SDK Reference: EVM Log Trigger

The EVM Log Trigger fires when a specific log (event) is emitted by an onchain smart contract.

Creating the trigger

import { cre } from "@chainlink/cre-sdk"

// Create an EVMClient instance with a chain selector
const network = getNetwork({
  chainFamily: "evm",
  chainSelectorName: "ethereum-testnet-sepolia",
  isTestnet: true,
})

const evmClient = new cre.capabilities.EVMClient(network.chainSelector.selector)

// Basic log trigger for a contract address
const trigger = evmClient.logTrigger({
  addresses: ["0x123...abc"],
})

// With topics for event filtering
const trigger = evmClient.logTrigger({
  addresses: ["0x123...abc"],
  topics: [
    {
      values: [
        // Keccak256 hash of "Transfer(address,address,uint256)"
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
      ],
    },
  ],
})

Configuration

The logTrigger() method accepts a configuration object with the following fields:

Field
Type
Description
addressesstring[]A list of contract addresses to monitor (as hex strings, e.g., "0x..."). At least one address is required.
topicsTopicValues[]Optional. A fixed 4-element array to filter event topics. The first element contains event signatures, and the next three elements contain indexed argument values. An empty array element acts as a wildcard.
confidencestringOptional. The block confirmation level to monitor. Can be:
  • "CONFIDENCE_LEVEL_LATEST": The most recent block (fastest but least secure).
  • "CONFIDENCE_LEVEL_SAFE" (default): A block unlikely to be reorged but not yet irreversible.
  • "CONFIDENCE_LEVEL_FINALIZED": A block considered irreversible (safest, but requires waiting longer for finality).

TopicValues

The topics array uses a special format for filtering events:

FieldTypeDescription
valuesstring[]Array of possible values for a topic (hex strings).

Topic array structure:

  • topics[0]: Event signatures (keccak256 hash of the event name and indexed arg types). Must have at least one value.
  • topics[1]: Optional. Values for the first indexed argument. Can be empty (wildcard).
  • topics[2]: Optional. Values for the second indexed argument. Can be empty (wildcard).
  • topics[3]: Optional. Values for the third indexed argument. Can be empty (wildcard).

Example:

const trigger = evmClient.logTrigger({
  addresses: ["0x1234567890abcdef..."],
  topics: [
    // Topic 0: Event signature (Transfer event)
    {
      values: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
    },
    // Topic 1: From address (indexed parameter 1)
    {
      values: ["0x000000000000000000000000abcdef..."],
    },
    // Topic 2: Empty (wildcard for any "to" address)
    {
      values: [],
    },
  ],
  confidence: "CONFIDENCE_LEVEL_FINALIZED",
})

Payload

The payload passed to your callback function is an EVMLog object containing the log data.

FieldTypeDescription
addressUint8ArrayAddress of the contract that emitted the log (20 bytes).
topicsUint8Array[]Indexed log fields, including event signature (32 bytes each).
dataUint8ArrayABI-encoded non-indexed log data.
txHashUint8ArrayHash of the transaction (32 bytes).
blockHashUint8ArrayHash of the block (32 bytes).
blockNumberbigintThe block number containing the log (optional).
txIndexnumberIndex of the transaction within the block.
indexnumberIndex of the log within the block.
eventSigUint8ArrayKeccak256 hash of the event signature (32 bytes).
removedbooleanTrue if the log was removed during a reorg.

Working with log data:

import { bytesToHex } from "@chainlink/cre-sdk"

const onLogTrigger = (runtime: Runtime<Config>, log: EVMLog): string => {
  // Convert addresses and hashes to hex
  const contractAddress = bytesToHex(log.address)
  const txHash = bytesToHex(log.txHash)

  // Access topics (first topic is typically the event signature)
  const eventSignature = bytesToHex(log.topics[0])
  const firstIndexedParam = bytesToHex(log.topics[1])

  runtime.log(`Event from ${contractAddress}`)
  runtime.log(`Transaction: ${txHash}`)

  return "Success"
}

Callback Function

Your callback function for EVM log triggers must conform to this signature:

import { type Runtime, type EVMLog } from "@chainlink/cre-sdk"

const onLogTrigger = (runtime: Runtime<Config>, log: EVMLog): YourReturnType => {
  // Your workflow logic here
  return result
}

Parameters:

  • runtime: The runtime object used to invoke capabilities and access configuration
  • log: The EVM log payload containing all event data

Example:

import { bytesToHex, type Runtime, type EVMLog } from "@chainlink/cre-sdk"

type Config = {
  contractAddress: string
}

const onLogTrigger = (runtime: Runtime<Config>, log: EVMLog): string => {
  const topics = log.topics

  if (topics.length < 3) {
    runtime.log(`Log payload does not contain enough topics: ${topics.length}`)
    throw new Error("Insufficient topics in log")
  }

  // Extract indexed parameters from topics
  // topics[0] is the event signature
  // topics[1], topics[2], etc. are indexed event parameters

  const eventSig = bytesToHex(topics[0])
  runtime.log(`Event signature: ${eventSig}`)

  // Access block information
  runtime.log(`Block number: ${log.blockNumber}`)
  runtime.log(`Transaction index: ${log.txIndex}`)

  return "Event processed successfully"
}

Complete Example

import { cre, bytesToHex, getNetwork, Runner, type Runtime, type EVMLog } from "@chainlink/cre-sdk"

type Config = {
  chainSelectorName: string
  contractAddress: string
}

const onLogTrigger = (runtime: Runtime<Config>, log: EVMLog): string => {
  const topics = log.topics

  if (topics.length < 2) {
    throw new Error("Log missing required topics")
  }

  runtime.log(`Processing log from ${bytesToHex(log.address)}`)
  runtime.log(`Event signature: ${bytesToHex(topics[0])}`)

  // Decode the log data based on your event ABI
  // For this example, we just log the raw data
  runtime.log(`Data length: ${log.data.length} bytes`)

  return "Log processed"
}

const initWorkflow = (config: Config) => {
  const network = getNetwork({
    chainFamily: "evm",
    chainSelectorName: config.chainSelectorName,
    isTestnet: true,
  })

  if (!network) {
    throw new Error(`Network not found: ${config.chainSelectorName}`)
  }

  const evmClient = new cre.capabilities.EVMClient(network.chainSelector.selector)

  return [
    cre.handler(
      evmClient.logTrigger({
        addresses: [config.contractAddress],
      }),
      onLogTrigger
    ),
  ]
}

export async function main() {
  const runner = await Runner.newRunner<Config>()
  await runner.run(initWorkflow)
}

main()

Decoding Log Data

For production workflows, you'll typically want to decode the log data based on the event's ABI. The TypeScript SDK uses viem for ABI encoding/decoding:

import { bytesToHex, type Runtime, type EVMLog } from "@chainlink/cre-sdk"

const onLogTrigger = (runtime: Runtime<Config>, log: EVMLog): string => {
  const topics = log.topics

  // topics[0] is the event signature
  // topics[1], topics[2], topics[3] are indexed event parameters

  // Example: Extract an address from topic 1 (last 20 bytes of 32-byte topic)
  const addressFromTopic = bytesToHex(topics[1].slice(12))
  runtime.log(`Address parameter: ${addressFromTopic}`)

  // For non-indexed parameters, you would decode log.data according to the ABI
  // The demo workflow uses viem for contract interactions and ABI handling

  return "Log decoded"
}

Get the latest Chainlink content straight to your inbox.