workflows

package module
v0.0.17 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2026 License: MIT Imports: 23 Imported by: 7

README

CREC Workflow Utils

Common utilities for building CRE (Chainlink Runtime Environment) event watcher workflows. Used as part of the CREC (CRE Connect) ecosystem.

Overview

This package provides shared functionality for CRE workflow extensions that implement event watcher workflows:

  • Configuration parsing — Parse YAML/JSON workflow configs
  • Event processing — Decode EVM log events, build verifiable events, post to Courier
  • Workflow initialization — Wire up EVM log triggers with handlers
  • Testing utilities — Mock runtime for unit testing handlers

Installation

go get github.com/smartcontractkit/crec-workflow-utils

Usage

Workflow Entry Point
//go:build wasip1

package main

import (
    "log/slog"
    "github.com/smartcontractkit/cre-sdk-go/cre"
    "github.com/smartcontractkit/cre-sdk-go/cre/wasm"
    workflows "github.com/smartcontractkit/crec-workflow-utils"
)

func main() {
    r := wasm.NewRunner(workflows.ParseWorkflowConfig)
    r.Run(func(cfg *workflows.Config, _ *slog.Logger, _ cre.SecretsProvider) (cre.Workflow[*workflows.Config], error) {
        return workflows.InitEventListenerWorkflow(cfg, OnLog)
    })
}
Log Handler
package handler

import (
    "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm"
    "github.com/smartcontractkit/cre-sdk-go/cre"
    workflows "github.com/smartcontractkit/crec-workflow-utils"
)

func OnLog(cfg *workflows.Config, rt cre.Runtime, payload *evm.Log) (string, error) {
    evmEvent, err := workflows.BuildEVMEventFromLog(rt, cfg, payload)
    if err != nil {
        return "", err
    }

    abiJSON, err := workflows.GetContractABI(cfg, cfg.DetectEventTriggerConfig.ContractName)
    if err != nil {
        return "", err
    }
    eventName, err := workflows.GetEventNameFromLog(cfg, payload, abiJSON)
    if err != nil {
        return "", err
    }

    verifiableEvent, err := workflows.BuildVerifiableEventForEVMEvent(
        cfg, evmEvent, cfg.Service, eventName, nil,
    )
    if err != nil {
        return "", err
    }

    return workflows.SignAndPostVerifiableEvent(cfg, rt, verifiableEvent)
}
Configuration

Workflow config accepts YAML or JSON (YAML is tried first; JSON is used if YAML parsing fails). Required fields:

Field Description
chainSelector Chain selector (uint64 string) for the target EVM chain
courierURL Base URL of the event ingestion service
watcherID Watcher identifier
detectEventTriggerConfig.contractName Contract name in contractReaderConfig.contracts
detectEventTriggerConfig.contractAddress Contract address to watch
detectEventTriggerConfig.contractEventNames List of event names to listen for
detectEventTriggerConfig.contractReaderConfig.contracts Map of contract name → ABI

Example config structure:

network: "sepolia"
chainSelector: "16015286601757825753"
courierURL: "https://courier.example.com"
watcherID: "my-watcher-id"
detectEventTriggerConfig:
  contractName: "MyContract"
  contractAddress: "0x..."
  contractEventNames: ["Transfer", "Approval"]
  contractReaderConfig:
    contracts:
      MyContract:
        contractABI: '[{"type":"event","name":"Transfer",...}]'

Testing

go test ./...

License

MIT

Documentation

Overview

Package workflows provides common utilities for CRE (Chainlink Runtime Environment) event watcher workflow development. It is used by CREC workflow extensions that listen for EVM contract events and post verifiable events to CREC.

Key Features

Usage

Create a workflow entry point:

r := wasm.NewRunner(workflows.ParseWorkflowConfig)
r.Run(func(cfg *workflows.Config, _ *slog.Logger, _ cre.SecretsProvider) (cre.Workflow[*workflows.Config], error) {
    return workflows.InitEventListenerWorkflow(cfg, OnLog)
})

Implement a LogHandler that decodes the EVM log, builds a verifiable event, and posts it:

