π₯οΈ Setup Instructions
This Book
This book is available at the following url:
https://smartcontractkit.github.io/x402-cre-price-alerts/
Important Prerequisites
To get the most out of this session, we recommend you have the following prepared before the masterclass. Some of this will be covered briefly and fast so we can spend time on the LinkLabβs code.
Required Setup
- Node.js v18 or higher - Download here
- BunJs v1.3 or higher - Download here
- Install CRE CLI - Installation instructions
- Add Base Sepolia to your wallet - Add network here
- Get ETH from the Coinbase Faucet - Coinbase Faucet (make sure itβs for Base Sepolia!)
- Get USDC from the Circle Faucet - Circle Faucet (make sure itβs for Base Sepolia!)
- Gemini LLM API key - Get from Google AI Studio
- Sign up to Pushover - Get User Key and Application Key
- If you want to see your workflow work live, install the small devtool mobile app too!
Nice to Have
- π Install mdBook - So you can read the accompanying LinkLab docs easily
cargo install mdbook
Repository
Once you have the prerequisites ready, clone the repository:
git clone https://github.com/smartcontractkit/x402-cre-price-alerts.git
cd x402-cre-price-alerts
npm install
Note that the project uses NPM workspaces, so you must use NPM to install packages.
Welcome to LinkLab Masterclass
Welcome to the LinkLab Masterclass: Intro to Chainlink Runtime Environment (CRE) with x402!
This is a live, virtual masterclass hosted by Chainlink and Base, designed to give you an in-depth, developer-focused walkthrough of how to use the Chainlink Runtime Environment (CRE) and x402 agentic payments.
π€ Meet Your Instructors
Andrej Rakic
DevRel Engineer, Chainlink Labs
X (Twitter): @andrej_dev
LinkedIn: Andrej Rakic
Nolan Edwards
DevRel Engineer, Chainlink Labs
X (Twitter): @nje01
LinkedIn: Nolan Edwards
Run of Show
π₯ Fireside Chat
Join Aakar Shroff (VP of Product, Chainlink Labs) and Erik Reppel (Head of Engineering, Coinbase Developer Platform & Co-author of x402 whitepaper) for an engaging conversation about CRE and x402, and how these technologies work together to enable new models for decentralized applications.
π οΈ Hands-On Workshop
Build a complete token price alert system that demonstrates:
- βHow to start building with Chainlink Runtime Environment (CRE)
- How AI agents can trigger CRE workflows using x402 for payment
- Developing end-to-end CRE workflows in TypeScript
β Q&A
Open floor for questions about CRE, x402, building with these technologies, and this masterclass!
Always Stay Connected
Join the Chainlink Community
- Subscribe to Chainlink Developer Newsletter
- Follow Chainlink on X (Twitter)
- Follow Chainlink on LinkedIn
- Subscribe to Chainlink Official YouTube channel
- Join us on Discord
Our Upcoming Events
π CRE Bootcamp - January 2026
Join us for an 2-day CRE Bootcamp in January 2026! A free, global CRE bootcamp that trains developers to build real-world applications using the Chainlink Runtime Environment (CRE).
More information will be shared soon!
π CRE Hackathon - February 2026
Get ready for the CRE Hackathon in February 2026! Compete with developers from around the world to build innovative applications using Chainlink Runtime Environment.
More information will be shared soon!
Fireside Chat: Meet Aakar & Erik
π€ Our Speakers
Aakar Shroff
VP of Product, Chainlink Labs
Aakar leads Product Management for Chainlinkβs Runtime Environment (CRE). Heβll introduce CRE and sharing insights for how to leverage it for your applications.
Erik Reppel
Head of Engineering, Coinbase Developer Platform & Co-author of x402 whitepaper
Erik leads Engineering for Coinbase Developer Platform and co-authored the x402 whitepaper. Heβll be introducing x402 protocol and explaining how micropayments can enable new models for API access and agentic payments.
Join us now for the fireside chat session!
Chapter 1: Setup Sprint
Before we start building, letβs make sure your CRE environment is set up correctly. Weβll follow the official setup instructions from cre.chain.link.
Step 1: Create a CRE Account
- Go to cre.chain.link
- Create an account or sign in
- Access the CRE platform dashboard

Step 2: Install the CRE CLI
The CRE CLI is essential for compiling and simulating workflows. It compiles your TypeScript/Go code into WebAssembly (WASM) binaries and allows you to test workflows locally before deployment.
Option 1) Automatic installation
The easiest way to install the CRE CLI is using the installation script:
macOs/Linux
curl -sSL https://cre.chain.link/install.sh | sh
Windows
irm https://cre.chain.link/install.ps1 | iex
Option 2) Manual Installation
If you prefer to install manually or the automatic installation doesnβt work for your environment, follow the installation instructions from the Official Chainlink Documentation for your platform:
Verify installation:
cre --version
Step 3: Authenticate with CRE CLI
Authenticate your CLI with your CRE account:
cre login
This will open a browser window for you to authenticate. Once authenticated, your CLI is ready to use.

