| section | cre | ||||||
|---|---|---|---|---|---|---|---|
| date | Last Modified | ||||||
| title | Migrate from Chainlink Automation to Chainlink CRE | ||||||
| pageId | cla-migration | ||||||
| sdkLang | ts | ||||||
| metadata |
|
import { Aside } from "@components"
Chainlink Automation v1.x sunsets on June 30, 2026. Chainlink Automation v2.1 sunsets on July 31, 2026 (testnet: June 24, 2026).Migrate to the Chainlink Runtime Environment (CRE), which provides all Automation functionality and more. Migrate your existing upkeeps before these dates to avoid service disruption.
Reduced Service Windows
To help users identify Automation jobs that are still active and prepare for the Automation sunset, we will temporarily reduce the Automation service during the following windows:
- July 15, 9:00 AM–1:00 PM EST (4 hours)
- July 22, 9:00 AM–5:00 PM EST (8 hours)
- July 27, 9:00 AM–July 28, 9:00 AM EST (24 hours)
During these windows, existing upkeeps may not execute as expected. These temporary service reductions are intended to help identify workloads that have not yet been migrated to CRE before the final sunset dates.
To ensure uninterrupted operation, migrate your upkeeps to CRE before the applicable sunset date.
You're viewing the **TypeScript** version of this guide. If you prefer Go, use the language selector in the left sidebar to switch to the Go version.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.
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.
CRE supports every chain CLA runs on today. For the canonical, up-to-date list of supported chains, see Supported Networks.
| 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 |
CLA has three primary entry paths: time-based, log-based, and custom logic. CRE makes the trigger first-class.
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.
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
),
]
}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.
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.
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.
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-workflowThe template is available in the main branch. You can scaffold a new project using the CRE CLI:
cre init --template=automation-migration-tsOr browse the template directly at smartcontractkit/cre-templates.
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.
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.
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
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
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
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: 0xabcdef12Alternatively, compute it programmatically in your test or deployment script:
import { getAddress, toFunctionSelector } from "viem"
const selector = toFunctionSelector("performUpkeep(bytes)")
// Result: 0x4585e33bA 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
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-settingsFor 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=0Once verified, deploy your workflow to the CRE DON:
cre workflow deploy my-workflow --target=production-settingsFor end-to-end runnable examples, see the smartcontractkit/cre-templates repository.
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.