diff --git a/.github/workflows/template-check.yml b/.github/workflows/template-check.yml new file mode 100644 index 00000000..d4f5ec73 --- /dev/null +++ b/.github/workflows/template-check.yml @@ -0,0 +1,85 @@ +name: Template Check + +on: + pull_request: + types: [opened, synchronize, reopened] + +concurrency: + group: template-compat-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +# Blocking merge gate for templates changed in the PR. +# +# Checks performed per template: +# TypeScript: YAML validation, bun install, typecheck, cre-compile to WASM +# Go: YAML validation, go mod verify, go vet, go build (GOOS=wasip1 GOARCH=wasm) +# +# The SDK version used is captured from the installed package (TS) or go.mod (Go) +# and printed in the Actions log when a check fails. + +permissions: + contents: read + +jobs: + template-check: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash {0} + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 # full history needed for git diff against base branch + + # Go is required for Go template builds (GOOS=wasip1 GOARCH=wasm). + # GOTOOLCHAIN=auto lets Go download the exact toolchain declared in go.mod + # (e.g. "toolchain go1.24.10") if it differs from the installed version. + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 'stable' + cache: false # cache managed explicitly below + env: + GOTOOLCHAIN: auto + + # Node + npm are required for TypeScript template checks. + - name: Setup Node + uses: actions/setup-node@v6 + with: + node-version: '24' + + # cre-compile is shipped with a bun shebang; npx invokes it as /usr/bin/env bun. + # Match local dev machines where Bun is installed alongside Node. + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + + # Cache Go module downloads. Keyed on all go.sum files so any new module + # version busts the cache while unchanged templates stay fast. + - name: Cache Go modules + uses: actions/cache@v5 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-templates-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go-templates- + + # Cache npm package downloads. Multiple templates sharing the same + # @chainlink/cre-sdk version (e.g. ^1.5.0) will hit the cache after + # the first install, avoiding repeated network downloads. + - name: Cache npm + uses: actions/cache@v5 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-templates-${{ hashFiles('**/package.json') }} + restore-keys: ${{ runner.os }}-npm-templates- + + - name: Run template checks + env: + BASE_REF: ${{ github.base_ref }} + RESULTS_FILE: /tmp/template-results.json + GOTOOLCHAIN: auto + run: ./scripts/check-templates.sh diff --git a/building-blocks/indexer-block-trigger/block-trigger-ts/workflow/package.json b/building-blocks/indexer-block-trigger/block-trigger-ts/workflow/package.json index d830e282..62b93d62 100644 --- a/building-blocks/indexer-block-trigger/block-trigger-ts/workflow/package.json +++ b/building-blocks/indexer-block-trigger/block-trigger-ts/workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0" + "@chainlink/cre-sdk": "^1.6.0" }, "devDependencies": { "typescript": "5.9.3" diff --git a/building-blocks/indexer-data-fetch/indexer-fetch-ts/workflow/package.json b/building-blocks/indexer-data-fetch/indexer-fetch-ts/workflow/package.json index 3d05b770..c1419963 100644 --- a/building-blocks/indexer-data-fetch/indexer-fetch-ts/workflow/package.json +++ b/building-blocks/indexer-data-fetch/indexer-fetch-ts/workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0" + "@chainlink/cre-sdk": "^1.6.0" }, "devDependencies": { "typescript": "5.9.3" diff --git a/building-blocks/kv-store/kv-store-ts/my-workflow/package.json b/building-blocks/kv-store/kv-store-ts/my-workflow/package.json index b1f73c75..7f5b27d0 100644 --- a/building-blocks/kv-store/kv-store-ts/my-workflow/package.json +++ b/building-blocks/kv-store/kv-store-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "@noble/hashes": "^1.7.2" }, "devDependencies": { diff --git a/building-blocks/read-data-feeds/read-data-feeds-ts/my-workflow/package.json b/building-blocks/read-data-feeds/read-data-feeds-ts/my-workflow/package.json index aa4b7cda..71e9d1bf 100644 --- a/building-blocks/read-data-feeds/read-data-feeds-ts/my-workflow/package.json +++ b/building-blocks/read-data-feeds/read-data-feeds-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/building-blocks/read-data-feeds/read-mvr-data-feeds-go/go.sum b/building-blocks/read-data-feeds/read-mvr-data-feeds-go/go.sum index 7c05ea58..d9557b6d 100644 --- a/building-blocks/read-data-feeds/read-mvr-data-feeds-go/go.sum +++ b/building-blocks/read-data-feeds/read-mvr-data-feeds-go/go.sum @@ -173,14 +173,14 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250918131840-564fe2776a35 h1:hhKdzgNZT+TnohlmJODtaxlSk+jyEO79YNe8zLFtp78= -github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250918131840-564fe2776a35/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= -github.com/smartcontractkit/cre-sdk-go v1.0.0 h1:O52/QDmw/W8SJ7HQ9ASlVx7alSMGsewjL0Y8WZmgf5w= -github.com/smartcontractkit/cre-sdk-go v1.0.0/go.mod h1:CQY8hCISjctPmt8ViDVgFm4vMGLs5fYI198QhkBS++Y= -github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v1.0.0-beta.0 h1:t2bzRHnqkyxvcrJKSsKPmCGLMjGO97ESgrtLCnTIEQw= -github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v1.0.0-beta.0/go.mod h1:VVJ4mvA7wOU1Ic5b/vTaBMHEUysyxd0gdPPXkAu8CmY= -github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0 h1:Tui4xQVln7Qtk3CgjBRgDfihgEaAJy2t2MofghiGIDA= -github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0/go.mod h1:PWyrIw16It4TSyq6mDXqmSR0jF2evZRKuBxu7pK1yDw= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251103211352-4c2e6505f4d6 h1:Zmwr/k+7JM/3FRf4AcK9SqLdQ+ZmIRt3LBALa+T1Igg= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251103211352-4c2e6505f4d6/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= +github.com/smartcontractkit/cre-sdk-go v1.0.1-0.20251103212936-f87c1b6768b7 h1:sKGjw83NUOSyVOvYUTNdbUzd35CuIS0hDsmNb4CPrg4= +github.com/smartcontractkit/cre-sdk-go v1.0.1-0.20251103212936-f87c1b6768b7/go.mod h1:CQY8hCISjctPmt8ViDVgFm4vMGLs5fYI198QhkBS++Y= +github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v1.0.0-beta.0.0.20251103212936-f87c1b6768b7 h1:riXijgLlYw3hu7YGMAuf6o1G1MYyLhqENoYqEz79HTU= +github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v1.0.0-beta.0.0.20251103212936-f87c1b6768b7/go.mod h1:mbkNee1UMkB13Dab+EMwlkPbYY2Qwj5+SaOtYWagY8s= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0.0.20251103212936-f87c1b6768b7 h1:mNArf+I7g6h9WLH+j65w6epY3CygfJy13HOoVRS42KQ= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v1.0.0-beta.0.0.20251103212936-f87c1b6768b7/go.mod h1:PWyrIw16It4TSyq6mDXqmSR0jF2evZRKuBxu7pK1yDw= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= diff --git a/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/contracts/package.json b/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/contracts/package.json +++ b/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/my-workflow/package.json b/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/my-workflow/package.json index 3ec59d83..eb6f9922 100644 --- a/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/my-workflow/package.json +++ b/building-blocks/read-data-feeds/read-mvr-data-feeds-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/scripts/check-templates.sh b/scripts/check-templates.sh new file mode 100755 index 00000000..457d5060 --- /dev/null +++ b/scripts/check-templates.sh @@ -0,0 +1,570 @@ +#!/bin/bash +# scripts/check-templates.sh +# Validates and compiles changed CRE templates (TypeScript + Go). +# +# Usage (CI): +# BASE_REF=main ./scripts/check-templates.sh +# +# Usage (local — specific templates): +# CHANGED_TEMPLATES="building-blocks/indexer-block-trigger/block-trigger-ts" \ +# ./scripts/check-templates.sh --verbose +# +# Usage (local — all templates): +# ./scripts/check-templates.sh --verbose +# +# Environment variables: +# BASE_REF Git base branch to diff against (e.g. "main"). +# Used to find changed templates in a PR. +# CHANGED_TEMPLATES Newline-separated list of template root dirs +# relative to the repo root. Overrides BASE_REF detection. +# VERBOSE Set to 1 for verbose output (same as --verbose/-v). +# RESULTS_FILE Path to write structured JSON results. +# Default: /tmp/template-results.json + +# -------------------------------------------------------------------------- +# Flags + +VERBOSE=false +for arg in "$@"; do + case "$arg" in + -v|--verbose) VERBOSE=true ;; + esac +done +[[ "${VERBOSE:-0}" == "1" ]] && VERBOSE=true + +# -------------------------------------------------------------------------- +# Logging helpers + +info() { echo "$@"; } +vlog() { $VERBOSE && echo "$@" || true; } + +# Capture stdout+stderr of a command into a variable, streaming in verbose mode. +# Usage: run_captured [args...] +run_captured() { + local _outvar="$1"; shift + local _tmpfile; _tmpfile=$(mktemp) + if $VERBOSE; then + "$@" 2>&1 | tee "$_tmpfile"; local _rc="${PIPESTATUS[0]}" + else + "$@" > "$_tmpfile" 2>&1; local _rc=$? + fi + printf -v "$_outvar" '%s' "$(cat "$_tmpfile")" + rm -f "$_tmpfile" + return "$_rc" +} + +# -------------------------------------------------------------------------- +# Setup + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(dirname "$SCRIPT_DIR")" +RESULTS_FILE="${RESULTS_FILE:-/tmp/template-results.json}" +JSONL_FILE=$(mktemp /tmp/cre-check-XXXXXX.jsonl) + +# Lockfile tracking — populated by check_ts_workflow before each bun install. +# Backed-up lockfiles are restored; generated ones are deleted on exit. +LOCKFILE_BACKUPS=() # "backup_path:original_path" pairs +GENERATED_LOCKFILES=() # paths to delete on exit + +cleanup() { + rm -f "$JSONL_FILE" "${_FAIL_OUT:-}" 2>/dev/null || true + + # Restore lockfiles that existed before we ran + local entry backup original + for entry in "${LOCKFILE_BACKUPS[@]+"${LOCKFILE_BACKUPS[@]}"}"; do + backup="${entry%%:*}" + original="${entry##*:}" + [[ -f "$backup" ]] && mv -f "$backup" "$original" 2>/dev/null || true + done + + # Remove lockfiles that we generated fresh + local f + for f in "${GENERATED_LOCKFILES[@]+"${GENERATED_LOCKFILES[@]}"}"; do + rm -f "$f" 2>/dev/null || true + done +} +trap cleanup EXIT INT TERM + +# -------------------------------------------------------------------------- +# YAML helpers (require python3 + PyYAML, always available on ubuntu-latest) + +# Print the value of a top-level YAML field to stdout. +yaml_field() { + local yaml_file="$1" field="$2" + python3 -c " +import yaml, sys +with open(sys.argv[1]) as f: + d = yaml.safe_load(f) or {} +val = d.get(sys.argv[2]) +if val is not None: + print(str(val).strip()) +" "$yaml_file" "$field" +} + +# Print workflow dirs (one per line) from .cre/template.yaml. +yaml_workflow_dirs() { + local yaml_file="$1" + python3 -c " +import yaml, sys +with open(sys.argv[1]) as f: + d = yaml.safe_load(f) or {} +for w in d.get('workflows', []): + print(w['dir']) +" "$yaml_file" +} + +# Validate a YAML file. For kind='template', also checks required fields. +# Prints an error message to stdout and returns 1 on failure. +validate_yaml() { + local yaml_file="$1" kind="${2:-}" + python3 -c " +import yaml, sys +kind = sys.argv[2] if len(sys.argv) > 2 else '' +try: + with open(sys.argv[1]) as f: + d = yaml.safe_load(f) + if d is None: + print('empty YAML file') + sys.exit(1) + if kind == 'template': + required = ['kind', 'id', 'title', 'language', 'category', 'workflows'] + missing = [f for f in required if f not in d] + if missing: + print(f'missing required fields: {missing}') + sys.exit(1) +except yaml.YAMLError as e: + print(f'YAML parse error: {e}') + sys.exit(1) +" "$yaml_file" "$kind" 2>&1 +} + +# -------------------------------------------------------------------------- +# Results recording +# +# Each result is one JSON line in JSONL_FILE. +# Failure output is read from _FAIL_OUT (a temp file written by run_captured). + +_FAIL_OUT=$(mktemp /tmp/cre-failout-XXXXXX.txt) + +# record_pass +record_pass() { + _write_result "$1" "$2" "$3" "$4" "pass" "" "" +} + +# record_fail +record_fail() { + _write_result "$1" "$2" "$3" "$4" "fail" "$5" "${6:-}" +} + +_write_result() { + local name="$1" lang="$2" sdk_range="$3" sdk_resolved="$4" + local status="$5" step="$6" output_file="$7" + python3 -c " +import json, sys, os +name, lang, sdk_range, sdk_resolved, status, step = sys.argv[1:7] +output_file = sys.argv[7] if len(sys.argv) > 7 else '' +output = None +if output_file and os.path.isfile(output_file): + with open(output_file) as f: + text = f.read(3000).strip() + if text: + output = text +print(json.dumps({ + 'name': name, + 'language': lang, + 'sdk_range': sdk_range, + 'sdk_resolved': sdk_resolved, + 'status': status, + 'failure_step': step or None, + 'failure_output': output, +})) +" "$name" "$lang" "$sdk_range" "$sdk_resolved" "$status" "$step" "$output_file" >> "$JSONL_FILE" +} + +finalize_results() { + python3 -c " +import json, sys, os +jsonl = sys.argv[1] +out = sys.argv[2] +templates = [] +if os.path.isfile(jsonl): + with open(jsonl) as f: + for line in f: + line = line.strip() + if line: + try: + templates.append(json.loads(line)) + except json.JSONDecodeError: + pass +passed = sum(1 for t in templates if t['status'] == 'pass') +failed = len(templates) - passed +with open(out, 'w') as f: + json.dump({'templates': templates, 'total': len(templates), + 'passed': passed, 'failed': failed}, f, indent=2) +" "$JSONL_FILE" "$RESULTS_FILE" +} + +# -------------------------------------------------------------------------- +# Detect changed template roots + +detect_template_roots() { + if [[ -n "${CHANGED_TEMPLATES:-}" ]]; then + # Use explicitly provided list (newline-separated) + while IFS= read -r dir; do + [[ -n "$dir" && -f "$REPO_ROOT/$dir/.cre/template.yaml" ]] && echo "$dir" + done <<< "$CHANGED_TEMPLATES" + return + fi + + if [[ -n "${BASE_REF:-}" ]]; then + # Derive from git diff against the PR base branch + local changed_files + changed_files=$(git -C "$REPO_ROOT" diff --name-only "origin/${BASE_REF}...HEAD" 2>/dev/null || true) + if [[ -z "$changed_files" ]]; then + return + fi + + while IFS= read -r file; do + [[ -z "$file" ]] && continue + local check_dir="$REPO_ROOT/$(dirname "$file")" + while [[ "$check_dir" != "$REPO_ROOT" && "$check_dir" != "/" ]]; do + if [[ -f "$check_dir/.cre/template.yaml" ]]; then + echo "${check_dir#"$REPO_ROOT/"}" + break + fi + check_dir="$(dirname "$check_dir")" + done + done <<< "$changed_files" + return + fi + + # Fallback: all templates (useful for local dev) + while IFS= read -r yaml; do + local template_dir + template_dir="$(dirname "$(dirname "$yaml")")" + echo "${template_dir#"$REPO_ROOT/"}" + done < <(find "$REPO_ROOT" -name "template.yaml" -path "*/.cre/template.yaml" \ + -not -path "*/node_modules/*" 2>/dev/null) +} + +# -------------------------------------------------------------------------- +# TypeScript workflow check + +# check_ts_workflow +check_ts_workflow() { + local template_dir="$1" workflow_subdir="$2" + local abs_wf="$REPO_ROOT/$template_dir/$workflow_subdir" + local pkg_json="$abs_wf/package.json" + local display="$template_dir / $workflow_subdir" + + if [[ ! -f "$pkg_json" ]]; then + vlog " ⚠️ No package.json in $workflow_subdir — skipping" + return 0 + fi + + info " 📦 $display" + + # Resolve the SDK semver range from package.json + local sdk_range sdk_resolved="unknown" + sdk_range=$(node -e " +const p = require('$pkg_json'); +const deps = Object.assign({}, p.dependencies, p.devDependencies); +process.stdout.write(deps['@chainlink/cre-sdk'] || 'unknown'); +" 2>/dev/null || echo "unknown") + + local _out="" + cd "$abs_wf" + + # 1. Validate workflow.yaml if present + if [[ -f "workflow.yaml" ]]; then + local yaml_err + if ! yaml_err=$(validate_yaml "workflow.yaml"); then + info " ❌ workflow.yaml invalid: $yaml_err" + printf '%s' "$yaml_err" > "$_FAIL_OUT" + record_fail "$display" "typescript" "$sdk_range" "$sdk_resolved" "yaml-validation" "$_FAIL_OUT" + return 0 + fi + vlog " ✅ workflow.yaml valid" + fi + + # 2. bun install — track lockfiles so we can restore them on exit. + # Use git to distinguish committed lockfiles (restore) from untracked/absent + # ones (delete). This correctly handles leftovers from previous script runs. + local _wf_rel="${abs_wf#"$REPO_ROOT/"}" + local lockfile + for lockfile in package-lock.json bun.lock; do + if git -C "$REPO_ROOT" ls-files --error-unmatch "$_wf_rel/$lockfile" &>/dev/null; then + cp "$lockfile" "${lockfile}.__cre_bak" + LOCKFILE_BACKUPS+=("$(pwd)/${lockfile}.__cre_bak:$(pwd)/$lockfile") + else + GENERATED_LOCKFILES+=("$(pwd)/$lockfile") + fi + done + vlog " Installing dependencies (SDK range: $sdk_range)..." + if ! run_captured _out bun install; then + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ bun install failed" + record_fail "$display" "typescript" "$sdk_range" "$sdk_resolved" "bun install" "$_FAIL_OUT" + return 0 + fi + + # 2b. bun install in template contracts/ when present (generated bindings import viem / cre-sdk). + local contracts_dir="$REPO_ROOT/$template_dir/contracts" + if [[ -f "$contracts_dir/package.json" ]]; then + vlog " Installing contracts dependencies..." + cd "$contracts_dir" + local _contracts_rel="${contracts_dir#"$REPO_ROOT/"}" + for lockfile in package-lock.json bun.lock; do + if git -C "$REPO_ROOT" ls-files --error-unmatch "$_contracts_rel/$lockfile" &>/dev/null; then + cp "$lockfile" "${lockfile}.__cre_bak" + LOCKFILE_BACKUPS+=("$(pwd)/${lockfile}.__cre_bak:$(pwd)/$lockfile") + else + GENERATED_LOCKFILES+=("$(pwd)/$lockfile") + fi + done + if ! run_captured _out bun install; then + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ bun install (contracts) failed" + record_fail "$display" "typescript" "$sdk_range" "$sdk_resolved" "bun install (contracts)" "$_FAIL_OUT" + cd "$abs_wf" + return 0 + fi + cd "$abs_wf" + fi + + # 3. Capture resolved SDK version + if [[ -f "node_modules/@chainlink/cre-sdk/package.json" ]]; then + sdk_resolved=$(node -e \ + "process.stdout.write(require('./node_modules/@chainlink/cre-sdk/package.json').version)" \ + 2>/dev/null || echo "unknown") + fi + vlog " SDK: $sdk_range → resolved $sdk_resolved" + + # 4. Typecheck (if script exists) + local has_typecheck + has_typecheck=$(node -e \ + "const s=(require('./package.json').scripts||{}); process.stdout.write(s.typecheck?'yes':'no')" \ + 2>/dev/null || echo "no") + if [[ "$has_typecheck" == "yes" ]]; then + vlog " Running typecheck..." + if ! run_captured _out bun run typecheck; then + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ typecheck failed (SDK $sdk_resolved)" + record_fail "$display" "typescript" "$sdk_range" "$sdk_resolved" "typecheck" "$_FAIL_OUT" + return 0 + fi + vlog " ✅ typecheck passed" + else + vlog " ⚠️ No typecheck script — skipping" + fi + + # 5. cre-compile (if main.ts exists) + if [[ -f "main.ts" ]]; then + vlog " Running cre-compile..." + if ! run_captured _out bunx cre-compile main.ts; then + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ cre-compile failed (SDK $sdk_resolved)" + record_fail "$display" "typescript" "$sdk_range" "$sdk_resolved" "cre-compile" "$_FAIL_OUT" + # Clean up any partial build artifacts + rm -f main.js main.wasm + return 0 + fi + rm -f main.js main.wasm + vlog " ✅ cre-compile passed" + info " ✅ $display (SDK $sdk_resolved)" + else + vlog " ⚠️ No main.ts — typecheck only" + info " ✅ $display (SDK $sdk_resolved, typecheck only)" + fi + + record_pass "$display" "typescript" "$sdk_range" "$sdk_resolved" +} + +# -------------------------------------------------------------------------- +# Go template check + +# check_go_template +check_go_template() { + local template_dir="$1" + local abs_template="$REPO_ROOT/$template_dir" + local display="$template_dir" + + info " 🔧 $display" + + if [[ ! -f "$abs_template/go.mod" ]]; then + vlog " ⚠️ No go.mod — skipping" + return 0 + fi + + # Resolve cre-sdk-go version from go.mod (first require line for the core module) + local sdk_version + sdk_version=$(grep -m1 'github\.com/smartcontractkit/cre-sdk-go ' "$abs_template/go.mod" \ + | awk '{print $2}') + sdk_version="${sdk_version:-unknown}" + + local _out="" + cd "$abs_template" + + # 1. Validate workflow.yaml files (one per workflow dir) + local wf_dir yaml_err + while IFS= read -r wf_dir; do + [[ -z "$wf_dir" ]] && continue + local wf_yaml="$wf_dir/workflow.yaml" + if [[ -f "$wf_yaml" ]]; then + if ! yaml_err=$(validate_yaml "$wf_yaml"); then + info " ❌ $wf_yaml invalid: $yaml_err" + printf '%s' "$yaml_err" > "$_FAIL_OUT" + record_fail "$display" "go" "$sdk_version" "$sdk_version" "yaml-validation" "$_FAIL_OUT" + return 0 + fi + vlog " ✅ $wf_yaml valid" + fi + done <<< "$(yaml_workflow_dirs "$abs_template/.cre/template.yaml")" + + # 2. go mod verify + vlog " Running go mod verify..." + if ! run_captured _out go mod verify; then + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ go mod verify failed" + record_fail "$display" "go" "$sdk_version" "$sdk_version" "go mod verify" "$_FAIL_OUT" + return 0 + fi + vlog " ✅ go mod verify passed" + + # 3. go vet (with wasip1 build tags so constrained files are included) + vlog " Running go vet..." + if ! run_captured _out env GOOS=wasip1 GOARCH=wasm go vet ./...; then + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ go vet failed" + record_fail "$display" "go" "$sdk_version" "$sdk_version" "go vet" "$_FAIL_OUT" + return 0 + fi + vlog " ✅ go vet passed" + + # 4. go build each workflow dir to WASM + local failed_build=false + while IFS= read -r wf_dir; do + [[ -z "$wf_dir" ]] && continue + if [[ ! -d "$wf_dir" ]]; then + vlog " ⚠️ Workflow dir '$wf_dir' not found — skipping" + continue + fi + + local wasm_out; wasm_out=$(mktemp /tmp/cre-wasm-XXXXXX.wasm) + vlog " Building ./$wf_dir → WASM..." + if ! run_captured _out env GOOS=wasip1 GOARCH=wasm go build -o "$wasm_out" "./$wf_dir"; then + rm -f "$wasm_out" + printf '%s' "$_out" > "$_FAIL_OUT" + info " ❌ go build ./$wf_dir failed (SDK $sdk_version)" + record_fail "$display" "go" "$sdk_version" "$sdk_version" "go build ./$wf_dir" "$_FAIL_OUT" + failed_build=true + break + fi + rm -f "$wasm_out" + vlog " ✅ go build ./$wf_dir passed" + done <<< "$(yaml_workflow_dirs "$abs_template/.cre/template.yaml")" + + if ! $failed_build; then + info " ✅ $display (SDK $sdk_version)" + record_pass "$display" "go" "$sdk_version" "$sdk_version" + fi +} + +# -------------------------------------------------------------------------- +# Main + +cd "$REPO_ROOT" + +TEMPLATE_ROOTS=() +while IFS= read -r root; do + [[ -n "$root" ]] && TEMPLATE_ROOTS+=("$root") +done < <(detect_template_roots | sort -u) + +if [[ ${#TEMPLATE_ROOTS[@]} -eq 0 ]]; then + info "No changed templates detected — nothing to check." + echo '{"templates":[],"total":0,"passed":0,"failed":0}' > "$RESULTS_FILE" + exit 0 +fi + +TOTAL=${#TEMPLATE_ROOTS[@]} +info "Found $TOTAL changed template(s) to check." +info "" + +IDX=0 +for template_dir in "${TEMPLATE_ROOTS[@]}"; do + IDX=$((IDX + 1)) + abs_template="$REPO_ROOT/$template_dir" + template_yaml="$abs_template/.cre/template.yaml" + + if [[ ! -f "$template_yaml" ]]; then + vlog "[$IDX/$TOTAL] Skipping $template_dir (no .cre/template.yaml)" + continue + fi + + # Validate .cre/template.yaml before anything else + local_yaml_err=$(validate_yaml "$template_yaml" "template" || true) + if [[ -n "$local_yaml_err" ]]; then + info "[$IDX/$TOTAL] $template_dir" + info " ❌ .cre/template.yaml invalid: $local_yaml_err" + printf '%s' "$local_yaml_err" > "$_FAIL_OUT" + record_fail "$template_dir" "unknown" "unknown" "unknown" "template-yaml-validation" "$_FAIL_OUT" + continue + fi + + language=$(yaml_field "$template_yaml" "language") + info "[$IDX/$TOTAL] $template_dir ($language)" + + case "$language" in + typescript) + # Check each workflow dir listed in .cre/template.yaml + while IFS= read -r wf_dir; do + [[ -z "$wf_dir" ]] && continue + check_ts_workflow "$template_dir" "$wf_dir" + done <<< "$(yaml_workflow_dirs "$template_yaml")" + ;; + go) + check_go_template "$template_dir" + ;; + *) + info " ⚠️ Unknown language '$language' — skipping" + ;; + esac + info "" +done + +# -------------------------------------------------------------------------- +# Summary + +finalize_results + +PASS_COUNT=0 FAIL_COUNT=0 +if [[ -f "$RESULTS_FILE" ]]; then + PASS_COUNT=$(python3 -c "import json; d=json.load(open('$RESULTS_FILE')); print(d['passed'])" 2>/dev/null || echo 0) + FAIL_COUNT=$(python3 -c "import json; d=json.load(open('$RESULTS_FILE')); print(d['failed'])" 2>/dev/null || echo 0) +fi + +info "========================================================" +info "Results: $PASS_COUNT passed, $FAIL_COUNT failed" +info "========================================================" + +if [[ "$FAIL_COUNT" -gt 0 ]]; then + info "" + info "Failed templates:" + python3 -c " +import json, sys +data = json.load(open('$RESULTS_FILE')) +for t in data['templates']: + if t['status'] != 'pass': + step = t.get('failure_step') or '' + print(f\" ❌ {t['name']} — {step}\") + out = t.get('failure_output') or '' + if out: + for line in out.strip().split('\n')[:20]: + print(f' {line}') +" + exit 1 +else + info "" + info "All templates passed!" + exit 0 +fi diff --git a/starter-templates/bring-your-own-data/workflow-ts/contracts/package.json b/starter-templates/bring-your-own-data/workflow-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/bring-your-own-data/workflow-ts/contracts/package.json +++ b/starter-templates/bring-your-own-data/workflow-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/bring-your-own-data/workflow-ts/nav/package.json b/starter-templates/bring-your-own-data/workflow-ts/nav/package.json index 2c000ba5..5952239d 100644 --- a/starter-templates/bring-your-own-data/workflow-ts/nav/package.json +++ b/starter-templates/bring-your-own-data/workflow-ts/nav/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/bring-your-own-data/workflow-ts/por/package.json b/starter-templates/bring-your-own-data/workflow-ts/por/package.json index 2266674b..12d0fdee 100644 --- a/starter-templates/bring-your-own-data/workflow-ts/por/package.json +++ b/starter-templates/bring-your-own-data/workflow-ts/por/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/circuit-breaker/circuit-breaker-ts/contracts/package.json b/starter-templates/circuit-breaker/circuit-breaker-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/circuit-breaker/circuit-breaker-ts/contracts/package.json +++ b/starter-templates/circuit-breaker/circuit-breaker-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/circuit-breaker/circuit-breaker-ts/my-workflow/package.json b/starter-templates/circuit-breaker/circuit-breaker-ts/my-workflow/package.json index 28d6d8e0..bda89733 100644 --- a/starter-templates/circuit-breaker/circuit-breaker-ts/my-workflow/package.json +++ b/starter-templates/circuit-breaker/circuit-breaker-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/custom-data-feed/cre-custom-data-feed-go/go.sum b/starter-templates/custom-data-feed/cre-custom-data-feed-go/go.sum index 2a9e8112..733b8ad5 100644 --- a/starter-templates/custom-data-feed/cre-custom-data-feed-go/go.sum +++ b/starter-templates/custom-data-feed/cre-custom-data-feed-go/go.sum @@ -175,17 +175,13 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250918131840-564fe2776a35 h1:hhKdzgNZT+TnohlmJODtaxlSk+jyEO79YNe8zLFtp78= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20250918131840-564fe2776a35/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= -github.com/smartcontractkit/cre-sdk-go v0.9.0 h1:MDO9HFb4tjvu4mI4gKvdO+qXP1irULxhFwlTPVBytaM= -github.com/smartcontractkit/cre-sdk-go v0.9.0/go.mod h1:CQY8hCISjctPmt8ViDVgFm4vMGLs5fYI198QhkBS++Y= +github.com/smartcontractkit/cre-sdk-go v0.10.0 h1:ZKQaTKa027R8Px30NezXauMaf0toUKeUiOEj+yc67Bg= github.com/smartcontractkit/cre-sdk-go v0.10.0/go.mod h1:CQY8hCISjctPmt8ViDVgFm4vMGLs5fYI198QhkBS++Y= -github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.9.0 h1:0ddtacyL1aAFxIolQnbysYlJKP9FOLJc1YRFS/Z9OJA= -github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.9.0/go.mod h1:VVJ4mvA7wOU1Ic5b/vTaBMHEUysyxd0gdPPXkAu8CmY= +github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.10.0 h1:G0w0cLzHy/5m74IzSGz1Ynjffym4ZxLeUrRLp8EFP5w= github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.10.0/go.mod h1:VVJ4mvA7wOU1Ic5b/vTaBMHEUysyxd0gdPPXkAu8CmY= -github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.9.0 h1:VTLdU4nZJ9L+4X0ql20rxQ06dt572A2kmGG2nVHRgiI= -github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.9.0/go.mod h1:M83m3FsM1uqVu06OO58mKUSZJjjH8OGJsmvFpFlRDxI= +github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.10.0 h1:nP6PVWrrTIICvjwQuFitsQecQWbqpPaYzaTEjx92eTQ= github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.10.0/go.mod h1:M83m3FsM1uqVu06OO58mKUSZJjjH8OGJsmvFpFlRDxI= -github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.9.0 h1:BWqX7Cnd6VnhHEpjfrQGEajPtAwqH4MH0D7o3iEPvvU= -github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.9.0/go.mod h1:PWyrIw16It4TSyq6mDXqmSR0jF2evZRKuBxu7pK1yDw= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.10.0 h1:g7UrVaNKVEmIhVkJTk4f8raCM8Kp/RTFnAT64wqNmTY= github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.10.0/go.mod h1:PWyrIw16It4TSyq6mDXqmSR0jF2evZRKuBxu7pK1yDw= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= diff --git a/starter-templates/custom-data-feed/cre-custom-data-feed-go/my-workflow/workflow_test.go b/starter-templates/custom-data-feed/cre-custom-data-feed-go/my-workflow/workflow_test.go index da790b91..cd4b337b 100644 --- a/starter-templates/custom-data-feed/cre-custom-data-feed-go/my-workflow/workflow_test.go +++ b/starter-templates/custom-data-feed/cre-custom-data-feed-go/my-workflow/workflow_test.go @@ -1,3 +1,5 @@ +//go:build !wasip1 + package main import ( diff --git a/starter-templates/custom-data-feed/cre-custom-data-feed-ts/contracts/package.json b/starter-templates/custom-data-feed/cre-custom-data-feed-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/custom-data-feed/cre-custom-data-feed-ts/contracts/package.json +++ b/starter-templates/custom-data-feed/cre-custom-data-feed-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/custom-data-feed/cre-custom-data-feed-ts/my-workflow/package.json b/starter-templates/custom-data-feed/cre-custom-data-feed-ts/my-workflow/package.json index b4563379..37b9292b 100644 --- a/starter-templates/custom-data-feed/cre-custom-data-feed-ts/my-workflow/package.json +++ b/starter-templates/custom-data-feed/cre-custom-data-feed-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/event-reactor/event-reactor-ts/contracts/package.json b/starter-templates/event-reactor/event-reactor-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/event-reactor/event-reactor-ts/contracts/package.json +++ b/starter-templates/event-reactor/event-reactor-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/event-reactor/event-reactor-ts/my-workflow/package.json b/starter-templates/event-reactor/event-reactor-ts/my-workflow/package.json index 22bfd020..f2e47cd6 100644 --- a/starter-templates/event-reactor/event-reactor-ts/my-workflow/package.json +++ b/starter-templates/event-reactor/event-reactor-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/keeper-bot/keeper-bot-ts/contracts/package.json b/starter-templates/keeper-bot/keeper-bot-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/keeper-bot/keeper-bot-ts/contracts/package.json +++ b/starter-templates/keeper-bot/keeper-bot-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/keeper-bot/keeper-bot-ts/my-workflow/package.json b/starter-templates/keeper-bot/keeper-bot-ts/my-workflow/package.json index aca85e7d..ecd294a3 100644 --- a/starter-templates/keeper-bot/keeper-bot-ts/my-workflow/package.json +++ b/starter-templates/keeper-bot/keeper-bot-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/multi-chain-token-manager/workflow-ts/contracts/package.json b/starter-templates/multi-chain-token-manager/workflow-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/multi-chain-token-manager/workflow-ts/contracts/package.json +++ b/starter-templates/multi-chain-token-manager/workflow-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/multi-chain-token-manager/workflow-ts/workflow/package.json b/starter-templates/multi-chain-token-manager/workflow-ts/workflow/package.json index cf5db34f..1dafa9ad 100644 --- a/starter-templates/multi-chain-token-manager/workflow-ts/workflow/package.json +++ b/starter-templates/multi-chain-token-manager/workflow-ts/workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/prediction-market/prediction-market-ts/contracts/package.json b/starter-templates/prediction-market/prediction-market-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/prediction-market/prediction-market-ts/contracts/package.json +++ b/starter-templates/prediction-market/prediction-market-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/prediction-market/prediction-market-ts/market-creation/package.json b/starter-templates/prediction-market/prediction-market-ts/market-creation/package.json index a832dddf..b54b2a18 100644 --- a/starter-templates/prediction-market/prediction-market-ts/market-creation/package.json +++ b/starter-templates/prediction-market/prediction-market-ts/market-creation/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/prediction-market/prediction-market-ts/market-dispute/package.json b/starter-templates/prediction-market/prediction-market-ts/market-dispute/package.json index 47e2358e..45aa5c20 100644 --- a/starter-templates/prediction-market/prediction-market-ts/market-dispute/package.json +++ b/starter-templates/prediction-market/prediction-market-ts/market-dispute/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/prediction-market/prediction-market-ts/market-resolution/package.json b/starter-templates/prediction-market/prediction-market-ts/market-resolution/package.json index aa673627..f44be7fe 100644 --- a/starter-templates/prediction-market/prediction-market-ts/market-resolution/package.json +++ b/starter-templates/prediction-market/prediction-market-ts/market-resolution/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/sports-resolution/sports-resolution-ts/contracts/package.json b/starter-templates/sports-resolution/sports-resolution-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/sports-resolution/sports-resolution-ts/contracts/package.json +++ b/starter-templates/sports-resolution/sports-resolution-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/sports-resolution/sports-resolution-ts/game-resolution/package.json b/starter-templates/sports-resolution/sports-resolution-ts/game-resolution/package.json index 5186cd4b..df394fe2 100644 --- a/starter-templates/sports-resolution/sports-resolution-ts/game-resolution/package.json +++ b/starter-templates/sports-resolution/sports-resolution-ts/game-resolution/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/main.ts b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/main.ts index e53ec61e..c31f7147 100644 --- a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/main.ts +++ b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/main.ts @@ -1,14 +1,17 @@ -import { +import { bytesToHex, + consensusIdenticalAggregation, cre, + EVMClient, getNetwork, type HTTPPayload, hexToBase64, + HTTPClient, + HTTPCapability, Runner, type Runtime, type NodeRuntime, TxStatus, - consensusMedianAggregation, } from '@chainlink/cre-sdk' import { encodeAbiParameters, parseAbiParameters, encodeFunctionData, decodeFunctionResult, getAddress, parseUnits } from 'viem' import { z } from 'zod' @@ -33,6 +36,8 @@ const configSchema = z.object({ type Config = z.infer +type PorReserveData = { totalReserve: number; lastUpdated: string } + // ======================================== // PAYLOAD SCHEMA // ======================================== @@ -99,7 +104,7 @@ const validateProofOfReserve = ( // For mock data (file:// URL), use hardcoded values // In production, this would fetch from a real PoR API endpoint - let reserveData: { totalReserve: number; lastUpdated: string } + let reserveData: PorReserveData if (config.porApiUrl.startsWith('file://')) { // Mock PoR data (matching mock-por-response.json) @@ -111,8 +116,8 @@ const validateProofOfReserve = ( } else { // Fetch from real PoR API in node mode reserveData = runtime.runInNodeMode( - (nodeRuntime: NodeRuntime) => { - const httpClient = new cre.capabilities.HTTPClient() + (nodeRuntime: NodeRuntime) => { + const httpClient = new HTTPClient() const response = httpClient.sendRequest(nodeRuntime, { url: config.porApiUrl, method: 'GET', @@ -124,7 +129,7 @@ const validateProofOfReserve = ( lastUpdated: data.lastUpdated, } }, - consensusMedianAggregation() + consensusIdenticalAggregation(), )().result() } @@ -163,7 +168,7 @@ const validateProofOfReserve = ( */ const mintWithACE = ( runtime: Runtime, - evmClient: cre.capabilities.EVMClient, + evmClient: EVMClient, beneficiary: string, mintRecipient: string, amount: bigint, @@ -231,13 +236,13 @@ const mintWithACE = ( // We need to check the actual execution result from Forwarder events // For now, check if txStatus is success and errorMessage is empty if (txStatus !== TxStatus.SUCCESS) { - const errorMsg = resp.errorMessage || txStatus - + const errorMsg = resp.errorMessage ?? String(txStatus) + // Check if it's a PolicyRunRejected error if (errorMsg.includes('PolicyRunRejected') || errorMsg.includes('blacklisted')) { throw new Error(`[ACE REJECTED] Address ${beneficiary} is blacklisted`) } - + throw new Error(`Failed to mint: ${errorMsg}`) } @@ -265,7 +270,7 @@ const mintWithACE = ( */ const transferWithACE = ( runtime: Runtime, - evmClient: cre.capabilities.EVMClient, + evmClient: EVMClient, sender: string, beneficiary: string, amount: bigint, @@ -322,13 +327,13 @@ const transferWithACE = ( const txStatus = resp.txStatus if (txStatus !== TxStatus.SUCCESS) { - const errorMsg = resp.errorMessage || txStatus - + const errorMsg = resp.errorMessage ?? String(txStatus) + // Check if it's a PolicyRunRejected error if (errorMsg.includes('PolicyRunRejected') || errorMsg.includes('blacklisted')) { throw new Error(`[ACE REJECTED] Beneficiary ${beneficiary} is blacklisted`) } - + throw new Error(`Failed to initiate CCIP transfer: ${errorMsg}`) } @@ -344,7 +349,7 @@ const transferWithACE = ( // ======================================== // HTTP TRIGGER HANDLER // ======================================== -const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload): object => { +const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload) => { runtime.log('=== Phase 3: PoR + ACE + CCIP Workflow ===') // Require payload @@ -375,7 +380,7 @@ const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload): object = throw new Error('Sepolia network not found') } - const evmClient = new cre.capabilities.EVMClient(network.chainSelector.selector) + const evmClient = new EVMClient(network.chainSelector.selector) // Convert amount to wei const amountWei = parseUnits(parsedPayload.amount, runtime.config.decimals) @@ -510,7 +515,7 @@ const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload): object = runtime.log(` Verify on-chain to confirm actual results`) runtime.log(`\nResult: ${safeJsonStringify(result)}`) - return JSON.stringify(result) + return result } catch (error: any) { runtime.log(`❌ Workflow error: ${error.message}`) @@ -522,7 +527,7 @@ const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload): object = // WORKFLOW INITIALIZATION // ======================================== const initWorkflow = (config: Config) => { - const httpTrigger = new cre.capabilities.HTTPCapability() + const httpTrigger = new HTTPCapability() return [ cre.handler(httpTrigger.trigger({}), onHTTPTrigger), diff --git a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/package.json b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/package.json index 9b8a27b2..a3543711 100644 --- a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/package.json +++ b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-por-ace-ccip-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "@openzeppelin/contracts": "5.4.0", "viem": "2.34.0", "zod": "3.25.76" diff --git a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/main.ts b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/main.ts index e00d2317..4d4f8489 100644 --- a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/main.ts +++ b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/main.ts @@ -1,9 +1,11 @@ -import { +import { bytesToHex, cre, + EVMClient, getNetwork, type HTTPPayload, hexToBase64, + HTTPCapability, Runner, type Runtime, TxStatus, @@ -50,7 +52,7 @@ const safeJsonStringify = (obj: any): string => const submitBankInstruction = ( runtime: Runtime, - evmClient: cre.capabilities.EVMClient, + evmClient: EVMClient, instructionType: number, account: string, amount: bigint, @@ -112,7 +114,7 @@ const submitBankInstruction = ( return txHashHex } -const processBankInstruction = (runtime: Runtime, evmClient: cre.capabilities.EVMClient, swiftData: SWIFTPayload): string => { +const processBankInstruction = (runtime: Runtime, evmClient: EVMClient, swiftData: SWIFTPayload): string => { runtime.log(`Processing ${swiftData.instructionCode} instruction from bank: ${swiftData.bankReference}`) // Convert amount from string to scaled bigint (e.g., "1000.00" -> 1000000000000000000000) @@ -142,7 +144,7 @@ const processBankInstruction = (runtime: Runtime, evmClient: cre.capabil return `${swiftData.instructionCode} instruction processed: ${swiftData.amount} ${swiftData.currency}` } -const onHTTPTrigger = (runtime: Runtime, evmClient: cre.capabilities.EVMClient, payload: HTTPPayload): string => { +const onHTTPTrigger = (runtime: Runtime, evmClient: EVMClient, payload: HTTPPayload): string => { runtime.log('Raw HTTP trigger received') // Require payload @@ -169,7 +171,7 @@ const onHTTPTrigger = (runtime: Runtime, evmClient: cre.capabilities.EVM } const initWorkflow = (config: Config) => { - const httpTrigger = new cre.capabilities.HTTPCapability() + const httpTrigger = new HTTPCapability() // Initialize EVM client for the configured chain const network = getNetwork({ @@ -184,12 +186,11 @@ const initWorkflow = (config: Config) => { ) } - const evmClient = new cre.capabilities.EVMClient(network.chainSelector.selector) + const evmClient = new EVMClient(network.chainSelector.selector) return [ - cre.handler(httpTrigger.trigger({}), (runtime, payload) => - onHTTPTrigger(runtime, evmClient, payload) - ), + cre.handler(httpTrigger.trigger({}), (runtime: Runtime, payload: HTTPPayload) => + onHTTPTrigger(runtime, evmClient, payload)), ] } diff --git a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/package.json b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/package.json index d6ea651a..93906fcc 100644 --- a/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/package.json +++ b/starter-templates/stablecoin-ace-ccip/bank-stablecoin-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "@openzeppelin/contracts": "5.4.0", "viem": "2.34.0", "zod": "3.25.76" diff --git a/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/main.ts b/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/main.ts index de105ae6..a17c911f 100644 --- a/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/main.ts +++ b/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/main.ts @@ -1,9 +1,11 @@ -import { +import { bytesToHex, cre, + EVMClient, getNetwork, type HTTPPayload, hexToBase64, + HTTPCapability, Runner, type Runtime, TxStatus, @@ -53,7 +55,7 @@ const safeJsonStringify = (obj: any): string => const submitCCIPTransfer = ( runtime: Runtime, - evmClient: cre.capabilities.EVMClient, + evmClient: EVMClient, consumerAddress: string, sender: string, recipient: string, @@ -115,7 +117,7 @@ const submitCCIPTransfer = ( return txHashHex } -const processCCIPTransfer = (runtime: Runtime, evmClient: cre.capabilities.EVMClient, transferData: TransferPayload): string => { +const processCCIPTransfer = (runtime: Runtime, evmClient: EVMClient, transferData: TransferPayload): string => { runtime.log(`Processing CCIP transfer from bank: ${transferData.bankReference}`) // Look up source and destination chain configs @@ -183,7 +185,7 @@ const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload): string = throw new Error(`Network not found for source chain: ${transferData.sourceChain}`) } - const evmClient = new cre.capabilities.EVMClient(sourceNetwork.chainSelector.selector) + const evmClient = new EVMClient(sourceNetwork.chainSelector.selector) return processCCIPTransfer(runtime, evmClient, transferData) } catch (error) { @@ -193,10 +195,12 @@ const onHTTPTrigger = (runtime: Runtime, payload: HTTPPayload): string = } const initWorkflow = (config: Config) => { - const httpTrigger = new cre.capabilities.HTTPCapability() + const httpTrigger = new HTTPCapability() return [ - cre.handler(httpTrigger.trigger({}), onHTTPTrigger), + cre.handler(httpTrigger.trigger({}), (runtime: Runtime, payload: HTTPPayload) => + onHTTPTrigger(runtime, payload), + ), ] } diff --git a/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/package.json b/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/package.json index 72902f5b..4eba1f0e 100644 --- a/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/package.json +++ b/starter-templates/stablecoin-ace-ccip/ccip-transfer-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" }, "devDependencies": { diff --git a/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/main.ts b/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/main.ts index 4e499ac3..16cc977f 100644 --- a/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/main.ts +++ b/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/main.ts @@ -82,8 +82,8 @@ const postData = (sendRequester: HTTPSendRequester, config: Config, assetParams: "Content-Type": "application/json", }, cacheSettings: { - readFromCache: true, // Enable reading from cache - maxAgeMs: 60000, // Accept cached responses up to 60 seconds old + store: true, + maxAge: '60s', }, } diff --git a/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/package.json b/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/package.json index a7cf19b9..8b221455 100644 --- a/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/package.json +++ b/starter-templates/tokenized-asset-servicing/asset-log-trigger-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/vault-harvester/vault-harvester-ts/contracts/package.json b/starter-templates/vault-harvester/vault-harvester-ts/contracts/package.json index a7c35a63..b09d43d4 100644 --- a/starter-templates/vault-harvester/vault-harvester-ts/contracts/package.json +++ b/starter-templates/vault-harvester/vault-harvester-ts/contracts/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0" } } diff --git a/starter-templates/vault-harvester/vault-harvester-ts/my-workflow/package.json b/starter-templates/vault-harvester/vault-harvester-ts/my-workflow/package.json index 1a44b5eb..d89de1cc 100644 --- a/starter-templates/vault-harvester/vault-harvester-ts/my-workflow/package.json +++ b/starter-templates/vault-harvester/vault-harvester-ts/my-workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0", + "@chainlink/cre-sdk": "^1.6.0", "viem": "2.34.0", "zod": "3.25.76" }, diff --git a/starter-templates/verifiable-build/verifiable-build-ts/workflow/package.json b/starter-templates/verifiable-build/verifiable-build-ts/workflow/package.json index dfe126d8..65487e3b 100644 --- a/starter-templates/verifiable-build/verifiable-build-ts/workflow/package.json +++ b/starter-templates/verifiable-build/verifiable-build-ts/workflow/package.json @@ -8,7 +8,7 @@ }, "license": "UNLICENSED", "dependencies": { - "@chainlink/cre-sdk": "^1.5.0" + "@chainlink/cre-sdk": "^1.6.0" }, "devDependencies": { "typescript": "5.9.3"