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 Automation | Chainlink CRE |
|---|---|
| Upkeep registration | CRE workflow deployment |
| Upkeep contract | Existing target contract plus an IReceiver bridge or consumer |
checkUpkeep() function | Workflow logic inside the handler |
performUpkeep(bytes performData) | onReport(metadata, report) on an IReceiver, then bridge call to the target contract |
| Time-based Upkeep | Built-in Cron trigger |
| Log Trigger Upkeep | EVM Log trigger |
| Custom Logic Upkeep | Cron trigger + evmClient.callContract() in handler |
| Automation Registry | CRE workflow registry/control plane |
| Automation Forwarder | CRE 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 (performUpkeep → onReport)
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.,
0x4585e33bforperformUpkeep) - allowed (bool): Set to
trueto 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)
- Parameter:
- setExpectedWorkflowId — Restrict calls to a specific workflow ID
- Parameter:
_workflowId(string)
- Parameter:
- setExpectedWorkflowName — Restrict calls to workflows with a specific name
- Parameter:
_workflowName(string)
- Parameter:
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:
-
Function selector mismatch — Ensure the selector you configured in
setCallAllowed()matches the function your workflow is attempting to call. Usecast sig 'functionName(types)'to compute the correct selector. For more information, see the Function Selector Reference. -
Permission not set — Confirm that
setCallAllowed()was called withallowed: truefor your target contract and function selector. -
Workflow authorization — If you set workflow identity checks (
setExpectedAuthor,setExpectedWorkflowId, orsetExpectedWorkflowName), ensure the workflow deploying the call matches those values. -
Forwarder mismatch — Verify that the
KeystoneForwarderaddress passed to theAutomationReceiverconstructor matches the address for your chain in the Forwarder Directory.