Migrate from Chainlink Automation to Chainlink CRE

This guide is for developers with production Chainlink Automation (CLA) upkeeps migrating their logic to the Chainlink Runtime Environment (CRE).

The majority of Automation use cases involve periodic on-chain maintenance or reacting to on-chain events. That maps cleanly to a CRE workflow: a trigger (Cron or EVM Log) → off-chain logic (replaces checkUpkeep) → a signed report written on-chain through the CRE KeystoneForwarder to an IReceiver consumer, which then calls your execution function.

How CLA and CRE differ

In CLA, you define an upkeep with a checkUpkeep() function (off-chain simulation) and a performUpkeep() function (on-chain execution). The Automation network periodically simulates checkUpkeep() and, if it returns true, submits a transaction to performUpkeep().

In CRE, the unit of execution is a workflow — a TypeScript or Go project compiled to WebAssembly and registered with the network. Workflows are started by triggers (cron schedule, HTTP request, or on-chain log event), can perform multiple steps (HTTP fetches, on-chain reads & on-chain writes) wrapped in your own business logic, and write data on-chain through a signed-report flow that any contract implementing IReceiver can consume.

Functionally, CRE is a superset of CLA: anything CLA does today, CRE does, and many patterns that previously required multiple upkeeps or offchain relayers collapse into a single workflow.

Chain support

CRE supports every chain CLA runs on today. For the canonical, up-to-date list of supported chains, see Supported Networks.

Terminology

Chainlink AutomationChainlink CRE
Upkeep registrationCRE workflow deployment
Upkeep contractExisting target contract plus an IReceiver bridge or consumer
checkUpkeep() functionWorkflow logic inside the handler
performUpkeep(bytes performData)onReport(metadata, report) on an IReceiver, then bridge call to the target contract
Time-based UpkeepBuilt-in Cron trigger
Log Trigger UpkeepEVM Log trigger
Custom Logic UpkeepCron trigger + evmClient.callContract() in handler
Automation RegistryCRE workflow registry/control plane
Automation ForwarderCRE KeystoneForwarder plus your receiver authorization

Triggers

CLA has three primary entry paths: time-based, log-based, and custom logic. CRE makes the trigger first-class.

Time-based

Replaces "Time-based Upkeep." Use a cron trigger with a handler callback.

const onCronTrigger = (runtime: Runtime<Config>): string => {
  runtime.log("Cron triggered")
  return "done"
}

const initWorkflow = (config: Config) => {
  const cron = new cre.capabilities.CronCapability()
  return [
    cre.handler(cron.trigger({ schedule: "0 */5 * * * *" }), onCronTrigger), // every 5 min
  ]
}

Standard 5- or 6-field cron expressions; minimum interval is 30 seconds. Prefix with TZ=<timezone> for timezone-aware scheduling.

Onchain events

Directly replaces "Log Trigger Upkeep." Instead of the registry watching for an event to call performUpkeep, the CRE DON watches for the event and fires the workflow handler with the decoded event data.

const onLogTrigger = (runtime: Runtime<Config>, triggerEvent: EVMLogPayload): string => {
  runtime.log(`Event received from ${triggerEvent.address}`)
  return "processed"
}

const initWorkflow = (config: Config) => {
  const evmLog = new cre.capabilities.EVMLogCapability()
  return [
    cre.handler(
      evmLog.trigger({
        contractAddress: config.contractAddress,
        chainSelectorName: config.chainSelectorName,
        eventSignature: "Transfer(address,address,uint256)",
      }),
      onLogTrigger
    ),
  ]
}

Task Authoring: The "Bridge" Pattern

The fastest way to migrate is using the official Automation Migration starter template. This template uses a "Bridge" pattern: you deploy a generic AutomationReceiver.sol contract, which receives CRE reports and forwards approved calls to your existing Automation contracts.

Your Solidity consumer contract usually does not need to reimplement checkUpkeep, checkLog, or performUpkeep, but some contracts still need a permission update. If your existing contract checks msg.sender, uses an Automation Forwarder allowlist, or has role-based permissions, authorize the new AutomationReceiver or adjust that permission boundary before deployment.

Logic Migration (checkUpkeep → Handler)

In the handler, you use evmClient.callContract() to perform the same checks previously done in checkUpkeep. Since this runs off-chain, you are not limited by on-chain gas constraints for the "check" phase.

On-chain Execution (performUpkeeponReport)

Your workflow generates a signed report and submits it to the AutomationReceiver. The receiver then calls the original performUpkeep function on your target contract.

// AutomationReceiver.sol (included in template)
function _processReport(bytes calldata report) internal override {
    (address target, bytes memory data) = abi.decode(report, (address, bytes));

    if (target == address(0)) {
        revert InvalidTargetAddress();
    }

    (bool success, bytes memory returnData) = target.call(data);
    if (!success) {
        revert CallExecutionFailed(target, returnData);
    }
}

AutomationReceiver inherits ReceiverTemplate, so it validates the configured CRE forwarder before processing reports. For production, also configure workflow identity checks such as expected workflow ID and/or expected workflow owner address, or narrow the receiver to known target contracts and function selectors. A generic receiver that accepts arbitrary (target, data) is convenient for migration, but it should not be left broadly reusable without explicit authorization controls.

Get started

1. Initialize your project

Use the CRE CLI to scaffold a new project using the migration template:

