Part 1: Project Setup & Simulation

In this first part, you'll go from an empty directory to a fully initialized CRE project and simulate your first, minimal workflow. The goal is to get a quick "win" and familiarize yourself with the core project structure and development loop.

What you'll do

  • Initialize a new project using cre init.
  • Explore the generated project structure and workflow code.
  • Configure your workflow for simulation.
  • Run your first local simulation with cre workflow simulate.

Prerequisites

Before you begin, ensure you have the following:

  • CRE CLI: See the Installation Guide for details.
  • CRE account & authentication: You must have a CRE account and be logged in with the CLI. See Create your account and Log in with the CLI for instructions.
  • Bun: You must have Bun version 1.2.21 or higher installed. Check your version with bun --version. See Install Bun for instructions.
  • Funded Sepolia Account: An account with Sepolia ETH to pay for transaction gas fees. Go to faucets.chain.link to get some Sepolia ETH.

Step 1: Verify your authentication

Before initializing your project, verify that you're logged in to the CRE CLI:

cre whoami

Expected output:

  • If you're authenticated, you'll see your account details:

    Account details retrieved:
    
    Email:           [email protected]
    Organization ID: org_AbCdEfGhIjKlMnOp
    
  • If you're not logged in, you'll receive an error message prompting you to run cre login:

    Error: failed to attach credentials: failed to load credentials: you are not logged in, try running cre login
    

    Run the login command and follow the prompts:

    cre login
    

    See Logging in with the CLI for detailed instructions if you need help.

Step 2: Initialize your project

The CRE CLI provides an init command to scaffold a new project. It's an interactive process that will ask you for a project name, a workflow template, and a name for your first workflow.

  1. In your terminal, navigate to a parent directory where you want your new CRE project to live.

  2. Run the init command. The CLI will guide you through the setup process:

    cre init
    
  3. Provide the following details when prompted:

    • Project name: onchain-calculator
    • Language: Select Typescript and press Enter.
    • Pick a workflow template: Use the arrow keys to select Helloworld: Typescript Hello World example and press Enter. We are starting from scratch to learn all the configuration steps.
    • Workflow name: my-calculator-workflow

The CLI will then create a new onchain-calculator directory and initialize your first workflow within it.

Step 3: Explore the generated files

The init command creates a directory with a standard structure and generates your first workflow code. Let's explore what was created.

Project structure

Your new project has the following structure:

onchain-calculator/
ā”œā”€ā”€ my-calculator-workflow/
│   ā”œā”€ā”€ config.production.json
│   ā”œā”€ā”€ config.staging.json
│   ā”œā”€ā”€ main.ts
│   ā”œā”€ā”€ package.json
│   ā”œā”€ā”€ README.md
│   ā”œā”€ā”€ tsconfig.json
│   └── workflow.yaml
ā”œā”€ā”€ .env
ā”œā”€ā”€ .gitignore
ā”œā”€ā”€ project.yaml
└── secrets.yaml
  • Project: The top-level directory (e.g., onchain-calculator/).
    • It contains project-wide files like project.yaml, which holds shared configurations for all workflows within the project.
    • A project can contain multiple workflows, each in its own subdirectory.
  • Workflow: A subdirectory (e.g., my-calculator-workflow/) that contains the source code and configuration for a single workflow.
    • Each workflow has its own package.json and tsconfig.json, making workflows independent and portable.

Here are the key files and their roles:

File
Role
project.yamlThe global configuration file. Contains shared settings like RPC URLs for different environments (called targets).
secrets.yamlStores references to secrets.
.envStores secrets and environment variables, like your private key. Never commit this file to version control.
.gitignorePrevents sensitive files (like .env) from being committed to version control.
my-calculator-workflow/A directory containing the source code and configuration for a single workflow.
ā”œā”€ā”€ main.tsThe heart of your workflow where you'll write your TypeScript logic. This is the entry point that gets compiled to WASM.
ā”œā”€ā”€ package.jsonManages dependencies for this specific workflow. Each workflow can have its own dependencies, allowing for flexibility and isolation.
ā”œā”€ā”€ tsconfig.jsonTypeScript configuration for this workflow. Controls how TypeScript compiles your code.
ā”œā”€ā”€ workflow.yamlContains configurations specific to this workflow, such as its name and workflow artifacts (entry point path, config file path, secrets file path). The workflow-artifacts section tells the CLI where to find your workflow's files.
ā”œā”€ā”€ config.staging.jsonContains parameters for your workflow when using the staging-settings target, which can be accessed in your code via the config object.
└── config.production.jsonContains parameters for your workflow when using the production-settings target, which can be accessed in your code via the config object.

