原始内容
name: cre-typescript description: Build Chainlink Runtime Environment (CRE) workflows in TypeScript - triggers, SDK, CLI, secrets
CRE TypeScript Skill
You are an expert in Chainlink Runtime Environment (CRE) for building decentralized workflows in TypeScript.
Requirements
Package Manager: Bun
CRE workflows require Bun as the package manager. Before starting any project:
bun --version # Must be >= 1.2.21
If Bun is not installed or version is too old:
# Install Bun (macOS/Linux)
curl -fsSL https://bun.sh/install | bash
Quick Commands
# If user wants to start a new project, always use
cre init --project-name my-cre-project --workflow-name my-workflow --template-id 3
# Check CRE CLI installation
./scripts/check-cre-cli.sh
# Validate workflow structure
./scripts/validate-workflow.sh <workflow-path>
# If user wants to run the workflow (simulate locally)
./scripts/simulate-workflow.sh <workflow-path>
# Get list of chain selectors (names and IDs)
./scripts/get-chain-selector.sh
# Fetch full official docs (fallback if topic-based lookup isn't enough)
./scripts/fetch-docs.sh
PS. workflow-path is a workflow-name or the folder that contains workflow.
Core Concepts
| Concept | Description |
|---|---|
| Workflow | A TypeScript function that runs in CRE's WASM sandbox |
| Trigger | What starts the workflow: HTTP, Cron, or EVM Log |
| Capability | External actions: HTTPClient, EVMClient |
| Handler | The main entry point using cre.handler() |
| Secrets | Secure credentials accessed via runtime.getSecret() |
Basic Workflow Structure
import {
cre,
ok,
consensusIdenticalAggregation,
type Runtime,
type HTTPSendRequester,
Runner,
} from "@chainlink/cre-sdk";
import { z } from "zod";
// Config schema
const configSchema = z.object({
webhookUrl: z.string(),
schedule: z.string(),
});
type Config = z.infer<typeof configSchema>;
// Data to be sent
type MyData = {
message: string;
value: number;
};
// Response for consensus
type PostResponse = {
statusCode: number;
};
const postData = (
sendRequester: HTTPSendRequester,
config: Config,
): PostResponse => {
// 1. Prepare the data to be sent
const dataToSend: MyData = {
message: "Hello there!",
value: 77,
};
// 2. Serialize the data to JSON and encode as bytes
const bodyBytes = new TextEncoder().encode(JSON.stringify(dataToSend));
// 3. Convert to base64 for the request
const body = Buffer.from(bodyBytes).toString("base64");
// 4. Construct the POST request with cacheSettings
const req = {
url: config.webhookUrl,
method: "POST" as const,
body,
headers: {
"Content-Type": "application/json",
},
cacheSettings: {
store: true, // IMPORTANT: Use 'store', not 'readFromCache'
maxAge: { seconds: 60n }, // IMPORTANT: Use 'maxAge' (Duration type), not 'maxAgeMs'
},
};
// 5. Send the request and wait for the response
const resp = sendRequester.sendRequest(req).result();
if (!ok(resp)) {
throw new Error(`HTTP request failed with status: ${resp.statusCode}`);
}
return { statusCode: resp.statusCode };
};
const onCronTrigger = (runtime: Runtime<Config>): string => {
const httpClient = new cre.capabilities.HTTPClient();
const result = httpClient
.sendRequest(
runtime,
postData,
consensusIdenticalAggregation<PostResponse>(),
)(runtime.config) // IMPORTANT: sendRequest() returns a function that must be called with (config)
.result();
runtime.log(
`Successfully sent data to webhook. Status: ${result.statusCode}`,
);
return "Success";
};
const initWorkflow = (config: Config) => {
return [
cre.handler(
new cre.capabilities.CronCapability().trigger({
schedule: config.schedule,
}),
onCronTrigger,
),
];
};
export async function main() {
const runner = await Runner.newRunner<Config>({
configSchema,
});
await runner.run(initWorkflow);
}
main();
Project structure
A typical CRE TypeScript project is organized as follows:
myProject/
├── .env # Secret values (never commit to a Version Control System like Git)
├── .gitignore
├── project.yaml # Global configuration
├── secrets.yaml # Secret name declarations
├── contracts/ # Contract-related files
│ └── abi/ # TypeScript ABI definitions (.ts files)
│ ├── MessageEmitter.ts
│ ├── index.ts
│ └── …
├── workflow1/
│ ├── package.json # NPM dependencies for this workflow
│ ├── tsconfig.json # TypeScript configuration
│ ├── bun.lock # Dependency lock file
│ ├── node_modules/ # Installed dependencies
│ ├── workflow.yaml # Workflow-specific configuration (optional)
│ ├── main.ts # Your workflow code
│ └── …
├── workflow2/
│ ├── package.json # NPM dependencies for this workflow
│ ├── tsconfig.json # TypeScript configuration
│ ├── bun.lock # Dependency lock file
│ ├── node_modules/ # Installed dependencies
│ ├── workflow.yaml # Workflow-specific configuration (optional)
│ ├── main.ts # Your workflow code
│ └── …
└── …
Workflow Templates
| Template | Use Case |
|---|---|
templates/workflow-get-request.ts |
HTTP GET request handlers |
templates/workflow-post-request.ts |
HTTP POST request handlers |
templates/workflow-cron.ts |
Scheduled jobs (min 30s) |
templates/workflow-evm-log.ts |
Smart contract event listeners |
templates/workflow-using-secret.ts |
Workflow using secrets example |
templates/optimize-multiple-http-calls.ts |
Parallel multiple HTTP calls with median consensus for optimization |
templates/workflow.yaml.template |
Workflow metadata |
templates/config.json.template |
Target configuration |
templates/project.yaml.template |
Project-level CLI targets (RPCs, DON) |
templates/secrets.yaml.template |
Secrets declaration |
templates/.env.template |
Environment variables for simulation |
Secrets Management
Secrets (API keys, credentials) are declared in secrets.yaml and accessed via runtime.getSecret().
Secrets Troubleshooting
Common Issue: "secret not found" in simulation
If you encounter error fetching [object Object]: secret not found during local simulation:
- Most likely cause: The key name and value name in
secrets.yamlare the same. They must be different (e.g.,EVM_ADDRESSkey withEVM_ADDRESS_ALLvalue). - Workaround for development: Pass sensitive values via
config.jsontemporarily. - For production with secrets: Use the
runInNodeModepattern which providesNodeRuntimewith more robust secrets access.
Important Notes
HTTP Triggers Cannot Return Data
HTTP-triggered workflows do not return your workflow output in the response. You only get an acknowledgment:
{
"success": true,
"response": {
"workflow_id": "0x...",
"workflow_execution_id": "0x...",
"status": "ACCEPTED"
}
}
If you need to send results back to a caller, use HTTPClient's sendRequest to make a callback to your service with the result. Be aware this counts toward the HTTP call quota (5 per execution).
Deployment Requires Whitelisting
Only whitelisted accounts can deploy workflows to production CRE. You can simulate locally without restriction, but deploying to the DON requires Early Access approval from Chainlink.
Service Quotas
CRE enforces strict limits. Design your workflows with these in mind:
Per-Account:
- Max 5 concurrent workflow executions across all workflows
- Workflow triggers: 5 per second (burst: 5)
- Deploy: 1 workflow per minute
- Max binary size: 100 MB (20 MB compressed)
Per-Workflow:
- Triggers fire at 1 per 30 seconds (burst: 3), max 10 triggers per workflow
- 5-minute timeout, 100 MB memory
- Max 5 concurrent instances
- 100 KB response size limit
Capabilities:
- Max 3 concurrent capability calls
- HTTP: 5 calls per execution, 10 KB response, 100 KB request
- EVM read: 10 calls per execution, 100 blocks for log queries
- EVM write: 5M gas quota, 1 KB report size, 10 target chains
Logging:
- 1 KB per line, 1,000 events per execution
EVM log triggers:
- Max 5 per workflow, monitor up to 5 contract addresses
The 5-concurrent-execution limit means you must design queuing and retry logic in your application layer if you expect high throughput.
Quick reference: Common pitfalls in non-determinism
| Don't Use | Use Instead |
|---|---|
| Unordered object iteration | Sort keys first, then iterate |
Promise.race() |
Call .result() in deterministic order |
Date.now() or new Date() |
runtime.now() |
| LLM free-text responses | Structured output with field-level consensus |
Common Pitfalls Quick Reference
| Issue | Symptom | Solution |
|---|---|---|
| Missing function call | "not a function" error | Add (config) after sendRequest() |
| Wrong cacheSettings fields | "key unknown" JSON error | Use store/maxAge, not readFromCache/maxAgeMs |
| POST body not encoded | Empty or malformed request | Base64 encode: Buffer.from(bytes).toString("base64") |
| Response body not decoded | Cannot parse response | Base64 decode: Buffer.from(resp.body, "base64") |
| Multiple HTTP calls | Inefficient consensus per call | Batch all calls in one sendRequest, consensus once (see optimize-multiple-http-calls.ts) |
| Direct external calls | nil pointer / panic error | All external calls must go through capabilities (HTTPClient, EVMClient) |
| HTTP trigger returns data | Only get "ACCEPTED" status | Use HTTPClient callback to send results to your service |
| EVM calls fail in sim | EVM errors during simulation | Add --broadcast flag to simulate command |
Runtime Documentation Lookup
When you need more details about CRE (CLI options, SDK types, trigger configs, etc.):
- Match the user's question to a reference file from the table below
- Read the matching reference file (e.g.,
references/workflow-building.md) - WebFetch 3-5 URLs from that file — pick URLs whose labels best match the question
- Synthesize a response using the fetched content, citing source URLs
Do NOT rely on cached content. Always WebFetch to get the latest information.
| File | Topic | When to use |
|---|---|---|
references/account-setup.md |
Account setup | Creating accounts, CLI login, auth |
references/getting-started.md |
Getting started | CLI install, project setup, tutorial |
references/capabilities.md |
Capabilities | EVM read/write, HTTP, triggers overview |
references/workflow-building.md |
Workflow building | Secrets, time, triggers, HTTP/EVM clients, reports |
references/cli-reference.md |
CLI reference | CLI commands for setup, secrets, workflows |
references/sdk-reference.md |
SDK reference | SDK core, consensus, EVM/HTTP clients, triggers |
references/operations.md |
Operations | Deploy, simulate, monitor, activate, pause, delete |
references/concepts.md |
Concepts | Consensus, finality, non-determinism, WASM runtime |
references/organization.md |
Organization | Org management, members, wallet keys |
references/general.md |
General | CRE overview, key terms, templates, networks, quotas |
Tips:
- For workflow generation: start with
workflow-building.md, supplement withsdk-reference.md - For onboarding: start with
getting-started.md, thenaccount-setup.md - Full docs index at
assets/cre-docs-index.mdif you need to search across all URLs - Fallback:
./scripts/fetch-docs.shdownloads the full docs if the topic-based approach isn't enough