Running a Demo Workflow

This guide walks you through the core developer loop of CRE: initializing a project from a template and running it locally using the simulator. 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 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 and Logging 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.
  • 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 faucets.chain.link 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:

    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

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:

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:

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

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:

    cd demo
    
  2. Run the simulate command:

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

    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.

    Workflow compiled
    
    🚀 Workflow simulation ready. Please select a trigger:
    1. [email protected] Trigger
    2. evm:ChainSelector:[email protected] LogTrigger
    
    Enter your choice (1-2): 1
    
    2025-10-31T16:57:45Z [SIMULATION] Simulator Initialized
    
    2025-10-31T16:57:45Z [SIMULATION] Running trigger trigger=[email protected]
    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 Sepolia Etherscan. You will see the full details of the transaction your workflow submitted.

  2. 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: 0x073671aE6EAa2468c203fDE3a79dEe0836adF032.
    • 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 transaction on the Sepolia testnet 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.

    Workflow compiled
    
    🚀 Workflow simulation ready. Please select a trigger:
    1. [email protected] Trigger
    2. evm:ChainSelector:[email protected] 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:[email protected]
    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 cre.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 cre.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 guides.
  • How does it fetch API data? The demo uses the HTTPClient to fetch offchain reserve data. Learn more in the API Interactions guide.
  • How does it read from or write to the blockchain? It uses the EVMClient for all onchain interactions. See the EVM Chain Interactions 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 guide.

Get the latest Chainlink content straight to your inbox.