Check your login status and account details with:
cre whoami
Whatβs Now Possible?
Now that your CRE environment is set up, you can:
-
Create new CRE projects/workflows: Start by running the
cre initcommand -
Compile workflows: The CRE CLI compiles your TypeScript/Go code into WASM binaries
-
Simulate workflows: Test your workflows locally with
cre workflow simulate -
Deploy workflows: Once ready, deploy to production (Early Access)
Chapter 2: The Mental Model
Before we dive into building, letβs establish the mental model for the two core technologies weβll be using: CRE and x402.
What is CRE?
Chainlink Runtime Environment (CRE) is the all-in-one orchestration layer that unlocks institutional-grade smart contracts β data-connected, compliance-ready, privacy-preserving, and interoperable across blockchains and existing systems.
Key Concepts
Workflows are the building blocks of CRE. As developers, we write Workflows using the CRE SDK (available in Go and TypeScript). We use the CRE CLI to compile these workflows into WebAssembly (WASM) binaries and simulate them locally before deployment. There are some major benefits to thios workflow-based design
-
A single workflow can be used with multiple chains, which saves a ton of dev time and infrastructure effort
-
Each workflow is orchestrated by a Workflow DON that monitors for triggers and coordinates execution
-
The workflow can invoke specialized Capability DONsβfor example, one that fetches offchain data or one that writes to a chain
-
During execution, each node in a DON performs the requested task independently
-
Their results are then cryptographically verified and aggregated via a Byzantine Fault Tolerant (BFT) consensus protocol, guaranteeing a single, correct, and consistent outcome
Capabilities are modular, decentralized services that performs a specific task, and each Capability is powered by its own independent Decentralized Oracle Network (DON), which is optimized for that specific task, ensuring security and reliable performance. Currently CRE has the following Capabilities:
- Triggers: Event sources that start your workflow executions.
- HTTP: Fetch and post data from external APIs with decentralized consensus.
- EVM Read & Write: Interact with smart contracts on EVM-compatible blockchains with decentralized consensus.
You can read more about all of these here.
What You Can Do with CRE
CRE workflows can do much more than what weβll cover in this workshop. Hereβs a comprehensive overview:
Blockchain Interactions:
- Read from blockchains: Query contract state, read events, fetch price data, check balances
- Write to blockchains: Execute transactions, update contract state, emit events, deploy contracts
- Multi-chain operations: Read from one chain, write to anotherβall in a single workflow
- Event-driven workflows: React to onchain events in real-time
External Data & APIs:
- Call APIs: Fetch data from any HTTP endpoint, authenticated APIs, webhooks
- Consensus aggregation: Multiple nodes fetch the same data, results are aggregated via BFT consensus
- Data transformation: Process and transform data before writing onchain
- Multi-source aggregation: Combine data from multiple APIs with consensus
Automation & Scheduling:
- Cron schedules: Run workflows on time-based schedules (every minute, hour, day, etc.)
- Event triggers: React to onchain events, HTTP requests, or custom triggers
- Conditional logic: Build complex workflows with conditional execution paths
Advanced Capabilities:
- Secret management: Securely store and access API keys, credentials
- Error handling: Robust error handling and retry logic
- Logging & monitoring: Built-in logging and observability
The Trigger-and-Callback Model
Workflows use a simple trigger-and-callback model:
- A Trigger: An event source that starts a workflow execution (e.g.,
cron.Trigger,http.Trigger,evm.LogTrigger) - A Callback: A function that contains your business logic
- The
cre.handler(): The glue that connects a trigger to a callback
cre.handler(
cronTrigger.trigger({ schedule: "0 */10 * * * *" }), // trigger fires every 10 minutes
onCronTrigger // your callback function
);
function onCronTrigger(runtime: Runtime<Config>): Record<string, never> {
// Your business logic here
return {};
}
Built-in Consensus
One of CREβs most powerful features is that every Capability execution automatically includes consensus. When your workflow invokes a Capability (like fetching data from an API or reading from a blockchain), multiple independent nodes perform the operation. Their results are validated and aggregated through BFT consensus, ensuring a single, verified outcome.
This means your entire workflowβnot just the onchain partsβbenefits from the same security and reliability guarantees as blockchain transactions.
Compiling and Simulating Workflows
The CRE CLI is your primary tool for developing workflows:
- Compile: Converts your TypeScript/Go code into WASM binaries
- Simulate: Runs workflows locally with real API calls and blockchain interactions
- Deploy: Deploys workflows to production DONs (Early Access)
# Compile and simulate a workflow
cre workflow simulate alerts
# The simulator will:
# 1. Compile your workflow to WASM
# 2. Prompt you to select a trigger
# 3. Execute the workflow locally
# 4. Make real calls to APIs and blockchains
What is x402?
x402 (Coinbaseβs standard for machine-to-machine payments) is a micropayment protocol that enables pay-per-call API access. Instead of subscriptions or API keys, users pay small amounts (like 0.01 USDC) for each API request.
x402 activates the old HTTP 402 βPayment Requiredβ status code, which has been marked as βreserved for future useβ since the early web. It never really took off because the web had no native, programmable money layer.
But now, with blockchains and stablecoins, the technology has finally caught up.
How x402 Works
x402 uses the HTTP 402 status code (Payment Required) to gate API access. Below is the detailed flow and we encourage you to read the docs as well:
βββββββββββ ββββββββββββ βββββββββββββββ ββββββββββββββββ
β Client βββββββββ>β Server βββββββββ>β Facilitator βββββββββ>β Blockchain β
βββββββββββ ββββββββββββ βββββββββββββββ ββββββββββββββββ
β β β β
β 1. GET /alerts β β β
βββββββββββββββββββββ>β β β
β β β β
β 2. 402 Payment β β β
β Required β β β
β<βββββββββββββββββββββ€ β β
β β β β
β 3. Create Payment β β β
β Authorization β β β
β (EIP-712) β β β
β β β β
β 4. Request + β β β
β X-PAYMENT β β β
βββββββββββββββββββββ>β β β
β β β β
β β 5. /verify β β
β ββββββββββββββββββββββ>β β
β β β β
β β β 6. Settle on-chain β
| | | (submit tx w/ sig |
| | | to USDC contract) |
β β βββββββββββββββββββββββ>β
β β β β
β β 7. Settlement β β
β β Confirmation β β
β β<ββββββββββββββββββββββ€ β
β β β β
β 8. 200 OK + β β β
β X-PAYMENT- β β β
β RESPONSE β β β
β<βββββββββββββββββββββ€ β β
β β β β
x402 Code Example
Hereβs how x402 works in code:
Server Side (Express.js):
import { paymentMiddleware } from "x402-express";
import { exact } from "x402/schemes";
// Protect endpoint with x402 payment
app.post(
"/alerts",
paymentMiddleware({
scheme: exact({
amount: "0.01",
currency: "USDC",
receiver: process.env.X402_RECEIVER_ADDRESS,
}),
facilitatorUrl: "https://x402.org/facilitator",
}),
(req, res) => {
// Payment validated, process request
res.json({ success: true, alertId: "..." });
}
);
Client Side:
import { x402Fetch } from "x402-fetch";
// x402-fetch automatically handles payment flow
const response = await x402Fetch("http://localhost:3000/alerts", {
method: "POST",
body: JSON.stringify({ asset: "BTC", condition: "gt", targetPrice: 60000 }),
headers: { "Content-Type": "application/json" },
wallet: {
privateKey: process.env.PRIVATE_KEY,
facilitatorUrl: "https://x402.org/facilitator",
},
});
const data = await response.json();
Key Benefits
- No API keys: Payment IS the authorization to use the
/alertsendpoint - Pay-per-use: No subscriptions, only pay for what you use
- Cryptographic proof: Payment is verified on-chain via EIP-712 signatures
- Instant settlement: Payments settle immediately on the blockchain
- Transparent pricing: Users see exactly what theyβre paying for
How They Work Together: Building a Price Alert System
Now letβs see how CRE and x402 work together to build our complete price alert system. This is the most important part: understanding the end-to-end flow.
System Architecture
βββββββββββββββ
β User β
β (via CLI) β
ββββββββ¬βββββββ
β
β 1. "Alert me when BTC > $60,000"
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β LLM Server β
β (Gemini AI + x402 Middleware) β
β β
β β’ Parses natural language β
β β’ Extracts: asset, condition, priceβ
β β’ x402 payment gate ($0.01 USDC) β
ββββββββ¬βββββββββββββββββββββββββββββββ
β
β 2. HTTP POST with alert data
β (after x402 payment)
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β CRE Workflow β
β (HTTP Trigger Handler) β
β β
β β’ Receives alert data β
β β’ Encodes as ABI parameters β
β β’ Generates CRE report β
β β’ Writes to RuleRegistry contract β
ββββββββ¬βββββββββββββββββββββββββββββββ
β
β 3. On-chain transaction
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β RuleRegistry Contract β
β (Base Sepolia) β
β β
β β’ Stores alert rules β
β β’ Rules: {id, asset, condition, β
β targetPrice, createdAt} β
βββββββββββββββββββββββββββββββββββββββ
β²
β
β 4. Cron trigger (hourly)
β
ββββββββ΄βββββββββββββββββββββββββββββββ
β CRE Workflow β
β (Cron Trigger Handler) β
β β
β β’ Reads prices from Chainlink feedsβ
β β’ Reads all rules from contract β
β β’ Evaluates conditions β
β β’ Sends Pushover notifications β
ββββββββ¬βββββββββββββββββββββββββββββββ
β
β 5. Push notification
β
βΌ
βββββββββββββββ
β User β
β (Mobile) β
βββββββββββββββ
The Complete Flow
Letβs walk through the complete flow step by step:
Phase 1: Alert Creation (x402 + CRE HTTP Trigger)
-
User sends natural language request
"Alert me when BTC is greater than 60000" -
LLM server parses intent
- Uses Gemini AI to extract structured data:
{ "asset": "BTC", "condition": "gt", "targetPriceUsd": 60000 }
- Uses Gemini AI to extract structured data:
- uses AI tool calling to invoke tool (function) to call the
create_price_alerttool. This tool invokescreatePriceAlert()which then contacts the x402 Facilitator using x402βs helper functions.
-
x402 payment gate
- Server responds with
402 Payment Required - Client (LLM server) creates EIP-712 payment authorization
- Client (LLM server) retries with
X-PAYMENTheader - Client (LLM server) validates payment via Facilitatorβs
/verifyendpoint - Payment settles on-chain ($0.01 USDC)
- Server responds with
-
CRE HTTP trigger receives data
- Client (LLM server) sends HTTP POST to CRE workflow
- Workflow receives alert data in callback
-
Workflow writes to blockchain
- Encodes alert data as ABI parameters
- Generates CRE report (cryptographically signed)
- Writes report to RuleRegistry contract
- Transaction confirmed on Base Sepolia
Phase 2: Price Monitoring (CRE Cron Trigger)
-
Cron trigger fires (every hour)
- CRE workflowβs cron handler executes
-
Fetch current prices
- Reads from Chainlink Price Feeds:
- BTC/USD feed
- ETH/USD feed
- LINK/USD feed
- Prices are consensus-verified (multiple nodes)
- Reads from Chainlink Price Feeds:
-
Read all rules from contract
- Calls
getAllRules()on RuleRegistry - Gets all active alert rules
- Calls
-
Evaluate conditions
- For each rule:
- Get current price for the asset
- Check if condition is met (gt, lt, gte, lte)
- Skip rules older than TTL (30 minutes)
- For each rule:
-
Send notifications
- If condition met: Send Pushover notification
- User receives push notification on mobile device
What Weβll Build
In this workshop, weβll build:
-
CRE Workflow with two triggers:
- HTTP trigger for receiving alert data
- Cron trigger for monitoring prices
-
Integration points:
- x402-protected server endpoint (
/alerts) - RuleRegistry smart contract
- Chainlink Price Feeds
- Pushover notifications
- x402-protected server endpoint (
-
Complete flow:
- User creates alert β Payment β On-chain storage β Automated monitoring β Notification

Chapter 3: Building Blocks - A Progressive Journey
Welcome! In this chapter, weβll build our CRE workflow step by step, starting from scratch. Weβll begin with the simplest Capability and gradually add more complexity. By the end, youβll have a complete understanding of how CRE workflows work.
Starting Your CRE Project
Creating a New Workflow
When you start a new CRE project, you use the cre init command. For this workshop, weβre working with an existing project, but letβs understand what a fresh CRE project looks like:
cre init
cd my-project
bun install --cwd ./my-workflow
cre workflow simulate my-workflow
Project Structure
After initialization, a CRE project has this structure:
my-project/
βββ project.yaml # Project-level settings (RPCs, targets)
βββ secrets.yaml # Secret variable mappings
βββ my-workflow/ # Your workflow directory
βββ workflow.yaml # Workflow-specific settings
βββ main.ts # Workflow entry point
βββ config.staging.json # Workflow configuration for simulation
βββ config.production.json # Workflow configuration for production
βββ package.json # Node.js dependencies
βββ tsconfig.json # TypeScript configuration
Key Files Explained
project.yaml - Defines project-wide settings (UPDATE with Base Sepolia details):
staging-settings:
rpcs:
- chain-name: ethereum-testnet-sepolia-base-1
url: https://sepolia.base.org
workflow.yaml - Maps targets to workflow files:
staging-settings:
user-workflow:
workflow-name: "my-workflow"
workflow-artifacts:
workflow-path: "./main.ts"
config-path: "./config.json"
secrets-path: "../secrets.yaml"
config.staging.json - Your workflowβs configuration, used for local simulations (loaded at runtime)
config.production.json - Your workflowβs configuration, for production usage (loaded at runtime)
main.ts - Your workflowβs entry point
The Runner Pattern
All CRE workflows use the Runner pattern to initialize and run workflows. This connects the trigger-and-callback model from Chapter 2:
export async function main() {
const runner = await Runner.newRunner<Config>();
await runner.run(initWorkflow);
}
The initWorkflow function returns an array of handlers, each connecting a trigger to a callback using cre.handler(). This is the foundation of every CRE workflow.
Step 1: Your First Workflow - Cron Trigger
Letβs start with the simplest capability: Cron. This will run on a schedule and just log a message.
Minimal Cron Example
Create main.ts:
import { cre, Runner, type Runtime } from "@chainlink/cre-sdk";
// Simple config type
type Config = {
schedule: string;
};
// Initialize workflow
const initWorkflow = (config: Config) => {
const cron = new cre.capabilities.CronCapability();
return [cre.handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)];
};
// Callback function
function onCronTrigger(runtime: Runtime<Config>): string {
runtime.log("Hello from CRE! Cron trigger fired!");
return "Success";
}
// Main entry point
export async function main() {
const runner = await Runner.newRunner<Config>();
await runner.run(initWorkflow);
}
main();
Create config.json:
{
"schedule": "0 */1 * * * *"
}
Testing Your First Workflow
cd my-project
cre workflow simulate my-workflow
You should see:
[USER LOG] Hello from CRE! Cron trigger fired!
Workflow Simulation Result:
"Hello world!"
[SIMULATION] Execution finished signal received
π Congratulations! Youβve created your first CRE workflow. Notice:
- The workflow compiled to WASM
- It ran locally but made real calls (if any)
- Multiple nodes would execute this in production with consensus
Step 2: Adding EVM Read - Fetching Prices
Now letβs add blockchain interaction. Weβll read from Chainlink Price Feeds to get current prices.
Reading from a Contract
Add this to your workflow:
import {
cre,
getNetwork,
encodeCallMsg,
bytesToHex,
Runtime,
Runner,
LAST_FINALIZED_BLOCK_NUMBER,
} from "@chainlink/cre-sdk";
import { encodeFunctionData, decodeFunctionResult, zeroAddress } from "viem";
type EvmConfig = {
chainSelectorName: string;
};
type Config = {
schedule: string;
evms: EvmConfig[];
};
// Chainlink Price Feed ABI (simplified)
const priceFeedAbi = [
{
name: "latestRoundData",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [
{ name: "roundId", type: "uint80" },
{ name: "answer", type: "int256" },
{ name: "startedAt", type: "uint256" },
{ name: "updatedAt", type: "uint256" },
{ name: "answeredInRound", type: "uint80" },
],
},
] as const;
function onCronTrigger(runtime: Runtime<Config>): bigint {
// Get the first EVM configuration from the list.
const evmConfig = runtime.config.evms[0];
// Get network configuration
const network = getNetwork({
chainFamily: "evm",
chainSelectorName: evmConfig.chainSelectorName,
isTestnet: true,
});
if (!network) {
throw new Error(`Unknown chain name: ${evmConfig.chainSelectorName}`);
}
// Create EVM client
const evmClient = new cre.capabilities.EVMClient(network.chainSelector.selector);
// Encode function call
const callData = encodeFunctionData({
abi: priceFeedAbi,
functionName: "latestRoundData",
args: [],
});
// Execute contract call (with consensus!)
const contractCall = evmClient
.callContract(runtime, {
call: encodeCallMsg({
from: zeroAddress,
to: "0x0FB99723Aee6f420beAD13e6bBB79b7E6F034298", // BTC/USD feed on Base Sepolia
data: callData,
}),
blockNumber: LAST_FINALIZED_BLOCK_NUMBER,
})
.result();
// Decode result
const priceData = decodeFunctionResult({
abi: priceFeedAbi,
functionName: "latestRoundData",
data: bytesToHex(contractCall.data),
}) as [bigint, bigint, bigint, bigint, bigint];
// Convert price (8 decimals)
const priceUsd = Number(priceData[1]) / 10 ** 8;
runtime.log(`BTC Price: $${priceUsd.toFixed(2)}`);
return priceData[1];
}
const initWorkflow = (config: Config) => {
const cron = new cre.capabilities.CronCapability();
return [cre.handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)];
};
export async function main() {
const runner = await Runner.newRunner<Config>();
await runner.run(initWorkflow);
}
main();
Understanding EVM Read
Key points:
getNetwork()- Gets chain configurationEVMClient- Client for blockchain interactionsencodeFunctionData()- Encodes Solidity function callscallContract()- Executes read (no gas needed)- Consensus: Multiple nodes read, results aggregated via BFT
Update Config
Make sure you added "ethereum-testnet-sepolia-base-1" to project.yaml already, as desribed above.
{
"schedule": "0 */1 * * * *",
"evms": [
{
"chainSelectorName": "ethereum-testnet-sepolia-base-1"
}
]
}
Test it: Run the simulation again. You should see the BTC price logged!
[USER LOG] BTC Price: $92325.42
Workflow Simulation Result:
9232542000000
[SIMULATION] Execution finished signal received
Reading Multiple Values from Contracts
In our actual workflow (cronCallback.ts), we also read rules from the RuleRegistry contract. Hereβs how we read multiple values:
// Read all rules from RuleRegistry (from cronCallback.ts)
const registryAbi = [
{
name: "getAllRules",
type: "function",
stateMutability: "view",
inputs: [],
outputs: [
{
name: "",
type: "tuple[]",
components: [
{ name: "id", type: "bytes32" },
{ name: "asset", type: "string" },
{ name: "condition", type: "string" },
{ name: "targetPriceUsd", type: "uint256" },
{ name: "createdAt", type: "uint256" },
],
},
],
},
] as const;
const callData = encodeFunctionData({
abi: registryAbi,
functionName: "getAllRules",
args: [],
});
const result = evmClient
.callContract(runtime, {
call: encodeCallMsg({
from: zeroAddress,
to: ruleRegistryAddress,
data: callData,
}),
})
.result();
const rules = decodeFunctionResult({
abi: registryAbi,
functionName: "getAllRules",
data: bytesToHex(result.data),
}) as Rule[];
This pattern allows you to read complex data structures (like arrays of structs) from contracts.
Step 3: Adding EVM Write - Storing Data On-Chain
Now letβs write data to a contract. This requires a two-step process: generate a report, then write it. This matches the pattern used in our httpCallback.ts file.
The Two-Step Write Pattern
import { encodeAbiParameters, parseAbiParameters } from "viem";
import { hexToBase64, bytesToHex, TxStatus, getNetwork } from "@chainlink/cre-sdk";
import { cre } from "@chainlink/cre-sdk";
// Example: Writing alert data to RuleRegistry (from httpCallback.ts)
function writeAlertToContract(
runtime: Runtime<Config>,
alert: { id: string; asset: string; condition: string; targetPriceUsd: bigint; createdAt: bigint }
): string {
// Get network and EVM client
const network = getNetwork({
chainFamily: "evm",
chainSelectorName: runtime.config.evms[0].chainSelectorName,
isTestnet: true,
});
const evmClient = new cre.capabilities.EVMClient(network.chainSelector.selector);
// Step 1: Ensure ID has 0x prefix for bytes32
const idBytes32 = alert.id.startsWith("0x") ? alert.id : `0x${alert.id}`;
// Step 2: Encode your data as ABI parameters
const reportData = encodeAbiParameters(
parseAbiParameters("bytes32 id, string asset, string condition, uint256 targetPriceUsd, uint256 createdAt"),
[idBytes32, alert.asset, alert.condition, alert.targetPriceUsd, alert.createdAt]
);
// Step 3: Generate CRE report (cryptographically signed)
const reportResponse = runtime
.report({
encodedPayload: hexToBase64(reportData),
encoderName: "evm",
signingAlgo: "ecdsa",
hashingAlgo: "keccak256",
})
.result();
// Step 4: Write report to contract
const writeResult = evmClient
.writeReport(runtime, {
receiver: runtime.config.evms[0].ruleRegistryAddress,
report: reportResponse,
gasConfig: {
gasLimit: runtime.config.evms[0].gasLimit,
},
})
.result();
if (writeResult.txStatus === TxStatus.SUCCESS) {
const txHash = bytesToHex(writeResult.txHash || new Uint8Array(32));
return txHash;
}
throw new Error(`Transaction failed: ${writeResult.txStatus}`);
}
Why Two Steps?
- Report Generation: Creates a cryptographically signed report that the contract can verify
- Write Report: Submits the signed report on-chain
This pattern ensures data integrity and allows the contract to verify the reportβs authenticity. Our RuleRegistry contract implements IReceiverTemplate to receive and verify these CRE reports.
Step 4: Adding HTTP Trigger - Receiving External Data
Now letβs add an HTTP trigger to receive data from external services. This matches the pattern used in our main.ts and httpCallback.ts files.
HTTP Trigger Setup
import { cre, Runner, type Runtime, type HTTPPayload, decodeJson } from "@chainlink/cre-sdk";
// Update initWorkflow to include HTTP trigger (from main.ts)
const initWorkflow = (config: Config) => {
const cron = new cre.capabilities.CronCapability();
const http = new cre.capabilities.HTTPCapability();
return [
cre.handler(cron.trigger({ schedule: config.schedule }), onCronTrigger),
cre.handler(
http.trigger({
authorizedKeys: [
{
type: "KEY_TYPE_ECDSA_EVM",
publicKey: "", // Empty string for demo, required for production
},
],
}),
onHttpTrigger
),
];
};
// HTTP trigger handler (from httpCallback.ts pattern)
function onHttpTrigger(runtime: Runtime<Config>, payload: HTTPPayload): string {
if (!payload.input || payload.input.length === 0) {
return "Empty request";
}
// Decode JSON payload
const inputData = decodeJson(payload.input);
runtime.log(`Received: ${JSON.stringify(inputData)}`);
// In our actual workflow, we would:
// 1. Extract alert data (id, asset, condition, targetPriceUsd, createdAt)
// 2. Encode as ABI parameters
// 3. Generate CRE report
// 4. Write to RuleRegistry contract
return "Success";
}
Testing HTTP Trigger
cre workflow simulate my-workflow
Select HTTP trigger (option 2):
π Workflow simulation ready. Please select a trigger:
1. cron-trigger@1.0.0 Trigger
2. http-trigger@1.0.0-alpha Trigger
Enter your choice (1-2): 2
And then paste the following JSON:
{ "id": "0x123...", "asset": "BTC", "condition": "gt", "targetPriceUsd": 60000, "createdAt": 1234567890 }
This matches the format our server sends to the CRE workflow.
You should see:
[USER LOG] Received: {"asset":"BTC","condition":"gt","createdAt":1234567890,"id":"0x123...","targetPriceUsd":60000}
Workflow Simulation Result:
"Success"
[SIMULATION] Execution finished signal received
Step 5: Adding HTTP Client - Making External Calls
Finally, letβs make HTTP requests to external APIs (like sending notifications). This matches the pattern used in our cronCallback.ts for Pushover notifications.
HTTP Client Example
import { cre, ok, consensusIdenticalAggregation, type HTTPSendRequester } from "@chainlink/cre-sdk";
const httpClient = new cre.capabilities.HTTPClient();
// Example: Sending Pushover notification (from cronCallback.ts)
const sendPushoverNotification =
(message: string, title: string, apiToken: string, userId: string) =>
(sendRequester: HTTPSendRequester, config: Config) => {
const payload = {
token: apiToken,
user: userId,
message: message,
title: title,
};
// Encode body as base64 (required by CRE HTTP Client)
const bodyBytes = new TextEncoder().encode(JSON.stringify(payload));
const body = Buffer.from(bodyBytes).toString("base64");
const req = {
url: "https://api.pushover.net/1/messages.json",
method: "POST" as const,
body,
headers: {
"Content-Type": "application/json",
},
cacheSettings: {
readFromCache: true,
maxAgeMs: 60000, // Cache for 1 minute
},
};
const resp = sendRequester.sendRequest(req).result();
if (!ok(resp)) {
throw new Error(`Request failed: ${resp.statusCode}`);
}
// Decode and verify response
const responseText = new TextDecoder().decode(resp.body);
const responseBody = JSON.parse(responseText);
if (responseBody.status !== 1) {
throw new Error(`API returned error: ${JSON.stringify(responseBody)}`);
}
return { statusCode: resp.statusCode };
};
// Use with consensus aggregation (from cronCallback.ts)
const result = httpClient
.sendRequest(
runtime,
sendPushoverNotification(
"BTC is now $60,123.45 (alert target: > $60,000.00)",
"CRE PRICE ALERT",
pushoverApiKey,
pushoverUserId
),
consensusIdenticalAggregation<{ statusCode: number }>()
)(runtime.config)
.result();
Consensus for HTTP Calls
Notice consensusIdenticalAggregation - multiple nodes make the same HTTP call, and results are aggregated via BFT consensus. This ensures reliability even if one API endpoint is down or returns incorrect data. Every HTTP call in CRE benefits from this built-in consensus mechanism.
Cache for HTTP Calls
Notice cacheSettings - By default, all nodes in the DON execute HTTP requests. For POST, PUT, PATCH, and DELETE operations, this would cause duplicate actions (like creating multiple resources or sending multiple emails). By utilizing cacheSettings, we can ensure that only one node makes the call and prevent duplicate requests. The first node makes the HTTP request and stores the response in the cache. Other nodes will first check the cache before attempting to make the HTTP request on their own. All nodes will still participate in consensus, even if the cache is used.
Step 6: Connecting x402 Server to CRE
Now that we understand CRE capabilities, letβs see how the x402-protected server integrates with our CRE workflow. This connects the concepts from Chapter 2 with the CRE building blocks we just learned.
Server-Side: x402 Payment Protection
Our server uses x402 middleware to protect the /alerts endpoint (from server/src/server.ts):
import { paymentMiddleware } from "x402-express";
import { createHash } from "node:crypto";
// x402 payment middleware (from server.ts)
app.use(
paymentMiddleware(
process.env.X402_RECEIVER_ADDRESS, // Payment recipient
{
"POST /alerts": {
price: "$0.01",
network: "base-sepolia",
config: {
description: "Create a crypto price alert",
},
},
},
{ url: "https://x402.org/facilitator" }
)
);
// /alerts endpoint handler (from server.ts)
app.post("/alerts", (req, res) => {
// Payment already validated by middleware!
// Create alert with deterministic ID
const alertData = {
asset: req.body.asset,
condition: req.body.condition,
targetPriceUsd: req.body.targetPriceUsd,
createdAt: Math.floor(Date.now() / 1000),
};
const id = createHash("sha256").update(JSON.stringify(alertData)).digest("hex");
const alert = { id, ...alertData };
// Output CRE workflow payload (for manual trigger in demo)
console.log("\nCRE Workflow Payload (copy for HTTP trigger):");
console.log(JSON.stringify(alert));
res.json({ success: true, alert });
});
Client-Side: x402 Payment Handling
The client uses x402-fetch to automatically handle the payment flow (from server/src/x402Client.ts):
import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";
// Wrap fetch with x402 payment handling
const account = privateKeyToAccount(process.env.AGENT_WALLET_PRIVATE_KEY);
const fetchWithPayment = wrapFetchWithPayment(fetch, account);
// Make request - x402-fetch automatically handles payment
const response = await fetchWithPayment("http://localhost:3000/alerts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
asset: "BTC",
condition: "gt",
targetPriceUsd: 60000,
}),
});
// Payment settled! Get transaction hash from header
const paymentResponse = response.headers.get("x-payment-response");
const data = await response.json();
The Complete Flow
Hereβs how x402 and CRE work together:
- Client β Server: User sends request to
/alertsendpoint - x402 Payment: Server responds with
402 Payment Required, client pays $0.01 USDC - Server β CRE: Server outputs CRE payload JSON (in demo, you manually trigger CRE)
- CRE HTTP Trigger: Receives alert data via HTTP trigger (Step 4)
- CRE EVM Write: Writes alert to RuleRegistry contract (Step 3)
- CRE Cron Trigger: Periodically checks prices (Step 1)
- CRE EVM Read: Reads prices and rules (Step 2)
- CRE HTTP Client: Sends notifications when conditions met (Step 5)
Key Integration Points
- x402 protects the API: Payment is the authorization (no API keys needed)
- Server creates alert data: After payment, server generates the alert payload
- CRE receives via HTTP Trigger: The alert data is sent to CREβs HTTP trigger
- CRE writes on-chain: The workflow writes the alert to the RuleRegistry contract
- CRE monitors automatically: Cron trigger checks prices and sends notifications
This demonstrates how x402 (micropayments) and CRE (decentralized workflows) work together to create a complete, payment-protected, on-chain automation system.
Putting It All Together
Now you understand the complete picture:
- Cron Trigger - Scheduled execution
- EVM Read - Reading from blockchains (with consensus)
- EVM Write - Writing to blockchains (two-step pattern)
- HTTP Trigger - Receiving external data
- HTTP Client - Making external API calls (with consensus)
- x402 Integration - Payment-protected API that triggers CRE workflows
In the next chapter, weβll set up and run the complete price alert system using all these Capabilities together!
Chapter 4: Hands-On Exercise
Now that you understand the building blocks from Chapter 3, letβs build our complete price alert workflow. This workflow combines all the Capabilities we learned:
- HTTP Trigger - Receives alert data from the server
- EVM Write - Writes alerts to the RuleRegistry contract
- Cron Trigger - Monitors prices on a schedule
- EVM Read - Reads prices from Chainlink feeds and rules from the contract
- HTTP Client - Sends notifications when conditions are met
Our Workflow Structure
The complete workflow consists of:
main.ts- Initializes both HTTP and Cron triggers (see Chapter 3 for the pattern)httpCallback.ts- Handles HTTP trigger, decodes alert data, and writes to RuleRegistry using the two-step write patterncronCallback.ts- Handles cron trigger, reads prices and rules, evaluates conditions, and sends notificationstypes.ts- TypeScript type definitions for Config, Rule, PriceData, etc.config.staging.json- Workflow configuration
All the code patterns you need are covered in Chapter 3. Here, weβll focus on setting up and running the complete system.
Complete Demo Sequence
Step 0: Deploy RuleRegistry Contract
Deploy contracts/RuleRegistry.sol to Base Sepolia. Set USDC token address in constructor (Base Sepolia USDC: 0x036CbD53842c5426634e7929541eC2318f3dCF7e).
You can use this Remix IDE link for this.
π‘ Note the deployed contract address, we will use this later.
If you do not have testnet tokens or canβt deploy the contract for other reasons, you may use this one 0x9B9fC1EeF6BFC76CD07501Ae81b66f24fAB322B1. However, keep in mind that this demo contract may be populated with multiple alerts from other developers.
Step 1: Clone and Install
git clone https://github.com/smartcontractkit/x402-cre-price-alerts.git
cd x402-cre-price-alerts
npm install
This will install dependencies for both the server and cre/alerts workspaces. The cre/alerts post install script will automatically run bunx cre-setup.
Step 2: Configure Environment Variables
Create a .env file in the project root (workspace root):
cp .env.example .env
β οΈ The workspace setup uses a single
.envfile at the project root. Bothserverandcreworkspaces can access environment variables from this root.envfile. You may specify the.envfile when using the CRE CLI by passing the--envargument.
Edit .env with your values:
# Server Configuration
PORT=3000
X402_RECEIVER_ADDRESS=0x... # Your wallet or RuleRegistry address
X402_FACILITATOR_URL=https://x402.org/facilitator
GEMINI_API_KEY=your_gemini_api_key
AGENT_WALLET_PRIVATE_KEY=your_private_key # Must have USDC on Base Sepolia
# CRE Configuration
CRE_ETH_PRIVATE_KEY=your_eth_private_key
CRE_TARGET=staging-settings
PUSHOVER_USER_KEY_VAR=your_pushover_user_key
PUSHOVER_API_KEY_VAR=your_pushover_api_key
Environment Variables Explained:
PORT: Server port (default: 3000)X402_RECEIVER_ADDRESS: Address that receives x402 payments (Can be an EOA or Deployed RuleRegistry contract/contracts)X402_FACILITATOR_URL: x402 facilitator endpoint (default: https://x402.org/facilitator)GEMINI_API_KEY: Your Gemini API key for natural language processingAGENT_WALLET_PRIVATE_KEY: Private key of wallet used to make x402 payments (must have USDC on Base Sepolia). You can use the same key as your CRE PK.CRE_ETH_PRIVATE_KEY: ETH private key used for local simulation of EVM Write capabilityCRE_TARGET: Target profile for CLI commandsPUSHOVER_USER_KEY_VAR: Your Pushover user keyPUSHOVER_API_KEY_VAR: Your Pushover API key
Step 3: Configure CRE Workflow
Edit cre/alerts/config.staging.json for staging/testing (add your ruleRegistryAddress):
{
"schedule": "0 0 * * * *",
"ruleTTL": 1800,
"publicKey": "",
"evms": [
{
"ruleRegistryAddress": "your_deployed_rule_registry",
"chainSelectorName": "ethereum-testnet-sepolia-base-1",
"gasLimit": "1000000",
"dataFeeds": {
"BTC": "0x0FB99723Aee6f420beAD13e6bBB79b7E6F034298",
"ETH": "0x4aDC67696bA383F43DD60A9e78F2C97Fbbfc7cb1",
"LINK": "0xb113F5A928BCfF189C998ab20d753a47F9dE5A61"
}
}
]
}
Configuration Fields:
schedule: Cron expression for price checks (default: hourly -"0 0 * * * *")ruleTTL: Time to live of a created rule (30 minutes by default. Older rules will not receive alerts.)publicKey: Public key used to verify incoming HTTP Trigger requests. This field is empty for this demo. However, it is required when the full HTTP Trigger is implemented for production. See Line 64 ofserver/src/server.ts.ruleRegistryAddress: Address of your deployed RuleRegistry contractchainSelectorName: Chain selector for Base Sepolia ("ethereum-testnet-sepolia-base-1") See the chain selector referencegasLimit: Gas limit for on-chain writesdataFeeds: Chainlink price feed addresses for BTC, ETH, LINK on Base Sepolia. You can find Base Sepolia Price Feed addresses here.
Important:
- Replace
your_deployed_rule_registrywith the address from Step 0 - If you were unable to deploy, you may use
0x9B9fC1EeF6BFC76CD07501Ae81b66f24fAB322B1. Be aware that this demo contract may be populated with multiple alerts from other developers.
Step 4: Start the Server
From the repository root:
npm run dev:server
The server will start on http://localhost:3000 (or your configured PORT).
You should see:
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Unified API Server
Port: 3000 | Payment: $0.01 USDC
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Server ready
http://localhost:3000
POST /chat (natural language, no payment)
POST /alerts (requires x402 payment)
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Interactive Chat Enabled
Type your message and press Enter (type 'exit' or 'quit' to leave)
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Live Demo
Step 5: User Creates Alert (Natural Language)
Option A: Interactive Chat (Recommended)
In the server terminal, type:
> Create an alert when BTC is greater than 60000
[!NOTE]: this is deliberately set to below current market price so that the trigger fires.
Type exit or quit to disable chat (server continues running).
Option B: Direct API Call
Alternatively, send a POST request to the /chat endpoint:
curl -X POST http://localhost:3000/chat \
-H "Content-Type: application/json" \
-d '{"message":"Create an alert when BTC is greater than 60000"}'
What happens:
- Gemini AI interprets your message
- Extracts alert parameters (asset, condition, target price)
- Creates a paid alert via
/alertsendpoint with x402 payment - Returns alert details and payment transaction hash
Supported Assets: BTC, ETH, LINK only
Step 6: Copy Alert JSON Payload
From the server console output, copy the CRE payload JSON. It will look like:
CRE Workflow Payload (copy for HTTP trigger):
{"id":"42d2ea846d5b5e0ba439b68f8835188e023b74454c504df80ae0a0eb329eccd6","asset":"BTC","condition":"gt","targetPriceUsd":60000,"createdAt":1765324585}
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step 7: Register the Alert in RuleRegistry ( using CRE HTTP Trigger)
Open a new terminal window so you can run commands.
Simulate the CRE HTTP Trigger to write the alert on-chain:
cd cre
cre workflow simulate alerts --env ../.env --broadcast
When prompted:
- Select HTTP trigger (option 2)
- Paste the JSON payload from Step 6
- The workflow will write the alert to the RuleRegistry contract on-chain
Example output:
2025-12-10T17:21:20Z [SIMULATION] Simulator Initialized
2025-12-10T17:21:20Z [SIMULATION] Running trigger trigger=http-trigger@1.0.0-alpha
2025-12-10T17:21:21Z [USER LOG] ββββββββββββββββββββββββββββββββββββββββββββββββββββ
2025-12-10T17:21:21Z [USER LOG] CRE Workflow: HTTP Trigger
2025-12-10T17:21:21Z [USER LOG] ββββββββββββββββββββββββββββββββββββββββββββββββββββ
2025-12-10T17:21:21Z [USER LOG] [Step 1] Received alert data: {"asset":"BTC","condition":"gt","createdAt":1765416045,"id":"1b381294cc2a9771743d80a3d11380cf4e377b64802a1c728776d2f6defed3cc","targetPriceUsd":10}
2025-12-10T17:21:21Z [USER LOG] [Step 2] Encoding alert data for on-chain write...
2025-12-10T17:21:21Z [USER LOG] [Step 3] Generating CRE report...
2025-12-10T17:21:21Z [USER LOG] [Step 4] Writing to RuleRegistry contract: 0x9B9fC1EeF6BFC76CD07501Ae81b66f24fAB322B1
2025-12-10T17:21:22Z [USER LOG] [Step 5] [SUCCESS] Transaction successful: 0x66b8b811d6902bfcfc5e5b4890602ec2620084f8dcc2a02e49a2dddc8d9f1a8a
2025-12-10T17:21:22Z [USER LOG] ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Workflow Simulation Result:
"0x66b8b811d6902bfcfc5e5b4890602ec2620084f8dcc2a02e49a2dddc8d9f1a8a"
2025-12-10T17:21:22Z [SIMULATION] Execution finished signal received
2025-12-10T17:21:22Z [SIMULATION] Skipping WorkflowEngineV2
Step 8: Simulate Cron Check On Price Conditions (using CRE Cron Trigger)
Execute the CRE CRON Trigger to check prices and send notifications:
cre workflow simulate alerts --env ../.env
When prompted:
- Select Cron trigger (option 1)
- The workflow will:
- Fetch current prices for BTC, ETH, LINK from Chainlink feeds
- Check all rules stored on-chain
- Send Pushover notifications when conditions are met
Example output:
2025-12-10T17:22:53Z [SIMULATION] Simulator Initialized
2025-12-10T17:22:53Z [SIMULATION] Running trigger trigger=cron-trigger@1.0.0
2025-12-10T17:22:53Z [USER LOG] ββββββββββββββββββββββββββββββββββββββββββββββββββββ
2025-12-10T17:22:53Z [USER LOG] CRE Workflow: Cron Trigger
2025-12-10T17:22:53Z [USER LOG] ββββββββββββββββββββββββββββββββββββββββββββββββββββ
2025-12-10T17:22:53Z [USER LOG] [Step 1] Fetching price data from Chainlink feeds...
2025-12-10T17:22:53Z [USER LOG] β’ BTC: $90855.76
2025-12-10T17:22:53Z [USER LOG] β’ ETH: $3253.52
2025-12-10T17:22:53Z [USER LOG] β’ LINK: $13.76
2025-12-10T17:22:53Z [USER LOG] [Step 2] Found 2 rules on-chain
2025-12-10T17:22:53Z [USER LOG] [Step 3] Checking 2 rules...
2025-12-10T17:22:53Z [USER LOG] [Rule 1] [SUCCESS] Condition met: BTC $90855 gt $5
2025-12-10T17:22:53Z [USER LOG] -> Pushover notification sent (Status: 200)
2025-12-10T17:22:53Z [USER LOG] [Rule 2] [SUCCESS] Condition met: BTC $90855 gt $10
2025-12-10T17:22:54Z [USER LOG] -> Pushover notification sent (Status: 200)
2025-12-10T17:22:54Z [USER LOG] [Step 4] [SUCCESS] Complete: 2 notification(s) sent
2025-12-10T17:22:54Z [USER LOG] ββββββββββββββββββββββββββββββββββββββββββββββββββββ
Workflow Simulation Result:
"Processed 2 rules, sent 2 notifications"
2025-12-10T17:22:54Z [SIMULATION] Execution finished signal received
2025-12-10T17:22:54Z [SIMULATION] Skipping WorkflowEngineV2
β οΈ The cron trigger runs automatically on the configured schedule once deployed. The frequency of the CRON trigger is set within
cre/alerts/config.staging.json, as well as the Rule TTL.
Step 9: Review the Pushover notification on your device
π Check your phone!
You should receive a push notification when the price condition is met.
Step 10: (Optional) View balance of x402 Receiver
If you set your x402 Receiver in the root .env file (X402_RECEIVER_ADDRESS) to your RuleRegistry contract, call the getUSDCBalance() function to see the USDC received from x402 payments.
Step 11: (Optional) Withdraw USDC tokens from RuleRegistry
If you set your x402 Receiver in the root .env file (X402_RECEIVER_ADDRESS) to your RuleRegistry contract, call the withdrawUSDC(address, amount) function to withdraw USDC received from x402 payments. Reminder, USDC uses 6 decimal places!
Success Checklist
- β Contract deployed to Base Sepolia
- β Server running and configured
- β Alert created via natural language chat
- β x402 payment processed ($0.01 USDC)
- β Alert written to contract (transaction hash verified)
- β Cron trigger executed
- β Push notification received π―
Troubleshooting
No notification?
- Verify Pushover credentials in
.env(PUSHOVER_USER_KEY_VARandPUSHOVER_API_KEY_VAR) - Check rule was written to contract (verify transaction hash on Base Sepolia Explorer)
- Current price must meet condition (e.g., BTC > $60,000 in example)
- Rule TTL is 30 minutes (1800 seconds) - create new alert if expired
Workflow errors?
- Verify contract address in
config.staging.jsonmatches deployed address - Check wallet has ETH for gas (simulation mode)
- Verify CRE CLI authenticated:
cre auth status - Check all environment variables are set correctly
- Verify
CRE_TARGET=staging-settingsmatches your workflow target configuration
x402 payment issues?
- Verify
AGENT_WALLET_PRIVATE_KEYhas USDC on Base Sepolia - Check
X402_RECEIVER_ADDRESSis set correctly - Verify facilitator URL is accessible
Gemini API issues?
- Rate limit exceeded error? Make sure you set up billing. Free tier for this masterclass is fine, but Google sometimes still requires your credit card is connected to the Gemini API key.
Questions & Answers
This is Q&A time! Feel free to ask questions about:
- CRE workflows and capabilities
- x402 integration patterns
- Building with these technologies
- Best practices and tips
Important Resources
- This Book: https://smartcontractkit.github.io/x402-cre-price-alerts/ - Reference guide for this workshop
- Repository: https://github.com/smartcontractkit/x402-cre-price-alerts - Complete code examples
- CRE Documentation: docs.chain.link/cre - Comprehensive CRE guides
- x402 Documentation: docs.cdp.coinbase.com/x402 - x402 protocol documentation
Feedback Survey
Your feedback is invaluable in helping us improve future masterclasses and better serve the developer community. Please take a moment to share your thoughts here.
Bonus: Prompting in TypeScript
Effective prompting is the art of communicating with AI assistants to generate high-quality code. When building CRE workflows in TypeScript, well-crafted prompts can help you:
- Generate boilerplate code for triggers, callbacks, and handlers
- Implement complex blockchain interactions with proper error handling
- Create type-safe configurations and data structures
- Debug workflow issues with targeted questions
- Learn CRE SDK patterns through examples
Use the structured prompt by copying and pasting it into your AI tool of choice (for example OpenAIβs ChatGPT or Anthropicβs Claude).
Make sure to enter your specific requirements at the end between the <user_prompt> and </user_prompt> tags
<system_context>
You are an advanced assistant specialized in building, simulating, and operating workflows on the Chainlink Runtime Environment (CRE).
You deeply understand:
- CRE architecture: workflows compiled to WASM and executed by Workflow DONs and Capability DONs
- The trigger-and-callback model:
- Triggers (cron schedules, HTTP triggers, EVM logs, manual execution, etc.)
- Callbacks that implement business logic
- `cre.handler(trigger, callback)` as the atom of execution
- The CRE SDKs in TypeScript and Go (primary examples in TypeScript, with notes for Go when relevant)
- CRE capabilities, including:
- EVM read/write via `EVMClient`
- HTTP integrations via `HTTPClient` and the sendRequest / sendReport patterns
- Cron triggers via `CronCapability`
- Secrets via `runtime.getSecret`
- Report generation via `runtime.report` and consensus-based execution
- Simulation and deployment flows:
- Local simulation via the CRE CLI
- Deployment to Workflow DONs
- Monitoring, logs, events, and metrics via the CRE UI
</system_context>
<behavior_guidelines>
- Respond in a clear, concise, and production-oriented manner
- Prefer TypeScript examples using `@chainlink/cre-sdk`
- When relevant, briefly explain how the same pattern would look in Go
- Use current CRE patterns and best practices from official docs
- Show complete, minimal-working examples (imports, config, and main entrypoint when useful)
- Use official CRE terminology: Workflow, Trigger, Callback, Runtime, Capability, Workflow DON, Capability DON, Consensus
- Emphasize safety, determinism, idempotency, and good observability in workflow design
- Make explicit where consensus and cryptographic guarantees are applied
- When there are multiple patterns (e.g., HTTP request vs. report-based integrations), explain the trade-offs and recommend one
- Suggest tests or simulations the user can run with the CRE CLI
- Structure answers with sections like:
- Overview / Architecture
- Config & Types
- Workflow code
- Capabilities / integrations
- Simulation / deployment notes
- If requirements are ambiguous, prefer proposing a concrete, reasonable design over asking many clarifying questions
</behavior_guidelines>
<cre_standards>
- Workflows:
- Follow the trigger-and-callback pattern:
- Define one or more Capabilities (e.g. `new cre.capabilities.CronCapability()`)
- Register them with `cre.handler(trigger, callback)`
- Return an array of handlers from an `initWorkflow(config)` function
- Callbacks should:
- Accept `runtime: Runtime` and an optional payload
- Instantiate Capability clients inside the callback
- Call Capabilities using the SDKβs `.result()` pattern to obtain consensus-verified results
- Return a serializable result (often `Record<string, unknown>` or a dedicated result type)
- Runtime usage:
- Use `runtime.config` for user configuration
- Use `runtime.log()` for structured logging
- Use `runtime.report()` for operations that require a signed, consensus-verified payload (onchain or offchain)
- Prefer stateless callbacks and avoid relying on mutable global state
- Secrets:
- Declare secrets in a `secrets.yaml` file and reference it in `workflow.yaml` via `workflow-artifacts.secrets-path`
- Fetch secrets with `runtime.getSecret({ id }).result()`
- Always fetch secrets sequentially (the WASM host does not support parallel secret fetches)
- Never log raw secrets; only log derived or masked information
- TypeScript project layout (example):
- `my-cre-project/`
- `project.yaml` β project and deployment configuration
- `workflows/` β workflow entrypoints (`main.ts`)
- `config/` β JSON or TS config (e.g. `config.json`)
- `contracts/abi/` β ABI & type definitions for consumer contracts
- `utils/` β shared helpers for encoding, parsing, etc.
- `secrets.yaml` β secret names (no secret values)
- Keep the workflow entrypoint (e.g. `main.ts`) small, delegating logic to modules under `utils/` when it grows
- Configuration:
- Define schemas (e.g. using `zod`) to validate config files
- Provide a `Config` type alias inferred from the schema
- Treat configuration as immutable inside the workflow
- EVM integration:
- Use `getNetwork({ chainFamily: "evm", chainSelectorName, isTestnet })` to resolve chain selector and network info
- Instantiate `new cre.capabilities.EVMClient(selector)` to read or write onchain
- For reads:
- Use viem helpers such as `encodeFunctionData`, `decodeFunctionResult`, and `encodeCallMsg`
- Use `LAST_FINALIZED_BLOCK_NUMBER` where appropriate for strong finality guarantees
- For writes:
- ABI-encode data using viem (`encodeAbiParameters`, `parseAbiParameters`, or `encodeFunctionData`)
- Generate signed reports via `runtime.report({ encodedPayload, encoderName: "evm", signingAlgo: "ecdsa", hashingAlgo: "keccak256" })`
- Submit via `evmClient.writeReport(runtime, { receiver, report, gasConfig }).result()`
- Check `txStatus` (e.g., SUCCESS / REVERTED / FATAL) and handle each case explicitly
- HTTP integration:
- Use `new cre.capabilities.HTTPClient()` with `sendRequest` or `sendReport`:
- `httpClient.sendRequest(runtime, (sendRequester) => { ... }, consensusStrategy)()`
- For report submissions to HTTP APIs:
- Use `sendRequester.sendReport(report, formatFn).result()`
- Implement `formatFn(report)` to return the expected HTTP payload (JSON body, headers, method, path)
- Configure cache or deduplication settings to avoid duplicate submissions from multiple nodes
- On the API side, encourage deduplication keyed on a hash of the raw report or workflow execution ID
- Cron triggers:
- Use `new cre.capabilities.CronCapability().trigger({ schedule })`
- Support both 5-field and 6-field cron expressions
- Prefer explicit schedules and document timezone assumptions (e.g. using `TZ` in the deployment environment)
- Error handling:
- Treat all external calls (EVM, HTTP, secrets) as potentially failing
- Check status codes / result structures and return meaningful error messages
- Prefer explicit error branches over silent failures
- Observability:
- Use `runtime.log()` for:
- Trigger activations
- External requests (EVM or HTTP) and key parameters
- Decisions, branches, and error conditions
- Final results returned by callbacks
- Keep logs structured and concise to make CRE UI and CLI logs useful
</cre_standards>
<cre_tooling_and_cli>
- CLI basics:
- `cre workflow simulate <name> --target <settings>` β simulate workflows locally using `workflow.yaml`
- `cre workflow deploy <name> --target <settings>` β deploy workflows to a Workflow DON
- `cre workflow logs <name> --target <settings>` β view workflow logs
- `cre workflow events <name> --target <settings>` β view events and execution history
- `cre account link-key --target <settings>` β link a wallet key to your CRE organization
- `cre account list-key` β list linked workflow owner addresses
- Project + config:
- Use `project.yaml` to define environments, workflow artifacts, and deployment targets
- Use `workflow.yaml` to describe workflow-specific artifacts (workflow path, config path, secrets path)
- Keep environment-specific details (RPC URLs, chain selectors, gateway URLs) in config / target settings rather than hard-coding them
- Monitoring:
- Use the CRE UI to inspect workflow executions: - Filter by workflow - Drill into individual execution IDs - Inspect logs, events, and report payloads
</cre_tooling_and_cli>
<naming_conventions>
- Files and modules:
- Use descriptive names: `price-oracle-aggregator`, `workflow/main.ts`, `onchain-write.ts`, `http-trigger.ts`
- Organize ABIs under `contracts/abi/` with clear names (e.g. `PriceFeedConsumer.ts`)
- Types and interfaces:
- Use PascalCase for TypeScript types and interfaces: `Config`, `PriceUpdate`, `WorkflowResult`
- Use descriptive types for payloads and results: `CronPayload`, `HttpInput`, `OnchainWriteResult`
- Functions:
- Use lowerCamelCase for functions: `initWorkflow`, `onCronTrigger`, `fetchPrices`, `writeDataOnchain`
- Name callbacks for triggers as `on<TriggerName>` (e.g. `onCronTrigger`, `onHttpTrigger`, `onLogTrigger`)
- Config:
- Use self-describing config fields: `schedule`, `chainSelectorName`, `consumerAddress`, `gasLimit`, `apiUrl`, `maxRetries`, `networkName`
- Avoid unclear abbreviations that obscure intent
</naming_conventions>
<workflow_patterns>
- General callback pattern:
- Receive `runtime` and an optional payload
- Initialize clients locally (e.g. `new cre.capabilities.EVMClient(...)`, `new cre.capabilities.HTTPClient()`)
- Perform Capability calls, in parallel when safe and supported
- Await consensus-verified results using `.result()`
- Transform results into a small, well-defined return object
- Cron + EVM read + HTTP write:
1. Cron trigger fires on schedule
2. Callback:
- Reads data from one or more EVM contracts via `EVMClient`
- Optionally fetches offchain data via `HTTPClient`
- Applies business logic, aggregation, or validation
- Writes a report onchain or submits a signed report to an HTTP API
- Report-based workflows:
- Use `runtime.report()` for any operation that needs a signed, consensus-verified payload
- Always specify:
- `encoderName` (e.g. `"evm"`)
- `signingAlgo` (e.g. `"ecdsa"`)
- `hashingAlgo` (e.g. `"keccak256"`)
- For HTTP targets, define dedicated formatters that turn a report into the exact HTTP request your API expects
- HTTP-triggered workflows:
- Design HTTP workflows so that triggers authenticate callers (e.g., authorized keys, JWTs)
- Treat input payloads as untrusted; validate and sanitize before processing
- Consider idempotency keys or deduplication when callers may retry
- Secrets and external APIs:
- Fetch secrets via `runtime.getSecret().result()` and inject them into HTTP headers or payloads
- Keep API keys out of logs and return values
- Use configuration to control which environments use which secrets
- Simulation and deployment:
- Provide a `main()` entrypoint that:
- Creates a `Runner` instance (e.g. `Runner.newRunner()`)
- Calls `runner.run(initWorkflow)` with config
- Use the same `initWorkflow` function for both simulation and production deployments to avoid configuration drift
</workflow_patterns>
<llm_and_ai_guidelines>
- When asked to design an architecture:
- Propose specific triggers, capabilities, and callback signatures
- Describe data flow between offchain APIs, EVM contracts, and reports
- Call out where consensus and cryptographic guarantees apply
- When asked to generate a new workflow:
- Provide:
- Config schema (e.g. with `zod`)
- TypeScript `Config` type
- `initWorkflow(config)` function returning handlers
- One or more callbacks using CRE capabilities
- `main()` function to run the workflow
- Keep examples runnable and consistent with `@chainlink/cre-sdk` APIs
- When asked to extend an existing workflow:
- Respect existing config patterns and file layout
- Reuse existing utilities where possible
- Explain exactly how new Capabilities or triggers integrate with the existing ones
- When asked to adapt patterns to Go:
- Maintain the same high-level design:
- Triggers, callbacks, runtime, capabilities
- Show idiomatic Go code:
- Package structure
- Strong typing and explicit error handling
- Call out any significant differences from the TypeScript SDK
- Always:
- Prefer deterministic logic and explicit error handling
- Avoid side effects outside the CRE workflow runtime
- Be explicit about what is simulation-only versus what requires deployment to a DON
</llm_and_ai_guidelines>
<observability_and_operations>
- Logging:
- Use `runtime.log()` for:
- Trigger firing details (including payload and schedule)
- External requests and key parameters
- Decisions, branches, and error paths
- Final result or report payload summaries
- Keep logs human-readable but structured enough to search and filter
- Failure handling:
- For EVM writes:
- Check `txStatus`
- On SUCCESS: log tx hash and return success result
- On REVERTED: log error message and propagate a useful error
- On FATAL: log and surface a clear error with context
- For HTTP:
- Check `statusCode`
- Decode and log response body (or portion of it) for non-2xx responses
- Convert technical errors into actionable messages
- Scaling patterns:
- For workflows with high frequency or multiple chains/APIs: - Consider caching and throttling strategies - Use cron schedules and configuration to control frequency - Design workflows to be idempotent wherever possible
</observability_and_operations>
<user_prompt>
Describe in detail what you need the assistant to build or explain.
You can:
- Ask for a brand new workflow:
- "Create a TypeScript CRE workflow that uses a cron trigger to read prices from two EVM chains, compare them to an offchain API price, and write an aggregated result to my consumer contract."
- Extend an existing workflow:
- "Here is my current CRE workflow that reads onchain data. Extend it so it also fetches data from an HTTP API, combines both, and submits a signed report to an HTTP endpoint."
- Request a Go version:
- "Take this TypeScript workflow and write an equivalent workflow using the Go SDK, preserving the same behavior and configuration."
Write your specific request here.
</user_prompt>
Bonus: Prompting in Go
Effective prompting is the art of communicating with AI assistants to generate high-quality code. When building CRE workflows in Go, well-crafted prompts can help you:
- Generate boilerplate code for triggers, callbacks, and handlers
- Implement complex blockchain interactions with proper error handling
- Create type-safe configurations and data structures
- Debug workflow issues with targeted questions
- Learn CRE SDK patterns through examples
Use the structured prompt by copying and pasting it into your AI tool of choice (for example OpenAIβs ChatGPT or Anthropicβs Claude).
Make sure to enter your specific requirements at the end between the <user_prompt> and </user_prompt> tags
<system_context>
You are an advanced assistant specialized in building, testing, and operating workflows on the Chainlink Runtime Environment (CRE) using Golang.
You deeply understand:
- CRE architecture:
- Workflows compiled to WASM and executed by Workflow DONs and Capability DONs
- Consensus, aggregation, and cryptographic guarantees for offchain and onchain actions
- The Go SDK (`github.com/smartcontractkit/cre-sdk-go`):
- `cre.Workflow`, `cre.Handler`, `cre.Runtime`, `cre.NodeRuntime`
- Triggers (cron, HTTP, EVM log, etc.) and their callback signatures
- Capabilities: `evm.Client`, `http.Client`, cron, secrets, etc.
- Promise model (`cre.Promise`, `.Await()`, `cre.Then`, `cre.ThenPromise`) and WASM execution semantics :contentReference[oaicite:0]{index=0}
- Simulation and deployment flows:
- Local simulation using `cre workflow simulate`
- Deployment to Workflow DONs with `cre workflow deploy`
- Monitoring, logs, and events via the CRE UI
- Go-specific patterns:
- Idiomatic package layout, error handling, and testing with `go test`
- Using generated EVM bindings for reads, writes, and event logs :contentReference[oaicite:1]{index=1}
</system_context>
<behavior_guidelines>
- Respond in a clear, practical, and production-oriented style
- Prefer Golang examples using `cre-sdk-go`:
- `github.com/smartcontractkit/cre-sdk-go/cre`
- `github.com/smartcontractkit/cre-sdk-go/capabilities/cron`
- `github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm`
- `github.com/smartcontractkit/cre-sdk-go/capabilities/http`
- When helpful, briefly reference equivalent patterns in the TypeScript SDK (but keep code in Go)
- Use current CRE patterns and best practices from official docs
- Provide complete, minimal-working examples:
- Imports
- Config struct
- `InitWorkflow` and callbacks
- Optional `main` with `Runner`
- Use official CRE terminology: Workflow, Trigger, Callback, Runtime, Capability, Workflow DON, Capability DON, Consensus
- Emphasize:
- Determinism and idempotency
- Explicit error handling
- Observability (`runtime.Logger()`)
- When multiple patterns exist (e.g., `http.SendRequest` vs `http.SendReport`), explain trade-offs and recommend one
- Suggest concrete simulations and CLI commands the user can run
- Prefer proposing a clear design over asking many clarifying questions when requirements are ambiguous
</behavior_guidelines>
<cre_standards>
- Workflow structure (Go):
- Define a `Config` struct (and nested configs as needed)
- Implement:
- `func InitWorkflow(config *Config, logger *slog.Logger, secretsProvider cre.SecretsProvider) (cre.Workflow[*Config], error)`
- `func Main() (cre.Entrypoint, error)` (or equivalent) for CRE to discover entrypoints :contentReference[oaicite:2]{index=2}
- Return a `cre.Workflow[*Config]{ ... }` containing one or more `cre.Handler` values
- Each `cre.Handler` is composed of:
- A trigger (e.g. `cron.Trigger(...)`, `http.Trigger(...)`, `evmLogTrigger`, etc.)
- A callback function with a signature that matches the trigger payload type
- Callback pattern:
- Signature style:
- For cron: `func onCron(config *Config, runtime cre.Runtime, payload *cron.Payload) (*MyResult, error)`
- For HTTP: `func onHTTP(config *Config, runtime cre.Runtime, payload *http.Payload) (*MyResult, error)`
- For EVM log:`func onLog(config *Config, runtime cre.Runtime, log *evm.Log) (*MyResult, error)`
- Inside callbacks:
- Retrieve logger: `logger := runtime.Logger()`
- Instantiate clients: `evmClient := &evm.Client{ ChainSelector: config.ChainSelector }`, `httpClient := &http.Client{}`
- Use promises and `.Await()` to get consensus-verified results
- Return strongly-typed results (`*MyResult`) with clear fields
- Runtime and secrets:
- Use `runtime.Logger()` for structured logging (via `log/slog`)
- Use `runtime.GenerateReport()` for operations needing signed, consensus-verified payloads (onchain or HTTP) :contentReference[oaicite:3]{index=3}
- Use secrets via `cre.SecretsProvider` and/or `runtime.GetSecret()`:
- Fetch secrets with `.Await()` and handle errors
- Never log raw secret values
- Support both local simulation (env / `.env`) and deployed workflows (Vault DON) transparently :contentReference[oaicite:4]{index=4}
- Project layout (example):
- `go.mod` β module definition
- `workflows/`
- `my-workflow/`
- `workflow.go` β workflow logic (`InitWorkflow`, callbacks)
- `main.go` β entrypoint & runner wiring
- `workflow.yaml` β workflow configuration
- `config/` β JSON/YAML config files
- `contracts/evm/src/`
- `abi/` β ABI files (`*.abi`)
- `generated/` β generated Go bindings
- `secrets.yaml` β logical secret names (no secret values)
- `internal/` or `pkg/` β shared utilities, types, helpers
- Configuration:
- Use JSON/YAML mapped to Go structs:
- `type Config struct { ... }`
- Keep config immutable in the workflow
- Prefer descriptive field names: `Schedule`, `ChainSelector`, `ProxyAddress`, `ApiURL`, `MaxRetries`
- EVM integration (Go):
- Use `evm.Client` via `github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm`
- Prefer generated bindings for contracts:
- Generate bindings with `cre generate-bindings evm` from ABIs in `contracts/evm/src/abi/` :contentReference[oaicite:5]{index=5}
- For reads:
- Use generated client methods that return promises
- Call `.Await()` with a block number (e.g. finalized `-3`)
- For writes:
- Use generated `WriteReportFrom*` helpers (e.g. `WriteReportFromPriceData`)
- These helpers:
- Generate report
- Send to EVM
- Return a promise that resolves with tx details
- HTTP integration (Go):
- Use `http.Client` via `github.com/smartcontractkit/cre-sdk-go/capabilities/http`
- For simple use cases:
- Use high-level `SendRequest` helpers:
- Provide method, URL, headers, and body
- Await result, check `StatusCode`, parse body
- For report-based flows:
- Generate report with `runtime.GenerateReport()`
- Use `http.Client.SendReport(...)` or `cre.RunInNodeMode` with a node-level function and consensus strategy
- Provide formatting function that converts a report into an HTTP request (method, headers, body) :contentReference[oaicite:6]{index=6}
- Cron triggers:
- Use `cron.Trigger("*/5 * * * *")` or similar schedule strings
- Be explicit about timing and expectations
- Consider chain finality and data freshness when combining cron with EVM reads
- Error handling and style:
- Always check error returns from `.Await()` calls
- Wrap errors with context using `fmt.Errorf("...: %w", err)`
- Avoid panics in workflow logic
- Prefer small functions with clear responsibilities
</cre_standards>
<cre_tooling_and_cli>
- Core CLI commands:
- `cre workflow simulate <name> --target <settings>` β simulate workflows locally
- `cre workflow deploy <name> --target <settings>` β deploy workflow to a Workflow DON
- `cre workflow logs <name> --target <settings>` β view logs
- `cre workflow events <name> --target <settings>` β view events and execution history
- `cre account link-key --target <settings>` β link an EOA/multi-sig as workflow owner
- `cre account list-key` β list linked workflow owners
- Project & workflow config:
- `project.yaml`:
- Defines shared project configuration, environments, and targets
- `workflow.yaml`:
- Points to Go workflow entrypoint, config file, and secrets file
- Defines workflow name per environment
- Simulation & deployment flow:
- Start with `cre workflow simulate` while iterating on code
- Once stable, use `cre workflow deploy` (requires Early Access)
- Use the CRE UI (`cre.chain.link`) to: - Inspect workflow - Drill into execution IDs - View Events and Logs panes
</cre_tooling_and_cli>
<naming_conventions>
- Packages and files:
- Lowercase, short but descriptive package names: `calculator`, `pricefeed`, `webhookhandler`
- File names:
- `workflow.go` for main workflow logic
- `main.go` for entrypoint and runner
- `bindings.go` usually generated under `contracts/evm/src/generated/<contract>`
- `_test.go` suffix for test files (e.g. `workflow_test.go`)
- Types and structs:
- PascalCase for types: `Config`, `EvmConfig`, `MyResult`, `PriceData`
- Prefer explicit type names: `CronPayload`, `HTTPConfig`, `EvmNetworkConfig`
- Functions:
- MixedCase / lowerCamelCase:
- `InitWorkflow`, `Main`, `onCronTrigger`, `onHTTPTrigger`, `onUserAdded`
- For callbacks:
- Use `on<TriggerName>` naming pattern to signal purpose
- Config fields:
- Prefer descriptive names: `Schedule`, `ChainSelector`, `ConsumerAddress`, `ProxyAddress`, `ApiURL`, `MaxRetries`
- Keep environment-specific values (e.g. RPC URLs) in config or target settings, not hard-coded
</naming_conventions>
<workflow_patterns>
- General callback pattern:
- Retrieve logger: `logger := runtime.Logger()`
- Log inputs and key decisions
- Instantiate Capability clients (`evm.Client`, `http.Client`)
- Call Capability methods to produce `cre.Promise[...]`
- Chain or await promises using:
- `p.Await()` for simple flows
- `cre.Then` / `cre.ThenPromise` for more complex chaining :contentReference[oaicite:7]{index=7}
- Return a small, well-defined result struct
- Cron + EVM read + HTTP write (example pattern):
1. Cron trigger fires based on `Schedule` in config.
2. Callback:
- Reads data from an EVM contract via generated bindings and `evm.Client`.
- Optionally fetches offchain data via `http.Client`.
- Aggregates and validates data.
- Generates a report and either:
- Writes onchain using EVM write bindings, or
- Submits report to an HTTP endpoint via `SendReport`.
- Onchain writes with generated bindings:
- Use helper methods like `WriteReportFromPriceData` that:
- Encode data struct
- Call `runtime.GenerateReport()`
- Submit via EVM Capability and return transaction info
- Check transaction result and log tx hash. :contentReference[oaicite:8]{index=8}
- Event-driven workflows with EVM logs:
- Use generated bindingsβ `LogTrigger...` helpers to create triggers for specific events.
- In `InitWorkflow`, create trigger via the binding and hook to handler via `cre.Handler`.
- In the handler:
- Reconstruct contract binding (or reuse it).
- Decode log to typed struct with `Codec.Decode<EventName>`.
- Implement business logic (e.g. react to user actions, query other contracts, etc.). :contentReference[oaicite:9]{index=9}
- HTTP-triggered workflows:
- Use HTTP trigger Capability to receive external requests.
- Treat payload as untrusted:
- Validate JSON schema, parameters, and auth.
- Execute business logic:
- Optionally make EVM / HTTP calls
- Return structured response to the caller
- Secrets:
- For simulation:
- Provide secrets via environment variables / `.env`.
- For deployed workflows:
- Declare secret names in `secrets.yaml`
- Store real values in Vault DON via `cre secrets` commands
- In workflow code:
- Fetch secrets via `runtime.GetSecret(namespace, id).Await()`
- Inject into HTTP headers, API keys, etc., without logging raw values :contentReference[oaicite:10]{index=10}
- Promise & consensus patterns:
- Use `cre.RunInNodeMode` when each node must perform its own action and you provide an aggregation function.
- Use provided consensus strategies such as `cre.ConsensusIdenticalAggregation[T]` when you expect identical results. :contentReference[oaicite:11]{index=11}
- Simulation & testing:
- Simulation:
- Use `cre workflow simulate <workflow-name> --target <settings>` to run end-to-end tests locally.
- Go tests: - Use `_mock.go` bindings for EVM mocking in `*_test.go` files. - Mock HTTP via stubbed clients. - Focus tests on callback functions (e.g. `onCronTrigger`) rather than CLI wiring. :contentReference[oaicite:12]{index=12}
</workflow_patterns>
<llm_and_ai_guidelines>
- When asked to design an architecture:
- Propose:
- `Config` structure
- Triggers and capabilities
- Callback signatures
- Sketch dataflow:
- Offchain APIs β workflow β onchain writes
- EVM events β workflow β downstream actions
- Call out where consensus, finality, and cryptographic guarantees apply.
- When asked to generate a new workflow (Go):
- Provide:
- `Config` struct (and nested structs)
- `InitWorkflow` implementation returning `cre.Workflow[*Config]`
- One or more callback functions with full signatures and imports
- Optional `main.go` with runner wiring
- Example `workflow.yaml` snippet or guidance on config
- Keep code idiomatic Go and consistent with `cre-sdk-go`.
- When asked to extend an existing workflow:
- Respect existing patterns (config, file layout, logging style).
- Reuse existing helpers (e.g. for HTTP calls, EVM bindings).
- Carefully integrate new triggers or Capabilities without breaking current behavior.
- When asked to translate from TypeScript to Go:
- Maintain high-level design:
- Same triggers, capabilities, and flow
- Use Go SDK primitives:
- `cre.Workflow`, `cre.Handler`, `cre.Promise`, `.Await()`
- Explain key differences (e.g. promise handling, types, logging).
- Always:
- Prefer safe, deterministic logic and explicit error handling.
- Avoid undeclared side effects outside CRE runtime.
- Clearly distinguish: - What works in local simulation only - What requires deployment to a Workflow DON and onchain contracts.
</llm_and_ai_guidelines>
<observability_and_operations>
- Logging:
- Use `runtime.Logger()` to create structured logs:
- Log inputs, key decisions, and outputs
- Log external calls (URLs, addresses, chain selectors) without leaking secrets
- Log errors with enough context to debug
- Keep log messages concise but searchable (e.g. include workflow name, trigger type, and execution IDs when possible).
- Failure handling:
- For EVM writes via bindings:
- Check returned error and log tx hash or error details.
- For demo code, return user-friendly error messages.
- For HTTP:
- Check `StatusCode` and parse response body on non-2xx
- Surface actionable errors in callback return values
- Scaling & reliability:
- Control trigger frequency via config (cron schedule, rate limits).
- Avoid non-idempotent logic where callers may retry.
- Consider:
- Timeouts and retries for HTTP
- Reasonable expectations for EVM finality
- Use CLI and CRE UI to monitor: - Error rates - Latency - Frequency of executions
</observability_and_operations>
<user_prompt>
Describe in detail what you need the assistant to build or explain, using Golang and the CRE Go SDK.
Examples:
- "Create a Go CRE workflow that uses a cron trigger to read prices from an EVM contract via generated bindings, compares them to an offchain API price, and writes an aggregated result to a consumer contract."
- "Extend this existing Go workflow so it also exposes an HTTP trigger that lets me query the latest aggregated value, with proper input validation and logging."
- "Take this TypeScript CRE workflow and translate it into Go, preserving the same triggers, capabilities, and overall behavior."
Write your specific request here.
</user_prompt>