func OnLog(cfg *workflows.Config, rt cre.Runtime, payload *evm.Log) (string, error) {
    evmEvent, err := workflows.BuildEVMEventFromLog(rt, cfg, payload)
    if err != nil { return "", err }
    eventName, err := workflows.GetEventNameFromLog(cfg, payload, abiJSON)
    if err != nil { return "", err }
    ve, err := workflows.BuildVerifiableEventForEVMEvent(cfg, evmEvent, cfg.Service, eventName, nil)
    if err != nil { return "", err }
    return workflows.SignAndPostVerifiableEvent(cfg, rt, ve)
}

Error Handling

Errors from ParseWorkflowConfig indicate invalid YAML/JSON or missing required fields (e.g. chainSelector). GetContractABI returns an error if the contract is not found in config. SignAndPostVerifiableEvent returns errors from consensus generation or CREC API failures.

Index

Constants

This section is empty.

Variables

View Source
var CurrencyCodes = map[uint8]string{}/* 179 elements not displayed */

CurrencyCodes maps ISO 4217 numeric currency IDs to their three-letter codes.

View Source
var ErrNilVerifiableEvent = errors.New("verifiable event cannot be nil")

ErrNilVerifiableEvent is returned when a nil VerifiableEvent is passed to EncodeVerifiableEvent.

Functions

func BuildEVMEventFromLog

func BuildEVMEventFromLog(rt cre.Runtime, cfg *Config, payload *evm.Log) (*models.EVMEvent, error)

BuildEVMEventFromLog constructs an EVMEvent from the given evm.Log payload, decoding parameters using the contract ABI specified in cfg.

func BuildVerifiableEventForEVMEvent

func BuildVerifiableEventForEVMEvent(
	cfg *Config, ev *models.EVMEvent, service *string, name string, data *map[string]interface{},
) (*models.VerifiableEvent, error)

BuildVerifiableEventForEVMEvent constructs a VerifiableEvent for the given EVMEvent, with the specified service (optional, can be nil for workflows not scoped to a service), name, and additional data.

func CheckResponse

func CheckResponse(resp *httpcap.Response) (*httpcap.Response, error)

CheckResponse validates the httpcap response and returns it unchanged if acceptable.

func ComputeEventHash

func ComputeEventHash(encoded string) (common.Hash, error)

ComputeEventHash returns the Keccak256 hash of the encoded string as a common.Hash. The encoded string is typically the base64 output from EncodeVerifiableEvent.

func ConfidenceLevelFromString added in v0.0.16

func ConfidenceLevelFromString(s string) evm.ConfidenceLevel

func CursorFromPB

func CursorFromPB(blockNumber *pb.BigInt, logIndex uint64, txHash string) string

CursorFromPB builds a "block-logIndex-txHash" cursor string from pb.BigInt block-number, a log-index, and an optional tx-hash. If txHash is empty, "0x" is used to match existing consumers.

func DecodeEventParams

func DecodeEventParams(abiJSON, eventName string, log *evm.Log) (map[string]any, error)

DecodeEventParams decodes an EVM log's topics/data into a named parameter map, using the provided ABI JSON and event-name. It returns parameters with snake_case keys and values sanitised via SanitiseJSON. It always returns a params map (possibly empty) even when an error occurs parsing ABI or event.

func DecodeVerifiableEvent

func DecodeVerifiableEvent(encoded string) (*models.VerifiableEvent, error)

DecodeVerifiableEvent decodes a base64-encoded JSON string into a VerifiableEvent. It returns an error if the input is not valid base64 or if JSON unmarshaling fails.

func EncodeVerifiableEvent

func EncodeVerifiableEvent(ve *models.VerifiableEvent) (string, error)

EncodeVerifiableEvent marshals the VerifiableEvent to JSON and encodes it as base64. It returns ErrNilVerifiableEvent if ve is nil.

func EnsureChainSelector

func EnsureChainSelector(cfg *Config, fallback string) string

EnsureChainSelector returns cfg.ChainSelector if set, otherwise returns the provided fallback.

func GetBlockTimestamp

func GetBlockTimestamp(rt cre.Runtime, chainSelector string, blockNumber *pb.BigInt) uint64

GetBlockTimestamp fetches the block timestamp via EVM HeaderByNumber; falls back to current UTC time if unavailable.

func GetContractABI

func GetContractABI(cfg *Config, contractName string) (string, error)