cre init --template=automation-migration-ts --project-name my-automation-migration --workflow-name my-workflow

The template is available in the main branch. You can scaffold a new project using the CRE CLI:

cre init --template=automation-migration-ts

Or browse the template directly at smartcontractkit/cre-templates.

2. Deploy the Bridge

Deploy AutomationReceiver.sol (found in the template) to your target chain, passing the CRE KeystoneForwarder address for that network to the constructor. Find the correct forwarder address for your chain in the Forwarder Directory. This contract acts as a translation layer, allowing you to reuse your existing checkUpkeep, checkLog, and performUpkeep logic while moving trigger and check orchestration into CRE.

3. Configure and Authorize the Receiver

Update my-workflow/config.test.json in your new project with your previously deployed AutomationReceiver contract address, target contract address, migration type (CRON, CUSTOM, or LOG), schedule, and log filters if applicable.

3a. Allow Upkeep Calls

Before your workflow can call your target contract through the receiver, you must authorize the function call using setCallAllowed(). This setter transaction grants the receiver permission to forward calls to your target contract with a specific function selector.

Parameters:

  • target (address): The address of your existing Automation upkeep contract
  • selector (bytes4): The 4-byte function selector you want to allow (e.g., 0x4585e33b for performUpkeep)
  • allowed (bool): Set to true to enable the call

Example transaction:

Function: setCallAllowed (0xb1e17766)
target (address): 0x0971Ad145A5462f6Ae18b1aD2b9c9b0c6d8CC9C8
selector (bytes4): 0x4585e33b
allowed (bool): true

3b. Set Workflow Identity Checks

For production deployments, configure additional authorization layers by setting expected workflow identity parameters. These optional checks ensure only authorized workflows can invoke your receiver.

Available setters:

  • setExpectedAuthor — Restrict calls to workflows authored by a specific address
    • Parameter: _author (address)
  • setExpectedWorkflowId — Restrict calls to a specific workflow ID
    • Parameter: _workflowId (string)
  • setExpectedWorkflowName — Restrict calls to workflows with a specific name
    • Parameter: _workflowName (string)

Example transactions:

Function: setExpectedAuthor
_author (address): 0x5c5c48fc95d68f88b4a13e0c9b0c6d8cc9c8c8c

Function: setExpectedWorkflowId
_workflowId (string): 0x0971ad145a5462f6ae18b1ad2b9c9b0c6d8cc9c8

Function: setExpectedWorkflowName
_workflowName (string): automation-migration-test

Parameters Quick Reference

Getting the Target Address

The target address is the address of your existing Automation upkeep contract — the contract that currently implements performUpkeep(). You can find this by:

  • Reviewing your Automation registry registration
  • Checking your deployment scripts or configuration
  • Looking up the contract address in your blockchain explorer

Computing the Function Selector

The function selector is the first 4 bytes of the keccak256 hash of the function signature. Use the cast command-line tool to compute it:

# For performUpkeep(bytes)
cast sig 'performUpkeep(bytes)'
# Output: 0x4585e33b

# For custom functions
cast sig 'myCustomFunction(uint256,address)'
# Output: 0xabcdef12

Alternatively, compute it programmatically in your test or deployment script:

import { getAddress, toFunctionSelector } from "viem"

const selector = toFunctionSelector("performUpkeep(bytes)")
// Result: 0x4585e33b

Example Transaction Outputs

A successful setCallAllowed transaction on a block explorer shows:

Transaction Type: Contract Interaction
Function: setCallAllowed(address,bytes4,bool)
Status: ✓ Success
From: 0xYourAddress
To: 0xAutomationReceiverAddress
Gas Used: 35,400 gas

Decoded Input:
target: 0x0971Ad145A5462f6Ae18b1aD2b9c9b0c6d8CC9C8
selector: 0x4585e33b
allowed: true

3c. Configure and Simulate

Then install dependencies and simulate from the project root. The CRE CLI prepares the workflow build tooling during simulation.

cd my-workflow
bun install
cd ..

cre workflow simulate my-workflow --target=test-settings

For log-trigger migrations, provide a transaction hash containing the event so simulation does not wait for a live event:

cre workflow simulate my-workflow \
  --target=test-settings \
  --non-interactive \
  --trigger-index=0 \
  --evm-tx-hash=0x... \
  --evm-event-index=0

4. Deploy

Once verified, deploy your workflow to the CRE DON:

cre workflow deploy my-workflow --target=production-settings

For end-to-end runnable examples, see the smartcontractkit/cre-templates repository.

Troubleshooting

CallNotAllowed Error

If your workflow execution fails with a CallNotAllowed error, verify the following:

  1. Function selector mismatch — Ensure the selector you configured in setCallAllowed() matches the function your workflow is attempting to call. Use cast sig 'functionName(types)' to compute the correct selector. For more information, see the Function Selector Reference.

  2. Permission not set — Confirm that setCallAllowed() was called with allowed: true for your target contract and function selector.

  3. Workflow authorization — If you set workflow identity checks (setExpectedAuthor, setExpectedWorkflowId, or setExpectedWorkflowName), ensure the workflow deploying the call matches those values.

  4. Forwarder mismatch — Verify that the KeystoneForwarder address passed to the AutomationReceiver constructor matches the address for your chain in the Forwarder Directory.

Get the latest Chainlink content straight to your inbox.