You don't need to understand every file and directory right now—this guide is designed to introduce each concept when you actually need it. For now, let's look at the workflow code that was generated.

The workflow code

The init command created a main.ts file with a minimal "Hello World!" workflow. Let's examine and understand this code.

This code defines a Config type to hold parameters from our config file. It then configures a cron trigger to run on the schedule provided in the config, and registers a simple callback that logs a message.

Open onchain-calculator/my-calculator-workflow/main.ts to see its contents:

onchain-calculator/my-calculator-workflow/main.ts
Typescript
1 import { cre, Runner, type Runtime } from "@chainlink/cre-sdk"
2 ​
3 type Config = {
4 schedule: string
5 }
6 ​
7 const onCronTrigger = (runtime: Runtime<Config>): string => {
8 runtime.log("Hello world! Workflow triggered.")
9 return "Hello world!"
10 }
11 ​
12 const initWorkflow = (config: Config) => {
13 const cron = new cre.capabilities.CronCapability()
14 ​
15 return [cre.handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)]
16 }
17 ​
18 export async function main() {
19 const runner = await Runner.newRunner<Config>()
20 await runner.run(initWorkflow)
21 }
22 ​
23 main()
24 ​

Key components:

  • Config type: Defines the shape of your configuration. TypeScript ensures type safety throughout your workflow.
  • onCronTrigger: The callback function that executes when the cron trigger fires. It receives the runtime object and returns a string result. This is where you write your business logic.
  • initWorkflow: Creates the workflow by registering handlers (trigger-callback pairs). This is where you define what events your workflow responds to.
  • main: The entry point that creates a Runner, passes your config type, and runs the workflow.

Step 4: Configure your workflow

Now that you've explored the generated files, let's configure your workflow for simulation. You'll need to adjust a few configuration files.

Review config files

The CLI generates separate config files for each target environment. Your workflow code can access the parameters from whichever config file corresponds to the target you're using.

The cre init command already created these files with a schedule. Open my-calculator-workflow/config.staging.json to see its contents:

{
  "schedule": "*/30 * * * * *"
}

This cron schedule means "run every 30 seconds". No changes are needed here for now. Since we'll be using the staging-settings target for this guide, you only need to be aware of config.staging.json. The config.production.json file contains the same schedule but is used when targeting the production environment.

Review workflow.yaml

This file tells the CLI where to find your workflow files. The cre init command created this file with default values. Open my-calculator-workflow/workflow.yaml and you'll see:

# ==========================================================================
staging-settings:
  user-workflow:
    workflow-name: "my-calculator-workflow-staging"
  workflow-artifacts:
    workflow-path: "./main.ts"
    config-path: "./config.staging.json"
    secrets-path: ""
# ==========================================================================
production-settings:
  user-workflow:
    workflow-name: "my-calculator-workflow-production"
  workflow-artifacts:
    workflow-path: "./main.ts"
    config-path: "./config.production.json"
    secrets-path: ""

