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 initcommand to scaffold a complete project from the Custom Data Feed template. - Configure your private key: Add your funded Sepolia private key to the
.envfile. - Install dependencies: Use
bun installto set up your workflow's TypeScript dependencies. - Run the simulation: Use
cre workflow simulateto 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 whoamiin your terminal to verify you're logged in, or runcre loginto 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.
-
In your terminal, navigate to where you want your project created.
-
Run the init command:
cre init -
Provide the following details when prompted:
- Project name:
demo(this becomes your project directory name) - Language: Select
Typescriptand 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
- Project name:
Result: The CLI creates a new demo directory with all the necessary files and folders, including:
- A
custom-data-feedsubdirectory containing your workflow code - A
contracts/abi/directory with TypeScript ABI definitions for the demo contracts - A pre-configured
project.yamlwith 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.
-
Navigate into your project directory:
cd demo -
Run the
simulatecommand:cre workflow simulate custom-data-feed --broadcast --target staging-settingsYou will first see a
Workflow compiledmessage, 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
-
At the prompt, select the
cron-triggerby pressing1and thenEnter.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
-
Check the Transaction: Copy the
txHashfrom 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. -
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 Contracttab. - Check the values for
lastTotalMintedandlastTotalReserve. 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
txHashfrom your logs). - Click on the
Logstab. - You'll see events emitted during the transaction, including the event from the ReserveManager contract confirming the data update.
- Navigate to the ReserveManager contract address used in the demo:
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
-
From the trigger menu, select the EVM Log Trigger by pressing
2and thenEnter. -
When prompted, provide the following details for a real transaction on the Sepolia testnet that emitted a
MessageEmittedevent:- Transaction hash:
0x420721d7d00130a03c5b525b2dbfd42550906ddb3075e8377f9bb5d1a5992f8e - Event index:
0
- Transaction hash:
-
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
MessageEmittercontract.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:
-
You provide the event's "coordinates": By giving the simulator a transaction hash and a log index, you point it to a specific
MessageEmittedevent on the blockchain. -
The workflow receives the event data: The simulator passes the raw log data into the
onLogTriggercallback function inmain.ts. This log contains a list oftopics, which are indexed fields from the Solidity event. -
The workflow extracts the emitter's address: The code for
onLogTriggerknows that for aMessageEmittedevent,topics[1]holds the address of the entity that emitted the message. It extracts this address. -
The workflow makes a new onchain call: This is the key step. The workflow now takes the
emitteraddress it just extracted from the event and uses it as an argument to call thegetLastMessagefunction on theMessageEmittercontract. It is effectively asking, "What was the last message from the specific emitter involved in the event that triggered me?" -
The workflow logs the result: Finally, it logs the message content it received from its
getLastMessagecall 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()andRunner: The entry point of the workflow. It creates a newRunnerinstance with your config schema usingRunner.newRunner(), then callsrunner.run()with theinitWorkflowfunction. 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 usinghandler(), 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 shareddoPORfunction, 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 calldoPOR. -
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, andgetLastMessage: 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
encodeFunctionDataanddecodeFunctionResultfor 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
ConsensusAggregationByFieldsto aggregate offchain data from multiple nodes. - Two-Step Write Pattern: The workflow uses
runtime.report()to generate a signed report, thenevmClient.writeReport()to submit it onchain. - Helper Functions: The SDK provides utilities like
getNetwork(),encodeCallMsg(),bytesToHex(), andhexToBase64()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
HTTPClientto fetch offchain reserve data. Learn more in the API Interactions guide. - How does it read from or write to the blockchain? It uses the
EVMClientfor 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.