diff --git a/cmd/generate-bindings/bindings/sourcecre.ts.tpl b/cmd/generate-bindings/bindings/sourcecre.ts.tpl index 45c2fb72..1b1c87e0 100644 --- a/cmd/generate-bindings/bindings/sourcecre.ts.tpl +++ b/cmd/generate-bindings/bindings/sourcecre.ts.tpl @@ -1,5 +1,11 @@ // Code generated — DO NOT EDIT. -import { decodeFunctionResult, encodeFunctionData, zeroAddress } from 'viem' +import { + decodeEventLog, + decodeFunctionResult, + encodeEventTopics, + encodeFunctionData, + zeroAddress, +} from 'viem' import type { Address, Hex } from 'viem' import { bytesToHex, @@ -8,10 +14,38 @@ import { hexToBase64, LAST_FINALIZED_BLOCK_NUMBER, prepareReportRequest, + type EVMLog, type Runtime, } from '@chainlink/cre-sdk' +export interface DecodedLog extends Omit { data: T } + {{range $contract := .Contracts}} +{{/* Event types: Topics (indexed only) and Decoded (all fields) */}} +{{range $event := $contract.Events}} + +/** + * Filter params for {{.Original.Name}}. Only indexed fields can be used for filtering. + * Indexed string/bytes must be passed as keccak256 hash (Hex). + */ +export type {{.Normalized.Name}}Topics = { + {{- range .Normalized.Inputs}} + {{- if .Indexed}} + {{.Name}}?: {{bindtopictype .Type $.Structs}} + {{- end}} + {{- end}} +} + +/** + * Decoded {{.Original.Name}} event data. + */ +export type {{.Normalized.Name}}Decoded = { + {{- range .Normalized.Inputs}} + {{.Name}}: {{bindtype .Type $.Structs}} + {{- end}} +} +{{end}} + export const {{$contract.Type}}ABI = {{unescapeabi .InputABI}} as const export class {{$contract.Type}} { @@ -103,5 +137,84 @@ export class {{$contract.Type}} { }) .result() } +{{- range $event := $contract.Events}} + + /** + * Creates a log trigger for {{.Original.Name}} events. + * The returned trigger's adapt method decodes the raw log into {{.Normalized.Name}}Decoded, + * so the handler receives typed event data directly. + * When multiple filters are provided, topic values are merged with OR semantics (match any). + */ + logTrigger{{.Normalized.Name}}( + filters?: {{.Normalized.Name}}Topics[], + ) { + let topics: { values: string[] }[] + if (!filters || filters.length === 0) { + const encoded = encodeEventTopics({ + abi: {{$contract.Type}}ABI, + eventName: '{{.Original.Name}}' as const, + }) + topics = encoded.map((t) => ({ values: [hexToBase64(t)] })) + } else if (filters.length === 1) { + const f = filters[0] + const args = { + {{- range $param := .Normalized.Inputs}} + {{- if $param.Indexed}} + {{$param.Name}}: f.{{$param.Name}}, + {{- end}} + {{- end}} + } + const encoded = encodeEventTopics({ + abi: {{$contract.Type}}ABI, + eventName: '{{.Original.Name}}' as const, + args, + }) + topics = encoded.map((t) => ({ values: [hexToBase64(t)] })) + } else { + const allEncoded = filters.map((f) => { + const args = { + {{- range $param := .Normalized.Inputs}} + {{- if $param.Indexed}} + {{$param.Name}}: f.{{$param.Name}}, + {{- end}} + {{- end}} + } + return encodeEventTopics({ + abi: {{$contract.Type}}ABI, + eventName: '{{.Original.Name}}' as const, + args, + }) + }) + topics = allEncoded[0].map((_, i) => ({ + values: [...new Set(allEncoded.map((row) => hexToBase64(row[i])))], + })) + } + const baseTrigger = this.client.logTrigger({ + addresses: [hexToBase64(this.address)], + topics, + }) + const contract = this + return { + capabilityId: () => baseTrigger.capabilityId(), + method: () => baseTrigger.method(), + outputSchema: () => baseTrigger.outputSchema(), + configAsAny: () => baseTrigger.configAsAny(), + adapt: (rawOutput: EVMLog): DecodedLog<{{.Normalized.Name}}Decoded> => contract.decode{{.Normalized.Name}}(rawOutput), + } + } + + /** + * Decodes a log into {{.Normalized.Name}} data, preserving all log metadata. + */ + decode{{.Normalized.Name}}(log: EVMLog): DecodedLog<{{.Normalized.Name}}Decoded> { + const decoded = decodeEventLog({ + abi: {{$contract.Type}}ABI, + data: bytesToHex(log.data), + topics: log.topics.map((t) => bytesToHex(t)) as readonly Hex[], + }) + const { data: _, ...rest } = log + return { ...rest, data: decoded.args as unknown as {{.Normalized.Name}}Decoded } + } +{{- end}} } {{end}}