# Running a Demo Workflow
Source: https://docs.chain.link/cre/templates/running-demo-workflow-ts
Last Updated: 2026-01-20

> For the complete documentation index, see [llms.txt](/llms.txt).

This guide walks you through the core developer loop of CRE: initializing a project from a template and running it locally using the [simulator](/cre/guides/operations/simulating-workflows). By the end, you will have run the Custom Data Feed demo workflow and tested its two distinct behaviors: a **proactive** path where it fetches data from an API to write a result onchain, and a **reactive** path where it listens for onchain events to trigger new actions.

## What you'll do

- **Initialize a project**: Use the `cre init` command to scaffold a complete project from the Custom Data Feed template.
- **Configure your private key**: Add your funded Sepolia private key to the `.env` file.
- **Install dependencies**: Use `bun install` to set up your workflow's TypeScript dependencies.
- **Run the simulation**: Use `cre workflow simulate` to execute the end-to-end workflow and observe its output.

## 1. Prerequisites

Before you begin, ensure you have the necessary tools installed:

- **CRE CLI**: You must have the CRE CLI installed. See [Install the CLI](/cre/getting-started/cli-installation) for instructions.
- **CRE account & authentication**: You must have a CRE account and be logged in with the CLI. Run cre whoami in your terminal to verify you're logged in, or run cre login to authenticate. See [Creating Your Account](/cre/account/creating-account) and [Logging in with the CLI](/cre/account/cli-login) for instructions.
- **Bun**: You must have Bun version 1.2.21 or higher installed. Check your version with bun --version. See [Install Bun](https://bun.com/docs/installation) for instructions.
- **Sepolia Testnet Account**: You need a private key for an account funded with Sepolia ETH. This is required because the demo workflow performs a write transaction. Go to <a href="https://faucets.chain.link" target="blank">faucets.chain.link</a> to get some Sepolia ETH.

## 2. Initialize the demo project

The `cre init` command scaffolds a new project from a template. The CLI will prompt you for configuration details during initialization.

1. **In your terminal, navigate to where you want your project created.**

2. **Run the init command:**

   ```bash
   cre init
   ```

3. **Provide the following details when prompted:**
   - **Project name**: demo (this becomes your project directory name)
   - **Language**: Select `Typescript` and press Enter.
   - **Pick a workflow template**: Select `Custom data feed: Typescript updating on-chain data periodically using offchain API data`
   - **Sepolia RPC URL**: Press Enter to use the default public RPC (`https://ethereum-sepolia-rpc.publicnode.com`), or provide your own Sepolia RPC URL.
   - **Workflow name**: custom-data-feed

**Result:** The CLI creates a new `demo` directory with all the necessary files and folders, including:

- A `custom-data-feed` subdirectory containing your workflow code
- A `contracts/abi/` directory with TypeScript ABI definitions for the demo contracts
- A pre-configured `project.yaml` with your RPC URL already set

> **NOTE: RPC URL is pre-configured**
>
> The CLI now asks for your RPC URL during initialization and automatically adds it to `project.yaml`. You can change it
> later if needed.

## 3. Configure your private key

The demo workflow needs your funded Sepolia private key to sign and broadcast transactions.

**Open the `.env` file** in your project root (`demo`) and replace the placeholder private key with your actual funded Sepolia private key:

> **CAUTION: Use the Raw Key**
>
> Your private key must be the 64-character hexadecimal string. Do **not** include the `0x` prefix.

> **CAUTION: Never Commit .env Files**
>
> The `.gitignore` file included in the project already prevents `.env` files from being committed to version control.
> **Never** share your private keys or commit them to Git.

> **NOTE: Best Practices for Security**
>
> Using a plaintext `.env` file is convenient for initial testing, but for better security, we recommend using a
> dedicated secrets manager. See our guide on [Managing Secrets with 1Password
> CLI](/cre/guides/workflow/secrets/managing-secrets-1password) to learn how to inject secrets securely at runtime.

## 4. Install dependencies

Your workflow needs to install its TypeScript dependencies. Run the `bun install` command with the `--cwd` flag to install dependencies in the workflow directory:

```bash
bun install --cwd ./demo/custom-data-feed
```

> **NOTE: Why install in the workflow directory?**
>
> Each CRE TypeScript workflow is a standalone npm package with its own `package.json` and dependencies. This allows you
> to manage dependencies independently for each workflow in your project. The `--cwd` flag tells Bun to install
> dependencies in the specified workflow directory.

## 5. Run the simulation

Now you are ready to compile and run the workflow. The single `main.ts` file you are about to simulate is cleverly designed to demonstrate two distinct, powerful workflow patterns. We will run each one separately.

- **Path A: End-to-End Custom Data Feed**: Triggered by a CRON schedule, this workflow performs the full offchain to onchain data feed check and writes the result to the blockchain.
- **Path B: Reactive Event Handling**: Triggered by an onchain event log, this workflow demonstrates how to use data from one event to react and query another contract.

1. **Navigate into your project directory:**

   ```bash
   cd demo
   ```

2. **Run the `simulate` command**:

   ```bash
   cre workflow simulate custom-data-feed --broadcast --target staging-settings
   ```

> **NOTE: How configuration is discovered**
>
> The CLI automatically discovers your workflow configuration from the `workflow.yaml` file in the `custom-data-feed`
> directory. This file specifies paths to your workflow code (`main.ts`) and config files (`config.staging.json`,
> `config.production.json`). The template already configures these paths correctly.

> **NOTE: Onchain Writes are Dry Runs by Default**
>
> The `--broadcast` flag is included here because this workflow performs an onchain write. By default, the `simulate`
> command performs a dry run and will not broadcast the transaction without this flag. For more details, see the `cre
>      workflow simulate` [reference](/cre/reference/cli/workflow#cre-workflow-simulate).

You will first see a `Workflow compiled` message, followed by the trigger selection menu.

***

### Path A: The end-to-end Custom Data Feed workflow

This path executes the core functionality of the demo: fetching offchain reserve data and writing the result onchain. It is initiated by the CRON trigger.

#### **Running with the CRON Trigger**

1. At the prompt, select the `cron-trigger` by pressing `1` and then `Enter`.

   ```bash
   Workflow compiled

   🚀 Workflow simulation ready. Please select a trigger:
   1. cron-trigger@1.0.0 Trigger
   2. evm:ChainSelector:16015286601757825753@1.0.0 LogTrigger

   Enter your choice (1-2): 1

   2025-10-31T16:57:45Z [SIMULATION] Simulator Initialized

   2025-10-31T16:57:45Z [SIMULATION] Running trigger trigger=cron-trigger@1.0.0
   2025-10-31T16:57:45Z [USER LOG] Running CronTrigger
   2025-10-31T16:57:45Z [USER LOG] fetching por url https://api.real-time-reserves.verinumus.io/v1/chainlink/proof-of-reserves/TrueUSD
   2025-10-31T16:57:46Z [USER LOG] ReserveInfo {
     "lastUpdated": "2025-10-31T21:57:35.528Z",
     "totalReserve": 494515082.75
   }
   2025-10-31T16:57:46Z [USER LOG] TotalSupply 1000000000000000000000000
   2025-10-31T16:57:46Z [USER LOG] TotalReserveScaled 494515082750000009035382784
   2025-10-31T16:57:46Z [USER LOG] NativeTokenBalance 0
   2025-10-31T16:57:46Z [USER LOG] Updating reserves totalSupply 1000000000000000000000000 totalReserveScaled 494515082750000009035382784
   2025-10-31T16:58:01Z [USER LOG] Write report transaction succeeded at txHash: 0x9fbbeee645704d020bef000d35de52f10ccb91d8a3793f9523a4dc5155cef109

   Workflow Simulation Result:
   "494515082.75"

   2025-10-31T16:58:01Z [SIMULATION] Execution finished signal received
   2025-10-31T16:58:01Z [SIMULATION] Skipping WorkflowEngineV2
   ```

#### **Verifying the result onchain**

1. **Check the Transaction**: Copy the `txHash` from the logs in your terminal and paste it into the search bar on <a href="https://sepolia.etherscan.io/" target="_blank" rel="noopener noreferrer">Sepolia Etherscan</a>. You will see the full details of the transaction your workflow submitted.

> **NOTE: What are you seeing on a blockchain explorer?**
>
> You'll notice the transaction's `to` address is not the ReserveManager contract you intended to call. Instead,
> it's to the Chainlink **Forwarder** contract. Your workflow sends the transaction to the Forwarder, which verifies
> the cryptographic signatures and then delivers your data to the ReserveManager contract by calling its
> `onReport()` function.

This is a core security pattern in CRE that ensures only verified, consensus-approved data reaches your smart
contracts. To learn more, see the [Onchain Write
guide](/cre/guides/workflow/using-evm-client/onchain-write/overview).

1. **Check the Contract State**: While your transaction went to the Forwarder, the underlying ReserveManager contract's state was still updated. You can verify this change directly on Etherscan in two ways:

   **Option A: Read the contract state**

   - Navigate to the ReserveManager contract address used in the demo: <a href="https://sepolia.etherscan.io/address/0x073671aE6EAa2468c203fDE3a79dEe0836adF032" target="_blank" rel="noopener noreferrer">`0x073671aE6EAa2468c203fDE3a79dEe0836adF032`</a>.
   - Go to the `Read Contract` tab.
   - Check the values for `lastTotalMinted` and `lastTotalReserve`. They should now reflect the data your workflow just wrote to the chain.

   **Option B: Check the transaction events**

   - Go to your transaction on Etherscan (using the `txHash` from your logs).
   - Click on the `Logs` tab.
   - You'll see events emitted during the transaction, including the event from the ReserveManager contract confirming the data update.

This completes the end-to-end loop: triggering a workflow, fetching data, and verifiably writing the result to a public blockchain.

***

### Path B: The reactive event handler

This path demonstrates a more advanced, reactive pattern. It uses an onchain event (a log) as a trigger, inspects the data within that event, and uses that data to make an onchain read call. This path does not write any data onchain.

#### **Running with the log trigger**

1. From the trigger menu, select the EVM Log Trigger by pressing `2` and then `Enter`.

2. When prompted, provide the following details for a real <a href="https://sepolia.etherscan.io/tx/0x420721d7d00130a03c5b525b2dbfd42550906ddb3075e8377f9bb5d1a5992f8e#eventlog" target="_blank" rel="noopener noreferrer">transaction on the Sepolia testnet</a> that emitted a `MessageEmitted` event:
   - **Transaction hash**: 0x420721d7d00130a03c5b525b2dbfd42550906ddb3075e8377f9bb5d1a5992f8e
   - **Event index**: 0

3. The simulator will find the onchain event and use its payload to run the workflow. The final log will show the message it retrieved from the `MessageEmitter` contract.

   ```bash
   Workflow compiled

   🚀 Workflow simulation ready. Please select a trigger:
   1. cron-trigger@1.0.0 Trigger
   2. evm:ChainSelector:16015286601757825753@1.0.0 LogTrigger

   Enter your choice (1-2): 2


   🔗 EVM Trigger Configuration:
   Please provide the transaction hash and event index for the EVM log event.
   Enter transaction hash (0x...): 0x420721d7d00130a03c5b525b2dbfd42550906ddb3075e8377f9bb5d1a5992f8e
   Enter event index (0-based): 0
   Fetching transaction receipt for transaction 0x420721d7d00130a03c5b525b2dbfd42550906ddb3075e8377f9bb5d1a5992f8e...
   Found log event at index 0: contract=0x1d598672486ecB50685Da5497390571Ac4E93FDc, topics=3
   Created EVM trigger log for transaction 0x420721d7d00130a03c5b525b2dbfd42550906ddb3075e8377f9bb5d1a5992f8e, event 0
   2025-10-31T17:05:17Z [SIMULATION] Simulator Initialized

   2025-10-31T17:05:17Z [SIMULATION] Running trigger trigger=evm:ChainSelector:16015286601757825753@1.0.0
   2025-10-31T17:05:17Z [USER LOG] Running LogTrigger
   2025-10-31T17:05:17Z [USER LOG] Emitter 0x6b6d462be56d0630579fdd1a1247140b5d51a8c6
   2025-10-31T17:05:18Z [USER LOG] Message retrieved from the contract this is a test message

   Workflow Simulation Result:
   "this is a test message"

   2025-10-31T17:05:18Z [SIMULATION] Execution finished signal received
   2025-10-31T17:05:18Z [SIMULATION] Skipping WorkflowEngineV2
   ```

#### **How it works: An event-driven pattern**

What you just witnessed is a powerful event-driven capability. The workflow didn't just react to an event; it used the information *inside* that event to drive its next action. Here's how it works:

1. **You provide the event's "coordinates"**: By giving the simulator a transaction hash and a log index, you point it to a specific `MessageEmitted` event on the blockchain.

2. **The workflow receives the event data**: The simulator passes the raw log data into the `onLogTrigger` callback function in `main.ts`. This log contains a list of `topics`, which are indexed fields from the Solidity event.

3. **The workflow extracts the emitter's address**: The code for `onLogTrigger` knows that for a `MessageEmitted` event, `topics[1]` holds the address of the entity that emitted the message. It extracts this address.

4. **The workflow makes a new onchain call**: This is the key step. The workflow now takes the `emitter` address it just extracted from the event and uses it as an argument to call the `getLastMessage` function on the `MessageEmitter` contract. It is effectively asking, "What was the last message from the specific emitter involved in the event that triggered me?"

5. **The workflow logs the result**: Finally, it logs the message content it received from its `getLastMessage` call and finishes.

This pattern showcases how you can build sophisticated, interconnected institutional-grade smart contracts that react to onchain activity in real-time.

## 6. Exploring the code

The `main.ts` file is a great example of how a single workflow can contain multiple, independent handlers to perform different tasks. Here is a high-level tour of the code to show how the two paths you just tested are implemented.

- **`main()` and `Runner`**: The entry point of the workflow. It creates a new `Runner` instance with your config schema using `Runner.newRunner()`, then calls `runner.run()` with the `initWorkflow` function. This is the standard pattern for initializing CRE workflows in the TypeScript SDK.

- **`initWorkflow`**: This function initializes the trigger capabilities and returns an array of two handlers: one for the cron trigger and one for the EVM log trigger. Each handler is created using `handler()`, which pairs a trigger configuration with a callback function.

- **`onCronTrigger`**: The entry point for **Path A**. It's a lightweight callback that immediately delegates to the shared `doPOR` function, demonstrating how you can reuse core logic.

- **`onLogTrigger`**: This is the self-contained entry point for **Path B**. It contains its own unique logic to handle the reactive pattern: it's triggered by an event, extracts data from that event's topics, and uses that data to make a new onchain query. It does **not** call `doPOR`.

- **`doPOR`**: This is the engine for **Path A**. It contains the core business logic for the Custom Data Feed workflow, orchestrating the sequence of helper functions to fetch API data, read contract state, and finally write the result back onchain.

- **`fetchReserveInfo`, `getTotalSupply`, `fetchNativeTokenBalance`, `updateReserves`, and `getLastMessage`**: These are the helper functions that execute the specific steps for the Custom Data Feed and reactive event handling workflows. They contain the calls to the SDK capabilities (`HTTPClient`, `EVMClient`) and use viem for ABI encoding/decoding.

## 7. Key TypeScript SDK features in this demo

This demo showcases several important patterns and features of the TypeScript SDK:

- **Runner Pattern**: The workflow uses the `Runner.newRunner()` pattern to initialize the workflow with a config schema and run it.
- **Zod Schema Validation**: The workflow uses Zod to define and validate the configuration schema, ensuring type safety at runtime.
- **Multiple Trigger Handlers**: A single workflow can register multiple handlers for different trigger types using `handler()`.
- **Viem Integration**: All EVM interactions use viem's `encodeFunctionData` and `decodeFunctionResult` for type-safe contract calls.
- **Manual ABI Management**: TypeScript workflows use manually defined ABI constants from the `contracts/abi/` directory.
- **Consensus Aggregation**: The HTTP capability uses `ConsensusAggregationByFields` to aggregate offchain data from multiple nodes.
- **Two-Step Write Pattern**: The workflow uses `runtime.report()` to generate a signed report, then `evmClient.writeReport()` to submit it onchain.
- **Helper Functions**: The SDK provides utilities like `getNetwork()`, `encodeCallMsg()`, `bytesToHex()`, and `hexToBase64()` for common tasks.

## Next steps

You have successfully run the Custom Data Feed demo workflow. To understand how the different pieces of the `main.ts` file work, explore these detailed guides:

- **How are the Cron and EVM Log events handled?** Learn how to use different event sources to start your workflow in the **[Using Triggers](/cre/guides/workflow/using-triggers/overview)** guides.
- **How does it fetch API data?** The demo uses the `HTTPClient` to fetch offchain reserve data. Learn more in the **[API Interactions](/cre/guides/workflow/using-http-client)** guide.
- **How does it read from or write to the blockchain?** It uses the `EVMClient` for all onchain interactions. See the [**EVM Chain Interactions**](/cre/guides/workflow/using-evm-client/overview) guides for details.
- **How does it work with contract ABIs?** The demo uses pre-defined TypeScript ABI files with viem. Learn more in the **[Onchain Read](/cre/guides/workflow/using-evm-client/onchain-read)** guide.