GetContractABI returns the ABI string for the specified contract-name. Only the canonical "contractABI" field is supported.

func GetCurrencyCode

func GetCurrencyCode(currencyId uint8) string

GetCurrencyCode returns the three-letter ISO 4217 code for the given numeric currency ID, or "Unknown" if the ID is not found in CurrencyCodes.

func GetEventNameFromLog

func GetEventNameFromLog(cfg *Config, payload *evm.Log, abiJSON string) (string, error)

GetEventNameFromLog identifies the event name matching the log's topic hash by checking against the list of configured ContractEventNames and the ABI.

func GetEventSignature

func GetEventSignature(cfg *Config, eventName string) string

GetEventSignature returns the ABI event signature (topic hash) for the given event name from the workflow config. It returns an empty string if the contract ABI cannot be loaded, parsed, or if the event is not found.

func InitEventListenerWorkflow

func InitEventListenerWorkflow(cfg *Config, handler LogHandler) (cre.Workflow[*Config], error)

InitEventListenerWorkflow wires the standard EVM Log trigger for event-listener workflows and attaches the provided handler. It resolves the event signatures from the ABI for all events in ContractEventNames and uses cfg.ChainSelector (required in the config).

func MustEvent

func MustEvent(abiJSON, eventName string) gethAbi.Event

MustEvent returns the ABI event by name (panics on error).

func NewEVMLogFilter

func NewEVMLogFilter(contractAddr string, eventSigHashes [][]byte, confidence evm.ConfidenceLevel) *evm.FilterLogTriggerRequest

NewEVMLogFilter returns a FilterLogTriggerRequest for a single-address subscription for one or more events. Includes wildcard slots for up to 3 indexed parameters. Confidence is chosen by the caller.

func PBToUint64

func PBToUint64(b *pb.BigInt) uint64

PBToUint64 converts a protobuf BigInt (unsigned) to a uint64.

func ParseChainSelector

func ParseChainSelector(chainSelectorStr *string) (*uint64, error)

ParseChainSelector validates and parses a chain_selector string parameter to uint64. Returns the parsed value or an error if the string is invalid.

func PrepareTestingRuntime

func PrepareTestingRuntime(t *testing.T) *testutils.TestRuntime

PrepareTestingRuntime creates a test runtime with standard secrets configured.

func Retry

func Retry[T any](logger *slog.Logger, name string, rc *RetryConfig, fn func() (T, error)) (T, error)

Retry is a generic helper to retry operations up to a fixed number of times. It uses an exponential backoff strategy; the starting delay comes from rc (see RetryConfig). If the operation fails after all attempts are exhausted, it returns the last error wrapped with context. It stops retrying immediately if the function returns an error wrapped with StopRetry.

func SanitiseJSON

func SanitiseJSON(v any) any

SanitiseJSON transforms keys to snake_case and encodes []byte or base64-like strings as 0x-hex. big.Int is rendered as string to avoid 64-bit precision loss. Additionally, fixed-size byte arrays (e.g. [32]byte) are encoded as 0x-hex. For JSON-derived []interface{} representing byte arrays (length 20 or 32), encode as 0x-hex as well.

func SignAndPostVerifiableEvent

func SignAndPostVerifiableEvent(cfg *Config, rt cre.Runtime, ve *models.VerifiableEvent) (string, error)

SignAndPostVerifiableEvent performs identical-consensus report generation and posts the signed event to the Courier /onchain-watcher-events endpoint. It returns the base64 verifiable event.

func StopRetry

func StopRetry(err error) error

StopRetry wraps an error to indicate that the retry loop should stop.

func TxHashFromLog

func TxHashFromLog(l *evm.Log) string

TxHashFromLog returns the 0x-hex transaction hash from a log if present; otherwise "0x".

Types

type Config