Understanding the sections:

  • Target names (staging-settings, production-settings): These are environment configuration sets. The cre init command pre-populates your workflow.yaml with these two common targets as a starting point, but you can name targets whatever you want (e.g., dev, test, prod). When running CLI commands, you specify which target to use with the --target flag.
  • workflow-name: Each target has its own workflow name with a suffix (e.g., -staging, -production). This allows you to deploy the same workflow to different environments with distinct identities.
  • workflow-path: "./main.ts": The entry point for your TypeScript code
  • config-path: Each target points to its own config file (config.staging.json or config.production.json)
  • secrets-path: "": The location of your secrets file (empty for now; you'll learn about secrets in more advanced guides)

For this guide, we'll use staging-settings for local simulation. When you run cre workflow simulate my-calculator-workflow --target staging-settings, the CLI reads the configuration from the staging-settings section of this file.

Set up your private key

The simulator requires a private key to initialize its environment, even for workflows that don't interact with the blockchain yet. This key will be used in later parts of this guide to read from and send transactions to the Sepolia testnet.

  1. Open the .env file located in your onchain-calculator/ directory.

  2. Add your funded Sepolia account's private key:

    # Replace with your own private key for your funded Sepolia account
    CRE_ETH_PRIVATE_KEY=YOUR_64_CHARACTER_PRIVATE_KEY_HERE
    

Step 5: Install dependencies

Before you can simulate the workflow, you need to install the workflow's dependencies.

  1. Navigate to your workflow directory:

    cd onchain-calculator/my-calculator-workflow
    
  2. Install the dependencies:

    bun install
    

    This command reads your package.json and installs all required dependencies, including the CRE SDK. The template comes with a postinstall script that automatically runs bunx cre-setup to set up the WebAssembly compilation tools.

    Expected output:

    bun install v1.2.23 (cf136713)
    
    $ bunx cre-setup
    [cre-sdk-javy-plugin] Detected platform: darwin, arch: arm64
    [cre-sdk-javy-plugin] Using cached binary: /Users/<user>/.cache/javy/v5.0.4/darwin-arm64/javy
    āœ… CRE TS SDK is ready to use.
    
    + @types/[email protected]
    + @chainlink/[email protected]
    
    30 packages installed [4.71s]
    
  3. Return to the project root:

    cd ..
    

Step 6: Run your first simulation

Now that your workflow is configured and dependencies are installed, you can run the simulation. Workflow simulation is a local execution environment that compiles your code to WebAssembly and runs it on your machine, allowing you to test and debug before deploying to a live network.

Run the simulate command from your project root directory (the onchain-calculator/ folder):

cre workflow simulate my-calculator-workflow --target staging-settings

This command compiles your TypeScript code to WebAssembly, uses the staging-settings target configuration from workflow.yaml, and spins up a local simulation environment.

Step 7: Review the output

After the workflow compiles, the simulator detects the single trigger you defined in your code and immediately runs the workflow.

Workflow compiled
2025-11-03T19:04:21Z [SIMULATION] Simulator Initialized

2025-11-03T19:04:21Z [SIMULATION] Running trigger trigger=[email protected]
2025-11-03T19:04:21Z [USER LOG] Hello world! Workflow triggered.

Workflow Simulation Result:
 "Hello world!"

2025-11-03T19:04:21Z [SIMULATION] Execution finished signal received
2025-11-03T19:04:21Z [SIMULATION] Skipping WorkflowEngineV2
  • [USER LOG]: This is the output from your own code—in this case, the runtime.log() call. This is where you will look for your custom log messages.
  • [SIMULATION]: These are system-level messages from the simulator, showing its internal state (initialization, trigger execution, completion).
  • Workflow Simulation Result: "Hello world!": This is the final return value of your workflow. In TypeScript, the workflow returns whatever your handler function returns.

Congratulations! You've built and simulated your first CRE workflow in TypeScript.

Key TypeScript concepts

Before moving to the next part, let's review some key TypeScript-specific concepts:

Type safety with TypeScript

The TypeScript SDK provides full type safety:

type Config = {
  schedule: string
}

const runner = await Runner.newRunner<Config>()

By passing the Config type to Runner.newRunner<Config>(), TypeScript ensures your workflow receives the correct configuration shape.

Optional: Runtime validation

For runtime validation of your configuration, you can use schema validation libraries. The CRE SDK supports any library that implements the Standard Schema interface, such as Zod or ArkType.

Here's an example using Zod:

import { z } from "zod"

const configSchema = z.object({
  schedule: z.string(),
})

type Config = z.infer<typeof configSchema>

const runner = await Runner.newRunner<Config>({ configSchema })

This provides both compile-time type checking and runtime validation. We'll use Zod in later parts of this guide.

Next steps

In the next section, you'll build on this foundation by modifying the workflow to fetch real data from an external API.

Get the latest Chainlink content straight to your inbox.