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.
  • Generate bindings: Use cre generate-bindings to create type-safe Go interfaces for the demo's smart contracts.
  • Sync dependencies: Use go mod tidy to download and sync your workflow's Go 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.
  • Go: You must have Go version 1.25.3 or higher installed. Check your version with go version. See Install Go 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 Golang and press Enter.
    • Pick a workflow template: Select Custom data feed: 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/evm/src/ directory with contract ABIs and a keystone/ subdirectory
  • 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. Generate contract bindings

To interact with the demo's smart contracts, you need to generate type-safe Go bindings from their ABIs.

  1. Navigate into your project directory:

    cd demo
    
  2. Run the generate-bindings command:

    cre generate-bindings evm
    

    This command finds the contract ABIs inside the contracts/evm/src/abi/ directory and generates Go packages in contracts/evm/src/generated/.

5. Sync your dependencies

Your workflow code imports SDK packages and the newly generated contract bindings. Run go mod tidy to automatically download and sync all the required dependencies for the entire project.

go mod tidy

6. Run the simulation

Now you are ready to compile and run the workflow. The workflow code (workflow.go and main.go) 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. Ensure you are in the project root directory (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
    
  2. The simulator will execute the full end-to-end workflow. The final logs will show the transaction hash from the successful onchain write.

    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-31T17:13:52Z [SIMULATION] Simulator Initialized
    
    2025-10-31T17:13:52Z [SIMULATION] Running trigger trigger=[email protected]
    2025-10-31T17:13:52Z [USER LOG] msg="fetching por" url=https://api.real-time-reserves.verinumus.io/v1/chainlink/proof-of-reserves/TrueUSD evms="[{TokenAddress:0x4700A50d858Cb281847ca4Ee0938F80DEfB3F1dd ReserveManagerAddress:0x51933aD3A79c770cb6800585325649494120401a BalanceReaderAddress:0x4b0739c94C1389B55481cb7506c62430cA7211Cf MessageEmitterAddress:0x1d598672486ecB50685Da5497390571Ac4E93FDc ChainName:ethereum-testnet-sepolia GasLimit:1000000}]"
    2025-10-31T17:13:53Z [USER LOG] msg=ReserveInfo reserveInfo="&{LastUpdated:2025-10-31 22:13:36.163 +0000 UTC TotalReserve:494515082.75}"
    2025-10-31T17:13:53Z [USER LOG] msg=TotalSupply totalSupply=1000000000000000000000000
    2025-10-31T17:13:53Z [USER LOG] msg=TotalReserveScaled totalReserveScaled=494515082750000000000000000
    2025-10-31T17:13:53Z [USER LOG] msg="Getting native balances" address=0x4b0739c94C1389B55481cb7506c62430cA7211Cf tokenAddress=0x4700A50d858Cb281847ca4Ee0938F80DEfB3F1dd
    2025-10-31T17:13:53Z [USER LOG] msg="Native token balance" token=0x4700A50d858Cb281847ca4Ee0938F80DEfB3F1dd balance=0
    2025-10-31T17:13:53Z [USER LOG] msg="Updating reserves" totalSupply=1000000000000000000000000 totalReserveScaled=494515082750000000000000000
    2025-10-31T17:13:53Z [USER LOG] msg="Writing report" totalSupply=1000000000000000000000000 totalReserveScaled=494515082750000000000000000
    2025-10-31T17:14:00Z [USER LOG] msg="Write report succeeded" response="tx_status:TX_STATUS_SUCCESS receiver_contract_execution_status:RECEIVER_CONTRACT_EXECUTION_STATUS_SUCCESS tx_hash:\"E7\\xe6\\x8d5h\\x87b\\xfeZ\\x9b\\x81\\x86W\\xbcߩxaѬ\\xe3\\xb9\\xf0\\x0ewL\\xb1\\x1e\\x82g\\xa0\" transaction_fee:{abs_val:\"\\x11M)[@\" sign:1}"
    2025-10-31T17:14:00Z [USER LOG] msg="Write report transaction succeeded at" txHash=0x4537e68d35688762fe5a9b818657bcdfa97861d1ace3b9f00e774cb11e8267a0
    
    Workflow Simulation Result:
    "494515082.75"
    
    2025-10-31T17:14:00Z [SIMULATION] Execution finished signal received
    2025-10-31T17:14:00Z [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:49:53Z [SIMULATION] Simulator Initialized
    
    2025-10-31T17:49:53Z [SIMULATION] Running trigger trigger=evm:ChainSelector:[email protected]
    2025-10-31T17:49:53Z [USER LOG] msg="Message retrieved from the event log" message="this is a test message"
    2025-10-31T17:49:53Z [USER LOG] msg="Message retrieved from the contract" message="this is a test message"
    
    Workflow Simulation Result:
    "this is a test message"
    
    2025-10-31T17:49:53Z [SIMULATION] Execution finished signal received
    2025-10-31T17:49:53Z [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 decoded event data: The simulator passes a decoded log payload into the onLogTrigger callback function in workflow.go. The Go SDK automatically decodes the event log into a strongly-typed structure, making it easy to access event fields.

  3. The workflow extracts the emitter's address: The code for onLogTrigger accesses payload.Data.Emitter to get the address of the entity that emitted the message. This is a decoded field from the event, not a raw topic.

  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 decoded 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.

7. Exploring the code

The workflow code (workflow.go) is a great example of how a single workflow can contain multiple, independent handlers to perform different tasks. The main.go file serves as the entry point that initializes the workflow runner. Here is a high-level tour of the code to show how the two paths you just tested are implemented.

  • InitWorkflow: This is the entry point. It initializes the cron trigger and returns a workflow with two handlers: one for the cron trigger and one for the EVM log trigger. The EVM log trigger is configured to listen for MessageEmitted events from the MessageEmitter contract.

  • onPORCronTrigger: 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 (the emitter address and message), 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.

  • fetchPOR, getTotalSupply, fetchNativeTokenBalance, updateReserves, and prepareMessageEmitter: 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 clients (http.Client, evm.Client) and the generated contract bindings.

8. Key Go SDK features in this demo

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

  • Generated Contract Bindings: The workflow uses type-safe Go bindings generated from contract ABIs, providing compile-time safety for contract interactions.
  • Multiple Trigger Handlers: A single workflow can register multiple handlers for different trigger types using cre.Handler().
  • Consensus Aggregation from Tags: The HTTP capability uses cre.ConsensusAggregationFromTags[*ReserveInfo]() to automatically aggregate offchain data based on struct field tags.
  • Promise-Based Async Operations: All SDK operations return promises that are awaited with .Await(), enabling both sequential and parallel execution patterns.
  • Strongly-Typed Event Decoding: EVM log triggers automatically decode event data into Go structs, eliminating manual topic parsing.
  • Two-Step Write Pattern: The workflow uses contract binding's WriteReportFrom* methods to generate a signed report and submit it onchain in one call.
  • Chain Selector Management: The SDK provides evm.ChainSelectorFromName() to convert human-readable chain names to chain selectors.
  • Finalized Block Numbers: Contract reads use rpc.FinalizedBlockNumber to ensure reading from finalized blockchain state.

Next steps

You have successfully run the Custom Data Feed demo workflow. To understand how the different pieces of the workflow code 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 http.Client 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 evm.Client for all onchain interactions. See the EVM Chain Interactions guides for details.
  • How does it use a contract binding? The demo uses pre-built bindings to interact with the contracts safely. Learn how to create your own in the Generating Bindings guide.

Get the latest Chainlink content straight to your inbox.