type Config struct {
	Network       string  `yaml:"network"                json:"network"`
	CourierURL    string  `yaml:"courierURL"             json:"courierURL"`
	Service       *string `yaml:"service,omitempty"      json:"service,omitempty"`
	ApiKeySecret  string  `yaml:"apiKeySecret,omitempty" json:"apiKeySecret,omitempty"`
	ChainSelector string  `yaml:"chainSelector"          json:"chainSelector"`
	WatcherID     string  `yaml:"watcherID"              json:"watcherID"`
	WorkflowName  string  `yaml:"workflowName"           json:"workflowName"`
	// ConfidenceLevel is the EVM log trigger confidence: "finalized", "safe", or "latest".
	// Defaults to "latest" when omitted after [ParseWorkflowConfig].
	ConfidenceLevel string `yaml:"confidenceLevel,omitempty" json:"confidenceLevel,omitempty"`

	DetectEventTriggerConfig DetectEventTriggerConfig `yaml:"detectEventTriggerConfig" json:"detectEventTriggerConfig"`
}

Config is the shared configuration for all event-detection workflows (YAML or JSON). It mirrors the structure produced by our existing config.tmpl files and remains compatible with server-side gomplate rendering used during e2e runs.

func ParseWorkflowConfig

func ParseWorkflowConfig(b []byte) (*Config, error)

ParseWorkflowConfig accepts YAML or JSON and returns a Config. chainSelector is expected to be provided by the workflow config.

type ContractDef

type ContractDef struct {
	ContractABI any `yaml:"contractABI" json:"contractABI"`
}

ContractDef holds the ABI string in "contractABI".

type ContractReaderConfig

type ContractReaderConfig struct {
	Contracts map[string]ContractDef `yaml:"contracts" json:"contracts"`
}

ContractReaderConfig contains the contracts map. We only need contractABI for this workflow.

type DetectEventTriggerConfig

type DetectEventTriggerConfig struct {
	ContractName         string               `yaml:"contractName"      json:"contractName"`
	ContractAddress      string               `yaml:"contractAddress"   json:"contractAddress"`
	ContractEventNames   []string             `yaml:"contractEventNames"          json:"contractEventNames"`
	ContractReaderConfig ContractReaderConfig `yaml:"contractReaderConfig"        json:"contractReaderConfig"`
}

DetectEventTriggerConfig matches the template data for the log trigger.

type Fixed2

type Fixed2 float64

Fixed2 represents a fixed-point decimal number with 2 decimal places, stored as a string.

func (Fixed2) MarshalJSON

func (f Fixed2) MarshalJSON() ([]byte, error)

MarshalJSON marshals the Fixed2 value to a JSON string.

func (*Fixed2) UnmarshalJSON

func (f *Fixed2) UnmarshalJSON(data []byte) error

UnmarshalJSON unmarshals the Fixed2 value from a JSON string.

type LogHandler

type LogHandler func(*Config, cre.Runtime, *evm.Log) (string, error)

LogHandler is the function signature implemented by each event-listener workflow's per-project handler (e.g. OnLog, OnCoordinatorLog). It processes an EVM log and returns a base64-encoded verifiable event (or empty string) and an error.

type OffChainReferenceData

type OffChainReferenceData struct {
	Source OffChainReferenceDataSource `json:"source"` // The source of the off-chain reference data.
	Data   map[string]any              `json:"data"`   // The data returned from the source call.
}

OffChainReferenceData is a structured set of fields that can be used for reference data from off-chain sources.

func GetCurrencyCodeAsOffChainReferenceData

func GetCurrencyCodeAsOffChainReferenceData(currencyId uint8) OffChainReferenceData

GetCurrencyCodeAsOffChainReferenceData returns an OffChainReferenceData struct containing the currency code for the given numeric ID, or "Unknown" if the ID is not in CurrencyCodes.

type OffChainReferenceDataSource

type OffChainReferenceDataSource struct {
	Type       string `json:"type"`       // The type of the off-chain reference data.
	Identifier string `json:"identifier"` // Typically the URN for the off-chain source or identifier of the standardised data format.
}

OffChainReferenceDataSource is the source of the off-chain reference data.

type OnChainReferenceData

type OnChainReferenceData struct {
	Source OnChainReferenceDataSource `json:"source"` // The source of the on-chain reference data.
	Data   map[string]any             `json:"data"`   // The data returned from the source call.
}

OnChainReferenceData is a structured set of fields that can be used for reference data from on-chain sources.

type OnChainReferenceDataSource

type OnChainReferenceDataSource struct {
	ContractAddress           string `json:"contract_address"`            // The contract address to call.
	ContractFunctionSignature string `json:"contract_function_signature"` // The function signature to call.
	CallData                  string `json:"call_data"`                   // The call data to pass to the function.
	Block                     string `json:"block"`                       // The block number to call the function at.
}

OnChainReferenceDataSource is the source of the on-chain reference data.

type PaymentCallback

type PaymentCallback struct {
	ContractAddress   string `json:"contractAddress,omitempty"`   // The contract address to call. If empty, uses ApplicationAddr from PaymentRequest.
	FunctionName      string `json:"functionName,omitempty"`      // The name of the function to call.
	FunctionSignature string `json:"functionSignature,omitempty"` // The ABI function signature to call (e.g., "fulfillPayment(bytes32,uint256)")
}

PaymentCallback specifies how to call back to the application after payment processing.

type PaymentRequest

type PaymentRequest struct {
	ApplicationType string           `json:"applicationType"`          // The type of the application that generates the payment request.
	ApplicationAddr string           `json:"applicationAddr"`          // The application that generates the payment request.
	E2EID           string           `json:"e2eId"`                    // The E2E ID of the payment request.
	Sender          string           `json:"sender"`                   // The sender of the payment request.
	Receiver        string           `json:"receiver"`                 // The receiver of the payment.
	Currency        string           `json:"currency"`                 // The currency of the payment.
	Amount          Fixed2           `json:"amount"`                   // The amount of the payment in fixed-point decimal format with 2 decimal places.
	Expiration      *int64           `json:"expiration,omitempty"`     // The expiration time of the payment request in seconds since epoch.
	CustomCallback  *PaymentCallback `json:"customCallback,omitempty"` // The custom callback to be used for the payment request.
}

PaymentRequest contains the details needed for an off-chain payment request.

type RawMessageType

type RawMessageType string

RawMessageType identifies the kind of payload in a TypeAndValue structure.

const (
	// RawMessageTypePaymentRequest indicates the value is a payment request payload.
	RawMessageTypePaymentRequest RawMessageType = "payment_request"
	// RawMessageTypeReferenceData indicates the value is reference data from on-chain or off-chain sources.
	RawMessageTypeReferenceData RawMessageType = "reference_data"
)
const (
	// RawMessageTypeMap indicates the value is a generic map structure.
	RawMessageTypeMap RawMessageType = "map"
)

type ReferenceData

type ReferenceData struct {
	OnChain  []OnChainReferenceData  `json:"on_chain,omitempty"`  // The on-chain reference data.
	OffChain []OffChainReferenceData `json:"off_chain,omitempty"` // The off-chain reference data.
	Requests []TypeAndValue          `json:"requests,omitempty"`  // The requests to be forwarded to the off-chain applications.
}

ReferenceData is a structured set of fields that can be used for reference data from on-chain and off-chain sources as well as requests to be forwarded to off-chain applications.

func GetReferenceDataFromVerifiableEvent

func GetReferenceDataFromVerifiableEvent(verifiableEvent models.VerifiableEvent) (*ReferenceData, error)

GetReferenceDataFromVerifiableEvent extracts the ReferenceData from the verifiable event data field if it exists.

type RetryConfig added in v0.0.16

type RetryConfig struct {
	MaxAttempts  int    `yaml:"maxAttempts,omitempty" json:"maxAttempts,omitempty"`
	InitialDelay string `yaml:"initialDelay,omitempty" json:"initialDelay,omitempty"`
}

RetryConfig controls how many times Retry runs fn and how long to wait between retries.

Management and defaults:

  • Pass nil for rc to use the library defaults (3 attempts, initial delay 5s).
  • Pass a non-nil value to override; fields that are "unset" fall back to the same defaults: MaxAttempts less than or equal to 0 means 3; InitialDelay "" or a string that time.ParseDuration rejects means 5s.
  • InitialDelay must be a duration string accepted by ParseDuration (e.g. "5s", "1s", "500ms").

Workflows that load retry settings from their own YAML/JSON can define a struct that embeds or duplicates these fields with the same tags, then pass the populated *RetryConfig into Retry. Exponential backoff doubles the delay after each failed attempt (after the first).

type TypeAndValue

type TypeAndValue struct {
	Type  RawMessageType  `json:"type"`
	Value json.RawMessage `json:"value"`
}

TypeAndValue is a type that holds a type and a value.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL